vue-router 是 Vue.js 官方的路由库,本着进修的目标,我对 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#/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.js、vue-router 以及其他 Vue 相干东西(Vuex、vue-loader)的示例,感兴趣能够看下:luobotang/vue-demo – github。