VUE-ROUTER注册
主要就是定义获取$router
和$route
的方式,然后就是注册RouterView
和RouterLink
两个组件,最后定义路由钩子的合并策略。其它还包括配合缓存组件实例。
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
// registerRouteInstance 在 src/components/view
// 用于替换缓存的实例
if (isDef(i) && isDef(i = i.data) && isDef(i = i.registerRouteInstance)) {
i(vm, callVal)
}
}
Vue.mixin({
beforeCreate () {
// options里有router表示根组件,在根组件中存储router实例
if (isDef(this.$options.router)) {
this._routerRoot = this
this._router = this.$options.router
// 在根组件里初始化
this._router.init(this)
// 把_route响应式的挂载在根组件实例
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
// 将组件内的路由钩子用和created一样的合并策略
strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created
}
init
init中对根组件的实例缓存,并监听根组件销毁事件,然后监听路由变化事件、popstate事件、滚动事件,并且完成第一次路由跳转。
init (app: any /* Vue component instance */) {
process.env.NODE_ENV !== 'production' &&
assert(
install.installed,
`not installed. Make sure to call \`Vue.use(VueRouter)\` ` +
`before creating root instance.`
)
this.apps.push(app)
// 注销后删除当前实例,如果没有实例了则清空监听事件
app.$once('hook:destroyed', () => {
const index = this.apps.indexOf(app)
if (index > -1) this.apps.splice(index, 1)
if (this.app === app) this.app = this.apps[0] || null
if (!this.app) {
this.history.teardownListeners()
}
})
// main app previously initialized
// return as we don't need to set up new history listener
if (this.app) {
return
}
this.app = app
const history = this.history
if (history instanceof HTML5History || history instanceof HashHistory) {
const handleInitialScroll = routeOrError => {
const from = history.current
const expectScroll = this.options.scrollBehavior
const supportsScroll = supportsPushState && expectScroll
if (supportsScroll && 'fullPath' in routeOrError) {
handleScroll(this, routeOrError, from, false)
}
}
const setupListeners = routeOrError => {
// 会监听hashchange/popstate事件,并记录滚动位置和跳转页面
history.setupListeners()
handleInitialScroll(routeOrError)
}
// 初始化第一次跳转
history.transitionTo(
history.getCurrentLocation(),
setupListeners,
setupListeners
)
}
history.listen(route => {
this.apps.forEach(app => {
// 跳转时替换_route触发更新
app._route = route
})
})
}
VUE-ROUTER实例化
在构造函数中主要解析options.routes,生成相关缓存;初始化mathcer生成match和addRoutes两个API,分别用于解析跳转路由和添加路由;按mode初始化history,用于处理地址解析和相关操作。
constructor (options: RouterOptions = {
}) {
this.app = null
this.apps = []
this.options = options
this.beforeHooks = []
this.resolveHooks = []
this.afterHooks = []
// 返回match和addRoutes两个API,并解析options.routes
// match:用于生成$route相关属性
// addRoutes:添加路由
this.matcher = createMatcher(options.routes || [], this)
let mode = options.mode || 'hash'
this.fallback =
mode === 'history' && !supportsPushState && options.fallback !== false
if (this.fallback) {
mode = 'hash'
}
if (!inBrowser) {
mode = 'abstract'
}
this.mode = mode
// 用不同方式处理路由解析等操作
switch (mode) {
case 'history':
this.history = new HTML5History(