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#/foo、http://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-router 实现分析的更多相关文章
- 深入浅出的webpack4构建工具---webpack+vue+router 按需加载页面(十五)
1. 为什么需要按需加载? 对于vue单页应用来讲,我们常见的做法把页面上所有的代码都打包到一个bundle.js文件内,但是随着项目越来越大,文件越来越多的情况下,那么bundle.js文件也会越来 ...
- Vue躬行记(8)——Vue Router
虽然Vue.js未提供路由功能,但是官方推出了Vue Router(即vue-router库),以插件的形式支持.它与Vue.js深度集成,可快速的创建单页应用(Single Page Applica ...
- Vue Router路由守卫妙用:异步获取数据成功后再进行路由跳转并传递数据,失败则不进行跳转
问题引入 试想这样一个业务场景: 在用户输入数据,点击提交按钮后,这时发起了ajax请求,如果请求成功, 则跳转到详情页面并展示详情数据,失败则不跳转到详情页面,只是在当前页面给出错误消息. 难点所在 ...
- Vue 2.0 + Vue Router + Vuex
用 Vue.js 2.x 与相配套的 Vue Router.Vuex 搭建了一个最基本的后台管理系统的骨架. 当然先要安装 node.js(包括了 npm).vue-cli 项目结构如图所示: ass ...
- vue router 只需要这么几步
<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8" ...
- Vue.js 2.x笔记:路由Vue Router(6)
1. Vue Router简介与安装 1.1 Vue Router简介 Vue Router 是 Vue.js 官方的路由管理器.它和 Vue.js 的核心深度集成,构建单页面应用. Vue Rout ...
- 前端Vue 源码分析-逻辑层
Vue 源码分析-逻辑层 预期的效果: 监听input的输入,input在输入的时候,会触发 watch与computed函数,并且会更新原始的input的数值.所以直接跟input相关的处理就有3处 ...
- Vue Router学习笔记
前端的路由:一个地址对应一个组件 Vue Router中文文档 一.路由基本使用 第1步:导入Vue Router: <script src="https://unpkg.com/vu ...
- vue router.push(),router.replace(),router.go()和router.replace后需要返回两次的问题
转载:https://www.cnblogs.com/lwwen/p/7245083.html https://blog.csdn.net/qq_15385627/article/details/83 ...
- 前端MVC Vue2学习总结(八)——Vue Router路由、Vuex状态管理、Element-UI
一.Vue Router路由 二.Vuex状态管理 三.Element-UI Element-UI是饿了么前端团队推出的一款基于Vue.js 2.0 的桌面端UI框架,手机端有对应框架是 Mint U ...
随机推荐
- 性能测试工具LoadRunner32-LR之windows性能监控Perfmon
Perfmon是啥? Perfmon提供了图表化的系统性能实时监视器.性能日志和警报管理,可以用于监视CPU使用率.内存使用率.硬盘读写速度.网络速度等 性能分析方法 内存分析方法 内存分析用于判断系 ...
- [转]Javascript的匿名函数
本文转自:http://dancewithnet.com/2008/05/07/javascript-anonymous-function/ 一.什么是匿名函数? 在Javascript定义一个函数一 ...
- Windows 10 JDK安装及环境配置(vim+gcc)
JDK安装 首先去官网下载JDK:点击进入 下载后点击安装: 中途会提示安装jre,注意jre的安装文件夹和jdk的不能相同,不然会覆盖掉jdk里面的jre文件.可以创建一个Java文件夹.将jdk和 ...
- 给用户添加sodu权限
vim /etc/sudoers 进入编辑模式,找到这一 行:"root ALL=(ALL) ALL"在起下面添加"xxx ALL=(ALL) ALL"(这里的 ...
- 【转】Android 中的 Service 全面总结
1.Service的种类 按运行地点分类: 类别 区别 优点 缺点 应用 本地服务(Local) 该服务依附在主进程上, 服务依附在主进程上而不是独立的进程,这样在一定程度上节约了资源,另 ...
- 语文,数学,ps
数学教2课以集合为主. 集合:是指具有某种特定性质的具体的或抽象的对象汇总成的集体,这些对象称为该集合的元素. 特点:确定性给定一个集合,哪些对象是这个集合元素,不允有模棱两可的情况.互用性任何两个元 ...
- WCF:无法满足对安全令牌的请求,因为身份验证失败。
服务端和客户端如果有认证的话的这样: <wsHttpBinding> <binding name="WSHttpBinding_IService1" closeT ...
- IoC(控制反转)和DI(依赖注入)
一.IOC 1.目标类 提供UserService接口和实现类 获得UserService实现类的实例 之前开发中,直接new一个对象即可,使用spring之后,将由spring创建 -->I ...
- VS2013 自定义项目模板以及制作.vsix文件
一.环境检查 打开VS2013新建项目.如果在"其他项目类型"中不包含扩展性节点,则需要下载并安装vs2013 SDK. 二.创建项目模板 1,在VS中新建一个类库项目(此处仅以类 ...
- JavaScript比较和逻辑运算符
JavaScript比较和逻辑运算符 JavaScript比较和逻辑运算符 比较和逻辑运算符用于测试true或者false. 比较运算符 比较运算符在逻辑语句中使用,以测定变量或值是否相等 例如设定x ...