一,前言
上篇,介绍了路由匹配的实现,包括以下几个点:
本篇,继续介绍路由变化触发视图更新;
二,视图更新的实现
1,前文回顾
前面,通过this.router.match(location)
已经能够得到当前路径所匹配到的全部路由记录了;
createRoute
方法,能够根据路径创建一个匹配规则:
this.current = createRoute(null, {
path: '/'
});
复制代码
2,更新当前路由的匹配结果
那么,每当路径变化时,需要更新匹配规则current
:
transitionTo(location, onComplete) {
// 根据路径进行路由匹配;route :当前匹配结果
let route = this.router.match(location);
this.current = route; // 每次路由切换时,都会更改current属性
onComplete && onComplete();
}
复制代码
3,路由更新前的查重
但是每次路径的变化,未必每次都需要执行更新操作,需要进行查重操作:
第一次 null 匹配不到结果;
第二次 / 能够匹配到结果;
transitionTo(location, onComplete) {
// 根据路径进行路由匹配;route :当前匹配结果
let route = this.router.match(location);
// 查重:如果前后两次路径相同,且路由匹配的结果也相同,那么本次无需进行任何操作
if (location == this.current.path && route.matched.length == this.current.matched.length) { // 防止重复跳转
return
}
this.current = route; // 每次路由切换时,都会更改current属性
onComplete && onComplete();
}
复制代码
此时,当路由改变时 current 属性虽然更新了,但并不会导致视图的重新渲染;
4,路由的响应式实现
所以,需要让 history 中的 current 属性成为响应式数据,在模板中进行依赖收集,当数据变化时更新视图:
// install.js
Vue.mixin({
beforeCreate() {
if (this.$options.router) {// 根组件
this._routerRoot = this;
this._router = this.$options.router;
this._router.init(this);
// 目标:让 this._router.history.current 成为响应式数据;
// 作用:current用于渲染时会进行依赖收集,当current更新时可以触发视图更新;
// 方案:在根组件实例上定义响应式数据 _route,将this._router.history.current对象中的属性依次代理到 _route 上;
// 优势:当current对象中的任何属性发生变化时,都会触发响应式更新;
// Vue.util.defineReactive: Vue 构造函数中提供的工具方法,用于定义响应式数据
Vue.util.defineReactive(this, '_route', this._router.history.current);
} else { // 子组件
this._routerRoot = this.$parent && this.$parent._routerRoot;
}
}
});
复制代码
在处理根组件时,定义响应式数据 _route
:
目标:让 this._router.history.current 成为响应式数据;
作用:current 用于渲染时会进行依赖收集,当 current 更新时可以触发视图更新;
方案:在根组件实例上定义响应式数据 _route,将 this._router.history.current 对象中的属性依次代理到 _route 上;
优势:当 current 对象中的任何属性发生变化时,都会触发响应式更新;
当路径发生变化时,在transitionTo
方法中,会更新 current 属性为当前匹配到的路由结果 route:
transitionTo(location, onComplete) {
let route = this.router.match(location);
// 更新 current 属性为当前匹配到的路由结果 route
this.current = route;
onComplete && onComplete();
}
复制代码
更新 current,但 _router 并没有改变,这样不能触发响应式更新,需要再触发一次 _router 的更新:
// index.js#VueRouter
init(app) {
const history = this.history;
const setUpListener = () => {
history.setupListener();
}
history.transitionTo(
history.getCurrentLocation(),
setUpListener
)
// 每次路径变化时,都会调用此方法
// 触发根实例 app 上响应式数据 _route 的更新
history.listen((route)=>{
app._route = route;
});
}
复制代码
在公用路由处理 history/base.js 中添加 listen 方法,存储路由变化时的更新回调函数:
// history/base.js
listen(cb) {
// 存储路由变化时的更新回调函数,即 app._route = route;
this.cb = cb;
}
复制代码
并在路由变化时的transitionTo
方法中,触发_route 的更新回调:
// history/base.js
transitionTo(location, onComplete) {
let route = this.router.match(location);
if (location == this.current.path && route.matched.length == this.current.matched.length) { // 防止重复跳转
return
}
// 使用当前路由route更新current,并执行其他回调
this.updateRoute(route);
onComplete && onComplete();
}
listen(cb) {
// 存储路由变化时的更新回调函数,即 app._route = route;
this.cb = cb;
}
/**
* 路由变化时的相关操作:
* 1,更新 current;
* 2,触发_route的响应式更新;
* @param {*} route 当前匹配到的路由结果
*/
updateRoute(route) {
// 每次路由切换时,都会更改current属性
this.current = route;
// 调用保存的更新回调,触发app._route的响应式更新
this.cb && this.cb(route);
}
复制代码
当app._route
改变时,就会触发响应式数据更新,导致页面更新;
三,视图更新的实现
本篇,介绍了路由变化时视图更新的实现,主要涉及以下内容:
更新当前路由的匹配结果;
路由更新前的查重;
路由的响应式实现;
下一篇,$route
、$router
与 <router-link>
组件的实现;
评论