理解:url与ui的映射关系,再改变url的情况下,展示对应内容,但是不会对整个页面进行刷新
(1)监视地址栏变化;(2)查找当前路径对应的页面组件;(3)将找到的页面组件替换到 router-vieW
的位置
实现前端路由的两个核心:
1》修改url,但是页面不刷新
2》能够检测到url的变化
1、hash特点
hash 是 URL 中 hash (#) 及后面的那部分,常用作锚点在页面内进行导航,改变 URL 中的 hash 部分不会引起页面刷新
hash 的变化不会导致浏览器向服务端发送请求,所以也就不会刷新页面;hash的实现全部在前端,不需要后端服务器配合,兼容性好,主要是通过监听hashchange事件,处理前端业务逻辑
2、hashchange事件
通过hashchange事件可以监听到hash值的变化
修改hash的几种方式:
1、pushState与replaceState
HTML5新出来的 history.pushState() 和 history.replaceState() 能够改变 URL 的 path 部分但是不会引起页面刷新
仅改变网址url,网页不会真的跳转,也不会获取到新的内容,本质上网页还停留在原页面
基本语法:
window.history.pushState(state, title, targetURL);
@状态对象:传给目标路由的信息,可为空
@页面标题:目前所有浏览器都不支持,填空字符串即可
@可选url:目标url,不会检查url是否存在,且不能跨域。如不传该项,即给当前url添加data
window.history.replaceState(state, title, targetURL);
@类似于pushState,但是会直接替换掉当前url,而不会在history中留下记录
2、popstate事件
popstate事件会在点击浏览器的前进、后退按钮(调用back、forward、go方法)时触发;
通过pushState、replaceState或标签改变 URL 不会触发 popstate 事件
history
跳转
但是我们可以拦截pushState、replaceState的调用和a标签的点击事件来检测url的变化
如果要触发popstate可以通过js 调用history的back,go,forward方法来触发该事件
hash
修改url
基于哈希来实现前端路由
history
history
我们通过a标签的href属性来改变URL的path值(当改变path值时,如果不是只修改hash,默认会触发页面的跳转,虽然不触发popstate事件,但是真个页面回刷新,所以需要拦截
标签点击事件默认行为)点击时使用 pushState 修改 URL并更新手动 UI,从而实现点击链接更新 URL 和 UI 的效果。
我们监听popState事件。一旦事件触发,就改变routerView的内容。
三种模式:
hash:哈希模式
history:历史模式
abstract:抽象模式
hash模式和history模式都是利用的浏览器api,abstract是在不支持浏览器API的环境下使用
基本使用
import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
mode: 'hash',
routes:[],
})
export default router
从它的使用上可以看出,VueRouter是一个类或构造函数,它通过use来注册,那肯定有一个install方法
下面是install的源码:“vue-router”: “^3.6.5”
import View from './components/view'
import Link from './components/link'
export let _Vue
export function install (Vue) {
if (install.installed && _Vue === Vue) return
install.installed = true
_Vue = Vue
const isDef = v => v !== undefined
const registerInstance = (vm, callVal) => {
let i = vm.$options._parentVnode
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
Vue.mixin({
beforeCreate () {
if (isDef(this.$options.router)) {
this._routerRoot = this
this._router = this.$options.router
this._router.init(this)
Vue.util.defineReactive(this, '_route', this._router.history.current)
} else {
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
}
registerInstance(this, this)
},
destroyed () {
registerInstance(this)
}
})
Object.defineProperty(Vue.prototype, '$router', {
get () { return this._routerRoot._router }
})
Object.defineProperty(Vue.prototype, '$route', {
get () { return this._routerRoot._route }
})
Vue.component('RouterView', View)
Vue.component('RouterLink', Link)
const strats = Vue.config.optionMergeStrategies
// use the same hook merging strategy for route hooks
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
注册了RouterView、RouterLink两个全局组件
mixin的作用是将mixin的内容混合到Vue的初始参数options中
为什么是beforeCreate而不是created呢?因为如果是在created操作的话,$options已经初始化好了
如果当前是根实例:那么就会添加_routerRoo
t和_router
属性,this._routerRoot
也就是拿到根实例,this._router
就是传入的router
this._routerRoot = this
this._router = this.$options.router
如果不是根实例:也会添加_routerRoot
属性,如果
this._routerRoot = (this.$parent && this.$parent._routerRoot) || this
这样每个组件中都可以拿到可以获取到根组件和传入的router了,子组件可以通过this.$parent._router.$options
也就是this._routerRoot._router
来拿到传入的router了
判断了是不是根组件是根据有没有传入router属性,在main.js入口文件中,我们是传入了router属性,但是在App.vue组件中,我们不会传入router
new Vue({
router,
store,
render: (h) => h(App),
}).$mount('#app')
为什么判断当前组件是子组件,就可以通过父组件拿到_root根组件呢
父beforeCreate-> 父created -> 父beforeMount -> 子beforeCreate ->子create ->子beforeMount ->子 mounted -> 父mounted
在执行子组件的beforeCreate的时候,父组件已经执行完beforeCreate了,那理所当然父组件拿到父组件中的_router
,父组件又会向上一级父组件查找,一种递归式的查找
他们的区别主要是因为他们的实现原理不同
1》形式上,哈希模式有#号,历史模式没有;
哈希有#号,不美观;如果考虑url的规范那么就需要使用history模式,因为history模式没有#号,是个正常的url,适合推广宣传
2》监听的事件不一样
hash模式监听的是hashChange事件,history模式监听的是popState事件
2》pushState() 设置的新 URL 可以是与当前 URL 同源的任意 URL;而 hash 只可修改 # 后面的部分,因此只能设置与当前 URL 同文档的 URL
pushState() 设置的新 URL 可以与当前 URL 一模一样,这样也会把记录添加到栈中;而 hash 设置的新值必须与原来不一样才会触发动作将记录添加到栈中
4》请求不同,历史模式会请求完整的url地址,而哈希模式只会请求#前面的地址,所以需要设置404的代理;而哈希不会,默认会展示之前页面的内容,但是你可以配置对应404页面
5》哈希模式的SEO不好;历史模式SEO相对会更好
6》push 和 replace 方法的实现不一样
使用中常见的一些处理
哈希模式配置404:
{
path: '/404',
component: () => import('../views/404.vue'),
},
{
path: '*',
redirect: '/404',
},
history模式处理404
location / {
try_files $uri $uri/ /index.html$args;
}
当请求的文件或目录不存在时,将请求重定向到index.html。这使得所有的路由请求都指向了Vue应用的入口页面
在开发环境需要设置historyApiFallback
devServer: {
historyApiFallback: {
index: '/index.html',
},
},
this.$router.push()如果是跳和当前路由一样的路由,就会报错,下面就捕获了下错误:
const originalPush = VueRouter.prototype.push
const originalReplace = VueRouter.prototype.replace
VueRouter.prototype.push = function (location, onResolve, onReject) {
if (onResolve || onReject)
return originalPush.call(this, location, onResolve, onReject)
return originalPush.call(this, location).catch((err) => err)
}
VueRouter.prototype.replace = function (location, onResolve, onReject) {
if (onResolve || onReject)
return originalReplace.call(this, location, onResolve, onReject)
return originalReplace.call(this, location).catch((err) => err)
}