vue-routerソース解析(二)プラグイン実装


vue-routerプラグイン方式の実現
vue-routerはプラグインとしてvueに統合されています.
vue-routerを使用する場合、最初の部分はプラグインVue.use(VueRouter);をインストールすることです.
プラグインについてはvueの公式ドキュメントを参照できます
プラグインの開発方法に重点を置いています
プラグインの開発方法Vue.jsは、プラグインに開示された方法installがあるべきであることを要求する.この方法の第1のパラメータはVueコンストラクタであり、第2のパラメータはオプションのオプションオブジェクトである.installの方法では、関連する処理を行うことができます.
  • グローバルメソッドまたは属性
  • を追加
  • グローバルリソースの追加:コマンド/フィルタ/遷移など、
  • グローバルmixinメソッドでコンポーネントオプションを追加します.
  • Vueインスタンスメソッドを追加することは、Vue.prototypeに追加することによって実現される.
  • は、上述の1つまたは複数の機能
  • を同時に提供する独自のAPIを提供するライブラリである.
    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の処理を見てみましょう.
    vue-routerのinstall
    まず、エントリファイルsrc/index.jsを参照してください.
    import { install } from './install';
    // ...more
    VueRouter.install = install;

    したがって、具体的な実装はinstallにある.次に具体的に何をしたのか見てみましょう.
    インストール実装
    Installは比較的論理的に簡単です.主に以下の部分を行いました.
    重複インストールの防止
    グローバル変数を1つ使用して、1回のみインストールできることを確認します.
    //       
    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;
    }

    その他
    シリーズ記事リスト個人ブログ