vue-router 是做爲插件集成到 vue 中的。javascript
咱們使用 vue-router 的時候,第一部就是要 安裝插件 Vue.use(VueRouter);
html
關於插件的介紹能夠查看 vue 的官方文檔vue
咱們重點關注如何開發插件java
Vue.js
要求插件應該有一個公開方法 install
。這個方法的第一個參數是 Vue
構造器,第二個參數是一個可選的選項對象。node
在 install
方法裏面,即可以作相關的處理:vue-router
Vue.prototype
上實現。MyPlugin.install = function (Vue, options) { // 1. 添加全局方法或屬性 Vue.myGlobalMethod = function () { // 邏輯... } // 2. 添加全局資源 Vue.directive('my-directive', { bind (el, binding, vnode, oldVnode) { // 邏輯... } ... }) // 3. 注入組件 Vue.mixin({ created: function () { // 邏輯... } ... }) // 4. 添加實例方法 Vue.prototype.$myMethod = function (methodOptions) { // 邏輯... } }
在粗略瞭解了 vue.js
插件的實現思路以後,咱們來看看 vue-router
的處理segmentfault
首先查看入口文件 src/index.js
ide
import { install } from './install'; // ...more VueRouter.install = install;
因此,具體的實如今 install
裏面。接下來咱們來看具體作了些什麼 ?函數
install 相對來講邏輯較爲簡單。主要作了如下幾個部分 :ui
經過一個全局變量來確保只安裝一次
// 插件安裝方法 export let _Vue; export function install(Vue) { // 防止重複安裝 if (install.installed && _Vue === Vue) return; install.installed = true; // ...more }
mixin
注入一些生命週期的處理export function install(Vue) { // ...more const isDef = v => v !== undefined; // 註冊實例 const registerInstance = (vm, callVal) => { let i = vm.$options._parentVnode; if ( isDef(i) && isDef((i = i.data)) && isDef((i = i.registerRouteInstance)) ) { i(vm, callVal); } }; // 混入生命週期的一些處理 Vue.mixin({ beforeCreate() { if (isDef(this.$options.router)) { // 若是 router 已經定義了,則調用 this._routerRoot = this; this._router = this.$options.router; this._router.init(this); Vue.util.defineReactive( this, '_route', this._router.history.current ); } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; } // 註冊實例 registerInstance(this, this); }, destroyed() { // 銷燬實例 registerInstance(this); } }); // ...more }
咱們看到 , 利用mixin
,咱們往實例增長了 beforeCreate
以及 destroyed
。在裏面註冊以及銷燬實例。
值得注意的是 registerInstance
函數裏的
vm.$options._parentVnode.data.registerRouteInstance;
你可能會疑惑 , 它是從哪裏來的 。
它是在 ./src/components/view.js
, route-view
組件的 render 方法裏面定義的。主要用於註冊及銷燬實例,具體的咱們後期再講~
經過如下形式,定義變量。咱們常常使用到的 this.$router ,this.$route
就是在這裏定義的。
// 掛載變量到原型上 Object.defineProperty(Vue.prototype, '$router', { get() { return this._routerRoot._router; } }); // 掛載變量到原型上 Object.defineProperty(Vue.prototype, '$route', { get() { return this._routerRoot._route; } });
這裏經過Object.defineProperty
定義get
來實現 , 而不使用Vue.prototype.$router = this.this._routerRoot._router
。
是爲了讓其只讀,不可修改
import View from './components/view'; import Link from './components/link'; export function install(Vue) { // ...more // 註冊全局組件 Vue.component('RouterView', View); Vue.component('RouterLink', Link); // ...more }
附上 install.js
完整的代碼
import View from './components/view'; import Link from './components/link'; export let _Vue; // 插件安裝方法 export function install(Vue) { // 防止重複安裝 if (install.installed && _Vue === Vue) return; install.installed = true; _Vue = Vue; const isDef = v => v !== undefined; // 註冊實例 const registerInstance = (vm, callVal) => { let i = vm.$options._parentVnode; if ( isDef(i) && isDef((i = i.data)) && isDef((i = i.registerRouteInstance)) ) { i(vm, callVal); } }; // 混入生命週期的一些處理 Vue.mixin({ beforeCreate() { if (isDef(this.$options.router)) { // 若是 router 已經定義了,則調用 this._routerRoot = this; this._router = this.$options.router; this._router.init(this); Vue.util.defineReactive( this, '_route', this._router.history.current ); } else { this._routerRoot = (this.$parent && this.$parent._routerRoot) || this; } // 註冊實例 registerInstance(this, this); }, destroyed() { registerInstance(this); } }); // 掛載變量到原型上 Object.defineProperty(Vue.prototype, '$router', { get() { return this._routerRoot._router; } }); // 掛載變量到原型上 Object.defineProperty(Vue.prototype, '$route', { get() { return this._routerRoot._route; } }); // 註冊全局組件 Vue.component('RouterView', View); Vue.component('RouterLink', Link); // 定義合併的策略 const strats = Vue.config.optionMergeStrategies; // use the same hook merging strategy for route hooks strats.beforeRouteEnter = strats.beforeRouteLeave = strats.beforeRouteUpdate = strats.created; }