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