很多时候面试如果你的技术栈中有Vue,那么会经常被问到Vue-Router的相关知识,所以今天给大家带来Vue-Router路由的全面分析,助你过五关斩六将,工资蹭蹭涨。
如果对您有所帮助,可以点个小赞咯~ ~
1.Vue-Router原理
1.总的说来,Vue-Router通过监听浏览器History对象的变化,再根据路由表中配置的路由对应关系,通过RouterView组件渲染到VuePage上,从而实现不刷新页面并跳转。
那么问题来了,首先,如何监听History对象变化?
监听History变化
通常来说,在Window对象上有两个事件可以实现对History对象变化的监听:
1. onhashchange
可以用来监听URL中“#”后的hash变化。
例如URL本来为:https://juejin.im/editor/drafts/6890734538818125837#ab
此时改变#后的hash值为#abc,页面不会触发请求或更新,但会触发onhashchange的监听事件,
通过监听hash的改变来实现单页面路由的切换。
window.onhashchange = function(){
console.log(location.hash) //abc
}
2. popstate
可以用来监听History栈的改变。
popstate可以用来监听浏览器点击前进/后退按钮、或者是调用了history.back()、history.go()、history.forward()、时均会触发API;
window.addEventListener('popstate',()=>{
console.log('1',history)
})
//tips:Html5中可以用
//history.pushState({},"title","anotherpage.html")
//或history.replaceState({},"title","anotherpage.html")
//来添加或重置当前History栈中最后一项,但这两个API调用时并不会触发popstate的监听。
2.从零开始的Vue-router手写
本文这次手写主要使用的是hash模式,主要分为3个步骤:
1.实现vue-router的插件功能,使引入手写的vue-router后能够直接使用vue.use()方法把router实例挂载到vue上,从而能够全局访问Vue.$router
项目初始化后,先在src目录新建self_router目录,里面包含index.js和self_router.js
1.1 其中index.js内容与平常使用router中的index.js一样,主要作用是将 router配置对象传入 new self_router()中并将self_router实例暴露出去。
index.js如下:
import Vue from 'vue'
import VueRouter from './self_router.js' //自定义router
import Home from '../views/Home.vue'
Vue.use(VueRouter)
const routes = [
{
path: '/',
name: 'Home',
component: Home
},
{
path: '/about',
name: 'About',
component: function () {
return import(/* webpackChunkName: "about" */ '../views/About.vue')
}
}
]
const router = new VueRouter({
mode: 'hash',
base: process.env.BASE_URL,
routes
})
export default router
初始化self_router.js 主要是实现在index.js中能够引入自身并use,然后可以new 一个self_router的实例。
vue.use(xxx)方法实际上时调用了xxx.install()方法,所以我们在self_router的install方法中完成vue中对router实例的挂载。
self_router.js代码如下:
let self_vue;
class self_router {
constructor(options){ //这里的options是index.js中new router时传入的配置对象
this.options = options;
}
}
self_router.install = function(Vue){
self_vue = Vue;
Vue.mixin({
beforeCreate(){
if (this.$options.router){
Vue.prototype.$router = this.$options.router;
}
}
})
}
module.exports = self_router
因为要确保vue.$router先传入router实例,再挂载到vue上,所以在install中使用vue.mixin()方法,在beforeCreate生命周期中再执行挂载,确保此时已传入了router实例。
运行项目,会发现此时控制台报错:“router-link”和“router-view”组件不存在,所以开始第二步。
2.实现router-view 和 router-link 两个自定义组件的全局注册和基本功能,使用户在点击router-link时,能够触发url中hash的改变。
self_router.js中self_router.install方法改写入下:
self_router.install = function(Vue){
self_vue = Vue;
Vue.mixin({
beforeCreate(){
if (this.$options.router){
Vue.prototype.$router = this.$options.router;
}
}
})
Vue.component('router-link',{//注册router-link组件
props:{
to:{//router-link组件调用时的父组件传参 “to”
type:String,
required:true
}
},
render(h){//用渲染函数写了一个router-link,
//相当于写了一个template组件或是用jsx写了个组件
return h('a',{attrs:{href:"#"+this.to}},this.$slots.default) //参数分别为:tagName,attr,是vue默认的子节点内容
}
})
Vue.component('router-view',{//注册router-view组件,这一步只是先保证不报错
render(h){
return h('div','view')
}
})
}
此时,点击页面中的不同router-link,已经能实现url中的对应hash变换。
3.对url的hash值进行监听,当其改变时,触发渲染对应的route中的组件。此时,点击router-link或修改hash值都可以触发router-view中视图层的更新。
self_router.js完整版如下
let self_vue;
class self_router {
constructor(options) { //这里的options是index.js中new router时传入的配置对象
this.options = options;
this.initHash = window.location.hash.slice(1) || '/';
//创建一个私有属性,表示最新的hash值,URL中hash值变化时改变这个值,这个值变化时,通知视图更新(通过将这个私有属性变为响应式即可)。
self_vue.util.defineReactive(this,'current',this.initHash)
//监听hash发生变化
window.addEventListener('hashchange', this.onHashChange.bind(this))//调用bind是因为让onhashchange的this指向self_router
window.addEventListener('onload', this.onHashChange.bind(this))
}
//hash变化时,改变this.current
onHashChange() {
this.current = window.location.hash.slice(1)//截取#之后的hash字符串
}
}
self_router.install = function (Vue) {
self_vue = Vue;
Vue.mixin({
beforeCreate() {
if (this.$options.router) {
Vue.prototype.$router = this.$options.router;
}
}
})
Vue.component('router-link', {//注册router-link组件
props: {
to: {//router-link组件调用时的父组件传参 “to”
type: String,
required: true
}
},
render(h) {//渲染函数写了一个建议的router-link,
//相当于写了一个template组件或是用jsx写了个组件
return h('a', { attrs: { href: "#" + this.to } }, this.$slots.default) //参数分别为:tagName,attr,是vue默认的子节点内容
}
})
Vue.component('router-view', {//注册router-view组件,这一步只是先保证不报错
render(h) {
const routes = this.$router.options.routes;
let currentRoute = routes.find((item) => {
return item.path == this.$router.current
})
let renderComponent = currentRoute.component || null
return h(renderComponent)
}
})
}
module.exports = self_router
此时,点击router-link或者修改url中的hash值,router-view中会自动根据传入的routes来渲染对应的组件视图。 综上3步,已完成vue-Router基础版的自定义手写,如果对你有所帮助,那将是我莫大的荣幸!