写点什么

【VueRouter 源码学习】第七篇 - 路由变化触发视图更新

用户头像
Brave
关注
发布于: 2021 年 09 月 09 日
【VueRouter 源码学习】第七篇 - 路由变化触发视图更新

一,前言


上篇,介绍了路由匹配的实现,包括以下几个点:


  • 路由匹配的分析;

  • 路由匹配的实现:router.match、matcher.match、createRoute;


本篇,继续介绍路由变化触发视图更新;


二,视图更新的实现

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> 组件的实现;

用户头像

Brave

关注

还未添加个人签名 2018.12.13 加入

还未添加个人简介

评论

发布
暂无评论
【VueRouter 源码学习】第七篇 - 路由变化触发视图更新