当前位置: 首页 > news >正文

vue-router 实现分析

vue-router 是 Vue.js 官方的路由库,本着学习的目的,我对 vue-router 的源码进行了阅读和分析,分享出来给其他感兴趣的同学做个参考吧。

参考

  • 源码:vuejs/vue-router v2.2.1 - github

  • 文档:vue-router 官方中文教程

初步

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

  • 从开发者角度来看,是这样的:

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 渲染机制

  • 地址变更监听

最后

我写了一个应用 Vue.js、vue-router 以及其他 Vue 相关工具(Vuex、vue-loader)的示例,感兴趣可以看下:luobotang/vue-demo - github。

相关文章:

  • 两个vlan之间单向控制,reflexiveacl配置介绍 转
  • 博为峰JavaEE技术文章 ——MyBatis Hibernate 工作原理
  • [3300万人的聊天室] 作为产品的上游公司该如何?
  • 技术的悟道之一 --认清自己
  • 【洛谷】3375 KMP字符串匹配
  • 移动无标题(边框)窗体
  • mysql创建数据表时如何判断是否已经存在?
  • c#使用多个远程桌面连接
  • [存档]名词解释
  • 单链表中一个插入操作的分析
  • js如何打印object对象
  • 使用Apache CXF创建简单Web Service
  • java中Keytool的使用总结 (加密 密钥(key)和证书(certificates))
  • 又到母亲节
  • Java学习的好群,极力推荐!
  • JavaScript-如何实现克隆(clone)函数
  • [译] 理解数组在 PHP 内部的实现(给PHP开发者的PHP源码-第四部分)
  • 【译】React性能工程(下) -- 深入研究React性能调试
  • ES6核心特性
  • iOS编译提示和导航提示
  • JAVA多线程机制解析-volatilesynchronized
  • java中的hashCode
  • js ES6 求数组的交集,并集,还有差集
  • MobX
  • Node.js 新计划:使用 V8 snapshot 将启动速度提升 8 倍
  • 开源中国专访:Chameleon原理首发,其它跨多端统一框架都是假的?
  • 三栏布局总结
  • 微服务入门【系列视频课程】
  • 学习HTTP相关知识笔记
  • 一道闭包题引发的思考
  • 译米田引理
  • 用Canvas画一棵二叉树
  • Java总结 - String - 这篇请使劲喷我
  • 你学不懂C语言,是因为不懂编写C程序的7个步骤 ...
  • 支付宝花15年解决的这个问题,顶得上做出十个支付宝 ...
  • ​​​​​​​sokit v1.3抓手机应用socket数据包: Socket是传输控制层协议,WebSocket是应用层协议。
  • #Z2294. 打印树的直径
  • (草履虫都可以看懂的)PyQt子窗口向主窗口传递参数,主窗口接收子窗口信号、参数。
  • (读书笔记)Javascript高级程序设计---ECMAScript基础
  • (附源码)基于SSM多源异构数据关联技术构建智能校园-计算机毕设 64366
  • (附源码)计算机毕业设计SSM智能化管理的仓库管理
  • ******IT公司面试题汇总+优秀技术博客汇总
  • .bat批处理(三):变量声明、设置、拼接、截取
  • .class文件转换.java_从一个class文件深入理解Java字节码结构
  • .desktop 桌面快捷_Linux桌面环境那么多,这几款优秀的任你选
  • .Family_物联网
  • .net 7 上传文件踩坑
  • .Net Core webapi RestFul 统一接口数据返回格式
  • .NET 使用配置文件
  • .NET4.0并行计算技术基础(1)
  • .NET导入Excel数据
  • @serverendpoint注解_SpringBoot 使用WebSocket打造在线聊天室(基于注解)
  • [ C++ ] 类和对象( 下 )
  • [BUUCTF 2018]Online Tool
  • [BZOJ 4034][HAOI2015]T2 [树链剖分]