一,前言
上篇,介绍了路由匹配的实现,包括以下几个点:
本篇,继续介绍路由变化触发视图更新;
二,视图更新的实现
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> 组件的实现;
评论