如何使用vue-router
vue项目使用配置:
// main.js
import Vue from 'vue'
import VueRouter from 'vue-router'
import RouterDemo from './router.vue'
import { routes } from './router.js'
// 注册router插件
Vue.use(VueRouter)
// 创建router实例
const router = new VueRouter({ routes })
// 注入路由
new Vue({
el: "#app",
render: h => h(RouterDemo),
router
})
// router.js
import Foo from './components/Foo.vue'
import Bar from './components/Bar.vue'
export const routes = [
{ path: '/foo', component: Foo },
{ path: '/bar', component: Bar }
]
vue.use()
vue通过vue.use()注册插件拓展能力,通过_installedPlugins数组维护所有注册过的插件
如果该插件已经注册过,会直接返回this即vue实例,实现链式编程
一般通过插件的install函数完成注册
// vue/src/core/global-api/use.js
export function initUse (Vue: GlobalAPI) {
Vue.use = function (plugin: Function | Object) {
// 判断是否已经注册过
const installedPlugins = (this._installedPlugins || (this._installedPlugins = []))
if (installedPlugins.indexOf(plugin) > -1) {
return this
}
// 获取除了第一个以外的参数
const args = toArray(arguments, 1)
args.unshift(this)
// 执行插件中的install函数
// 注意第一参数是vue,这样router中就可以不import vue了 可以减小包体积
if (typeof plugin.install === 'function') {
plugin.install.apply(plugin, args)
} else if (typeof plugin === 'function') {
plugin.apply(null, args)
}
// 注册完成加入_installedPlugins
installedPlugins.push(plugin)
return this
}
}
vue-router的install
vue-router的install方法实现了哪些功能?
// vue-router的入口文件:src/index.js
export default class VueRouter {...}
// 实现了 install 的静态方法
// 当用户执行 Vue.use(VueRouter) 的时候,实际上就是在执行 install 函数
VueRouter.install = install
// src/install.js
export let _Vue
export function install (Vue) {
// 确保 install 逻辑只执行一次
if (install.installed && _Vue === Vue) return
install.installed = true
_Vue = Vue
// 利用 Vue.mixin 去把 beforeCreate 和 destroyed 钩子函数注入到每一个组件中
Vue.mixin({
beforeCreate () {
// ...
},
destroyed () {
// ...
}
})
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)
// ...
}
思维导图
// createRouteMap函数
// 遍历 routes 为每一个 route 执行 addRouteRecord 方法生成一条记录
routes.forEach(route => {
addRouteRecord(pathList, pathMap, nameMap, route, parentRoute)
})
// addRouteRecord 函数
function addRouteRecord (
pathList: Array<string>,
pathMap: Dictionary<RouteRecord>,
nameMap: Dictionary<RouteRecord>,
route: RouteConfig,
parent?: RouteRecord,
matchAs?: string
) {
var record = {
path: normalizedPath,
regex: compileRouteRegex(normalizedPath, pathToRegexpOptions),
components: route.components || { default: route.component },
alias: route.alias
? typeof route.alias === 'string'
? [route.alias]
: route.alias
: [],
instances: {},
enteredCbs: {},
name: name,
parent: parent,
matchAs: matchAs,
redirect: route.redirect,
beforeEnter: route.beforeEnter,
meta: route.meta || {},
props:
route.props == null
? {}
: route.components
? route.props
: { default: route.props }
};
// 嵌套子路由
route.children.forEach(child => {
const childMatchAs = matchAs
? cleanPath(`${matchAs}/${child.path}`)
: undefined
addRouteRecord(pathList, pathMap, nameMap, child, record, childMatchAs)
})
// 为 pathList 和 pathMap 各添加一条记录。
if (!pathMap[record.path]) {
pathList.push(record.path)
pathMap[record.path] = record
}
}
举例:
nameMap: {}
pathList: (2) ['/foo', '/bar']
pathMap : {
/bar: {
alias : [] beforeEnter : undefined components : {
default: {
beforeCreate: [ƒ]
beforeDestroy: [ƒ]
name: "Bar"
render: ƒ () staticRenderFns: []
__file: "src/components/Bar.vue"
_compiled: true
[[Prototype]]: Object
}
} enteredCbs : {} instances : {} matchAs : undefined meta : {} name : undefined parent : undefined path : "/bar" props : {} redirect : undefined regex : /^/bar(?:/(?=$))?$/i [[Prototype]]: Object
},
/foo: {
alias: []
beforeEnter: undefined
components: {
default: {
beforeCreate: [ƒ]
beforeDestroy: [ƒ]
name: "Foo"
render: ƒ () staticRenderFns: []
__file: "src/components/Foo.vue"
_compiled: true
[[Prototype]]: Object
}
}
enteredCbs: {}
instances: {}
matchAs: undefined
meta: {}
name: undefined
parent: undefined
path: "/foo"
props: {}
redirect: undefined
regex: /^/foo(?:/(?=$))?$/i
[[Prototype]]: Object
}
}
createRoute 函数
export function createRoute (
record: ?RouteRecord,
location: Location,
redirectedFrom?: ?Location,
router?: VueRouter
): Route {
// 路由表
const route: Route = {
name: location.name || (record && record.name),
meta: (record && record.meta) || {},
path: location.path || '/',
hash: location.hash || '',
query,
params: location.params || {},
fullPath: getFullPath(location, stringifyQuery),
matched: record ? formatMatch(record) : []
}
return Object.freeze(route)
}
// 打平父子路由
function formatMatch (record: ?RouteRecord): Array<RouteRecord> {
const res = []
while (record) {
res.unshift(record)
record = record.parent
}
return res
}
resolveQueue 函数
function resolveQueue (
current: Array<RouteRecord>,
next: Array<RouteRecord>
): {
updated: Array<RouteRecord>,
activated: Array<RouteRecord>,
deactivated: Array<RouteRecord>
} {
let i
const max = Math.max(current.length, next.length)
for (i = 0; i < max; i++) {
if (current[i] !== next[i]) {
break
}
}
return {
updated: next.slice(0, i),
activated: next.slice(i),
deactivated: current.slice(i)
}
}
history.listen
// index.js init 函数中
history.listen(route => {
this.apps.forEach(app => {
app._route = route
})
})
// history base.js
listen (cb: Function) {
this.cb = cb
}
// confirmTransition 成功回调
updateRoute (route: Route) {
this.current = route
this.cb && this.cb(route)
}
路径切换
思考:路由切换的时候 先更新地址栏路由地址,然后渲染页面?
完整导航解析流程
- 导航被触发。
- 在失活的组件里调用
beforeRouteLeave守卫。
- 调用全局的
beforeEach守卫。
- 在重用的组件里调用
beforeRouteUpdate守卫(2.2+)。
- 在路由配置里调用
beforeEnter。
- 解析异步路由组件。
- 在被激活的组件里调用
beforeRouteEnter。
- 调用全局的
beforeResolve守卫(2.5+)。
- 导航被确认。
- 调用全局的
afterEach钩子。
- 触发 DOM 更新。
- 调用
beforeRouteEnter守卫中传给next的回调函数,创建好的组件实例会作为回调函数的参数传入。
组件内的守卫
beforeRouteEnter
beforeRouteUpdate
beforeRouteLeave
思维导图
runQueue
// 这是一个非常经典的异步函数队列化执行的模式
export function runQueue (queue: Array<?NavigationGuard>, fn: Function, cb: Function) {
const step = index => {
if (index >= queue.length) {
cb()
} else {
if (queue[index]) {
fn(queue[index], () => {
step(index + 1)
})
} else {
step(index + 1)
}
}
}
step(0)
}
RouterView
思维导图
parent.$route
routerView所在组件的路由表
RouterLink
思维导图
router.resolve
解析当前link的路由信息
link颜色可自定义
关于我
欢迎关注我的公众号~ 不定期更新各种干活