• vue-router 实现分析


    我们分别从不同的视角来看 vue-router。

    http://mp.weixin.qq.com/s?__biz=MzUxMzcxMzE5Ng==&mid=2247485737&idx=1&sn=14fe8a5c72aaa98c11bf6fc57ae1b6c0&source=41#wechat_redirect

    • 从开发者角度来看,是这样的:
    var router = new VueRouter({
      routes: [
       {path: '/foo', component: {template: '<div>FOO</div>'}},
       {path: '/bar', component: {template: '<div>BAR</div>'}}
      ]
    })
    
    var vm = new Vue({
      el: '#app',
      router: router
    })

    我们创建一个 router,传入的 routes 中的每一项即为一条路由(route)配置,表示在匹配给定的地址时,应该使用什么组件渲染视图。

    router 传入 new Vue() 用于创建根组件,这样根组件中对应的视图区域,可以基于 router 中的配置,根据页面地址显示不同的内容。当然,这还需要在组件模板中使用 <router-view> 来定义区域。

    • 从视图角度来看,是这样的:
    <div id="app">
      ...
      <router-view></router-view>
      ...
    </div>

    页面地址变更后,<router-view> 对应的区域会更新为地址匹配的组件。例如,路径是 /foo 则对应区域显示 FOO,路径是 /bar 则显示 BAR,路径没有匹配的组件时,则不显示内容。

    • 从数据角度来看,是这样的:
    vm
    + _router | $router
      - history
      - matcher
    + _route | $route
      - matched

    vm.$router 引用当前组件对应的 router 对象,该对象在初始化时(在 vm 创建过程中执行初始化),会启动对页面地址变更的监听,从而在变更时更新 vm 的数据($route),进而触发视图的更新。

    深入

    如何实现对地址变更的监听?

    对于缺省的 HashHistory 模式(也就是基于页面地址的 hash 部分来实现路由功能,如 http://example.com/path#/foohttp://example.com/path#/bar),是通过监听 hashChange 事件来实现:

    window.addEventListener('hashchange', () => {
      // this.transitionTo(...)
    })

    源码

    这个动作是什么时候执行的呢?

    是在 router.init()源码)中调用的,而 router.init() 则是在根组件创建时(源码)调用的。

    而 Vue 组件在创建时,又怎么会去调用 router.init() 呢?

    这是由于 vue-router 将自身作为一个插件安装到了 Vue,通过 Vue.mixin() 注册了一个 beforeCreate() 钩子函数,从而在之后所有的 Vue 组件创建时都会调用该钩子函数,给了检查是否有 router 参数,从而进行初始化的机会。进而通过层层调用执行了监听 hashchange 事件的动作。

    整理一下:

    • new Vue()
    • 执行 vue-router 注入的 beforeCreate 钩子函数
    • 执行 router.init(vm)
    • 执行 history.setupListeners(),注册事件监听

    地址变更如何通知到 vm

    这个过程比较简单,hashchange 时,执行 history.transitionTo(...),在这个过程中,会进行地址匹配,得到一个对应当前地址的 route,然后将其设置到对应的 vm._route 上。

    只是进行了赋值,为什么 vm 就可以感知到路由的改变了呢?

    答案在 vue-router 注入 Vue 的 beforeCreate 钩子函数中(源码):

    Vue.util.defineReactive(this, '_route', this._router.history.current)

    采用与 Vue 本身数据相同的“数据劫持”方式,这样对 vm._route 的赋值会被 Vue 拦截到,并且触发 Vue 组件的更新渲染流程。

    地址变更如果同步视图更新?

    接上一步,vm._route 已经接收到路由的变更,从而触发视图更新。而当视图更新进一步调用到 <router-view>render() 时,即进入了 <router-view> 的处理(源码)。

    <router-view>render() 采用函数调用(h())模式,而非通过模板生成。这也是 Vue2 支持的定义组件渲染逻辑的方式,类似 React 的 render()。采用这种模式的好处是可以完全利用 JavaScript 的能力来编写逻辑,不必受制于 Vue 的类 HTML 模板语法。

    这里的主要处理逻辑是从根组件中取出当前的路由对象(parent.$route),然后取得该路由下对应的组件,然后交由该组件进行渲染:

    return h(component, data, children)

    这其中还涉及 <router-view> 嵌套的处理,不过主要逻辑就是这样了。

    小结

    其实 vue-router 从 <router-view> 的实现来看,就是一个具有特定功能的 Vue 组件而已,不过要配合根组件的 router 发挥作用。但整体还是很“响应式”的,也是蛮“Vue风格”的。

    vue-router 以插件方式“侵入”Vue,从而支持一个额外的 router 属性,以提供监听并改变组件路由数据的能力。这样每次路由发生改变后,可以同步到数据,进而“响应式”地触发组件的更新。

    <router-view> 作为根组件下的子组件,从根组件那里可以获取到当前的路由对象,进而根据路由对象的组件配置,取出组件并用其替换当前位置的内容。这样,也就完成整个路由变更到视图变更的过程。

    路由变更到视图变更的过程整理为:

    hashchange
    -->
    match route
    -->
    set vm._route
    -->
    <router-view> render()
    -->
    render matched component

    实现过程中的技术点包括:

    • Vue 插件机制
    • 响应式数据机制
    • Vue 渲染机制
    • 地址变更监听
  • 相关阅读:
    PLSQL远程访问Oracle数据库配置
    考勤系统之计算工作小时数
    C#压缩或解压(rar和zip文件)
    C#基础Queue(队列)的应用
    PetaPoco模糊查询
    牛腩购物14:商品相关表的设计 后台增加用户管理 Eval高级应用 商品类别无限分类,外键的建立,repeater嵌套repeater
    asp.net 4.0 新功能 路由
    sqlserver 2008 使用维护计划,备份数据库
    asp.net Linq和泛型,IEnumerable和IQueryable之间的区别,Lambda表达式,Linq to Sql停止开发转为 Entity Framework
    List 对象集合的操作和使用 List 集合对象 对象集合 自动属性 对象初始化 集合初始化器
  • 原文地址:https://www.cnblogs.com/Bobo999/p/8041160.html
Copyright © 2020-2023  润新知