-
Notifications
You must be signed in to change notification settings - Fork 442
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
vue-router 源码分析-整体流程 #53
Comments
下次可以安利一下react-router吗? |
Open
mark |
1 similar comment
mark |
传说中的用github 的 issue 写博客。 有markdown 也有评论。666。 |
match那一块,其实挺复杂的 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
在前端框架 React、Vue.js 和 Angular 三足鼎立的年代, Vue.js 因其易用、易学、学习成本低等特点已经成为了广大前端er的新宠, 而其对应的路由 vue-router 也是简单好用, 功能强大. 本文将结合 Vue.js 来分析
vue-router
的整体流程.目录结构
[email protected] 的整体目录结构如下:
主要关注点就是
components
、history
目录以及create-matcher.js
、create-route-map.js
、index.js
、install.js
等文件. 下面以一个小 demo 来分析vue-router
的整体流程.入口
首先看 demo 入口的代码部分:
(2和4并无特定的顺序关系)
插件安装
在上述代码的第2步中, 利用了
Vue.js
的插件机制来安装vue-router
, 这有三个作用:$router
和$route
对象<router-view>
和<router-link>
组件Vue.js
通过use(plugin)
来安装插件时, 会调用 plugin 的install
方法, 如果没有该方法, 则将 plugin 本身作为函数来调用. 其实现如下:VueRouter 是在
src/index.js
中导出的, 提供了静态的install
方法:这是 Vue.js 插件的常规开发方式, 给 plugin 对象增加 install 方法, 然后在 install 中实现具体逻辑. 此外, 并作浏览器环境检测, 如果是在浏览器环境并且存在
window.Vue
就自动使用 plugin.浏览器环境的检测很简单:
install
作为一个单独的模块存在:这里导出一个私有的 Vue 引用的目的是: 插件不必将 Vue.js 作为一个依赖打包, 但插件的其它模块有可能要依赖 Vue 实例的一些方法, 其它模块可以从这里获取到 Vue 实例引用.
在
beforeCreate mixin
中, 在创建 Vue 实例时, 如果判断传入了 router(不传入 router, 在渲染router-view
组件时会因获取不到matched
属性而出错), 就将 router 赋值给私有属性_router
, 便于后续的初始化和 getter 定义.在 Vue.js 应用中, 所有组件都是 Vue 实例的扩展, 也就意味着所有的组件都可以访问到这个实例原型上定义的属性. 所以, VueRouter 将
$route
和$router
属性定义在了 Vue 实例的原型上.Router 实例化
在应用入口文件中, 对 VueRouter 进行了实例化, 并将其作为参数传给 Vue 实例的
options
. VueRouter 类的入口在src/index.js
:在实例化时, 主要作了两件事:
matcher
对象路由匹配
matcher
对象是由src/create-matcher.js
中的createMatcher
创建的:createMatcher
根据传入的routes
配置生成对应的路由 map, 然后直接返回一个matcher
对象.继续来看
src/create-route-map.js
中的createRouteMap
函数:cleanPath
的逻辑比较简单, 只是对双/
作正则替换从上述代码可以看出,
create-route-map.js
的主要功能是根据用户的routes
配置的path
、alias
以及name
来生成对应的路由记录, 方便后续匹配对应.History 实例化
VueRouter 提供了
HTML5History
、HashHistory
以及AbstractHistory
三种方式, 根据不同的mode
和环境来实例化 History. 所有的 History 类都是在src/history/
目录下, 并且都继承自src/history/base.js
:到这, History 就实例化完成了, VueRouter 的实例化也完成了. 接下来看下 Vue.js 的实例化.
Vue 实例化
在启动 Vue.js 应用之前, 需要先对其进行实例化, 并传入 VueRouter 实例:
在创建 Vue 实例时, 定义在
src/install.js
中的mixin
会被调用:在
beforeCreate
钩子中, 会判断实例化时options
是否包含router
.router
在这有两个作用:router-view
组件的渲染提供$route
router.init
只被调用一次对于第二点, 因为
mixin beforeCreate
是全局的, 其它非函数式组件(如 APP/Hello)渲染时, 该钩子会优先于组件内beforeCreate
(如果有)执行, 但$options
并不会有router
属性, 该属性只在app
被实例化时传入.如果有则进行
router
的初始化工作.从上述代码可以看出, 主要进行了
app
赋值, 针对于HTML5History
和HashHistory
特殊处理,因为在这两种模式下才有可能存在进入时候的不是默认页, 需要根据当前浏览器地址栏里的path
或者hash
来激活对应的路由, 此时就是通过调用transitionTo
来达到目的. 注意: 这里在处理HashHistory
时, 是在 route 切换完成之后再设置hashchange
的监听, 这是为了修复 vuejs/vue-router#725 而做的. 因为如果钩子函数beforeEnter
是异步的话,beforeEnter
钩子就会被触发两次. 因为在初始化时, 如果此时的hash
值不是以/
开头的话就会补上#/
, 这个过程会触发hashchange
事件, 就会再走一次生命周期钩子, 也就意味着会再次调用beforeEnter
钩子函数.transitionTo
的第一个参数是当前的location
, 其实现在src/history/base.js
中:在
transitionTo
中, 首先通过调用 VueRouter 实例的match
方法获取到和当前location
对应的route
对象:matcher.match
的实现在src/create-matcher.js
中:createRoute
的实现在src/util/route.js
中:回到
src/history/base.js
, 在transitionTo
方法中获取到匹配的route
之后, 就调用了confirmTransition
:从上述代码可知, 整个过程就是执行组件的各种钩子以及处理异步组件问题. 再回到之前看的
init
, 最后调用了history.listen
方法:listen
设置了 Route 改变之后的回调, 会在confirmTransition
的onComplete
回调中调用, 其作用就是更新下当前应用实例的_route
值. 在前文的分析中,_route
属性被定义为一个 reactive 属性, 初始值是当前的路由对象:history
的改变会去更新_route
, 进而触发 Vue 实例的更新机制, 调用render
去重新渲染界面.总结
vue-router
的整体流程就分析到这了. 由于篇幅有限, 省略了很多细节, 但不影响对整个流程的了解, 后续会再针对具体的模块(组件/History 等)进行具体的分析.The text was updated successfully, but these errors were encountered: