共计 2688 个字符,预计需要花费 7 分钟才能阅读完成。
问题
1. 为什么 url 变更时,router-view 组件就能渲染出在 routes 中定义的与当前 path 匹配的组件?
- root vue 实例上定义了一个响应式属性
Vue.util.defineReactive(this, '_route', this._router.history.current)
- url 变更时,会匹配到最新的 route,并且会设置
this._routerRoot._route
, 触发setter
- router-view 组件的 render 函数中有使用到
parent.$route
, 也就是间接的触发了this._routerRoot._route
的getter
。 - 则
this._routerRoot._route
的setter
触发时即会触发router-view
的渲染 watcher
, 再次渲染, 并且此时拿到的路由组件也是最新的。
本质上利用了 vue 的响应式属性,在 route 属性变更和 router-view 视图渲染之间建立关系。
route 变更 => render 重新渲染
2. router-view render 中使用到 parent.$route
为什么就会被 this._routerRoot._route
收集 watcher
在挂载 router-view 组件时,会生成一个 watcher, router-view 的 render 函数中又触发了_route 的 getter 方法,那么此 watcher 就被收集到_route 的 Dep 中了。
在_route 的 setter 触发时,自然执行了这个 watcher, 组件重新 render。
在 vue 的仓库中 vue/src/core/instance/lifecycle.js
中mountComponent
方法中,能看到挂载组件时是会生成一个 watcher
的:
export function mountComponent(
vm: Component,
el: ?Element,
hydrating?: boolean
) {
...
let updateComponent
updateComponent = () => {vm._update(vm._render(), hydrating)
}
new Watcher(vm, updateComponent, noop, {before() {...}
})
...
return vm
}
3. this.$router, this.$route 是怎么挂载每个 vue 组件上的?
Object.defineProperty(Vue.prototype, '$router', {get () {return this._routerRoot._router}
})
Object.defineProperty(Vue.prototype, '$route', {get () {return this._routerRoot._route}
})
4. 替换 routes 的写法(这样写为什么有用)
// 替换现有 router 的 routes
router.matcher = new VueRouter({routes: newRoutes}).matcher
router.matcher 是比较核心的一个属性。对外提供两个方法 match(负责 route 匹配), addRoutes(动态添加路由)。
具体原因 :在做路径切换transitionTo
方法中,首先就会使用 const route = this.router.match(location, this.current)
来匹配 route, 其实内部会使用 matcher 来做匹配。修改了 watcher 即新的 routes 生效。
对 router.matcher 属性做修改,即新的 routes 就会替换老的 routes, 其实就是 replaceRoutes()
的含义。
export type Matcher = {match: (raw: RawLocation, current?: Route, redirectedFrom?: Location) => Route;
addRoutes: (routes: Array<RouteConfig>) => void;
};
5. router-view 是什么
<router-view> 组件是一个 functional 组件,渲染路径匹配到的视图组件。<router-view> 渲染的组件还可以内嵌自己的 <router-view>,根据嵌套路径,渲染嵌套组件。
<transition>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
工作原理简要流程图
hashchange
-->
match route
-->
set vm._route
-->
<router-view> render()
-->
render matched component
主要流程
-
初始化
- 实例化 Router, options 作为属性,根据 mode 生成 HTML5History 实例,或 HashHistory 实例
- 数据劫持,this.router.route =》this.history.current
- 数据劫持,this.history.current.route 变化时,自动执行 render。
- 立即执行一次路由跳转(渲染路由组件)
-
路由监听
- HashHistory 监听 hashChange 事件,HTML5History 监听 popstate 事件
-
路由变化处理
-
两种方式触发路由变化
- a 标签 href 方法(url 先变化,会触发两个 history 监听的 hashChange 或 popstate 事件,然后进入路由变化处理)
- 调用 router 的 push, replace, go 方法(先进入路由变化处理,然后修改 url)
-
路由变化具体处理过程
- history.transitionTo(根据 path 匹配到相应的 route, 然后调度执行 hooks, 然后更新 this.current 属性,触发视图更新)
- history.confirmTransition(调度执行上一个 route 和下一个 route 的相关生命周期 hooks)
-
-
router 的辅助 API
- push(先进行路由变化处理,在更新 url,使用 history.pushState)
- replace() 和 push 处理方式一致, 只是更新 url 使用 history.replaceState
- go 使用 history.go 方法
参考链接
https://cnodejs.org/topic/58d…
https://zhuanlan.zhihu.com/p/…
https://ustbhuangyi.github.io…【这个比较详细,但是废话太多,看不清重点】