Vueプラグインの実装で発生した問題の概要

Vueプラグインの実装で発生した問題の概要

シーン紹介

最近、H5 の作業中に、各ページにタイトル付きのヘッダーを表示する必要があるというシナリオに遭遇しました。実装のアイデアの 1 つは、グローバル コンポーネントを使用することです。 TheHeader.vue というグローバル コンポーネントを作成するとします。疑似コードは次のようになります。

<テンプレート>
    <h2>{{ タイトル }}</h2>
</テンプレート>

<スクリプト>
エクスポートデフォルト{
小道具: {
    タイトル:
        タイプ: 文字列、
        デフォルト: ''
    }
}
}
</スクリプト>

グローバル コンポーネントを作成したら、各ページ コンポーネントでそれを参照し、props に渡します。たとえば、ページAでこのコンポーネントを参照する場合、ページAに対応するコンポーネントはA.vueです。

<テンプレート>
    <div>
        <TheHeader:title="タイトル" />
    </div>
</テンプレート>
<スクリプト>
    エクスポートデフォルト{
        データ() {
            タイトル: ''
        },
        作成された(){
            this.title = '私のホームページ'
        }
    }
</スクリプト>

使い方は非常に簡単ですが、欠点が 1 つあります。ヘッダー コンポーネントが多数のプロパティを渡す必要がある場合、ページ コンポーネントで対応するプロパティを維持するのが面倒になります。この場合、このシナリオを実装するには、Vue プラグインを使用するのがより良い方法です。

Vue プラグインを使用する場合、A.vue コンポーネントで head コンポーネントを呼び出す同じメソッドはより簡潔になります。

<テンプレート>
    <div />
</テンプレート>
<スクリプト>
    エクスポートデフォルト{
        作成された(){
            this.$setHeader('マイホームページ')
        }
    }
</スクリプト>

Vue プラグインを使用して実装する場合、A.vue に TheHeader コンポーネントを明示的に配置する必要はなく、A.vue のデータ関数に対応するプロパティを配置する必要もないことがわかります。関数を呼び出すだけで済みます。それで、このプラグインはどのように機能するのでしょうか?

プラグインの実装

具体的な実施手順は以下のとおりです。

  1. SFC(単一ファイルコンポーネント)を作成します。ここにヘッダーコンポーネントがあります。
  2. plugin.js ファイルを作成し、SFC を導入し、Vue.extend メソッドを使用して新しい Vue コンストラクターを取得してインスタンス化します。
  3. 関数呼び出しを介して Vue コンポーネント インスタンスをインスタンス化および更新します。

上記の手順に従って、plugin.js ファイルを作成しましょう。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

定数ヘッダープラグイン = {
    インストール(Vue) {
        const vueInstance = new (Vue.extend(TheHeader))().$mount()
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = タイトル
            document.body.prepend(vueInstance.$el)
            
        }
    }
}
Vue.use(ヘッダープラグイン)

次に、plugin.js を main.js に導入して、プラグイン実装の論理プロセス全体を完了します。しかし、このプラグインは実装されているものの、かなり問題があります。

問題1: 重複したヘッダーコンポーネント

これを単一ページ コンポーネントで使用する場合、router.push メソッドを使用する限り、新しいページに 2 つのヘッダー コンポーネントが表示されるという不思議な問題が発生します。さらに数回ジャンプすると、ヘッドコンポーネントの数も増えます。これは、各ページでこのメソッドを呼び出すため、各ページが対応する DOM をドキュメントに配置するためです。

これを考慮して、上記のコンポーネントを最適化する必要があります。インスタンス化プロセスをプラグインの外部に配置します。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

const vueInstance = new (Vue.extend(TheHeader))().$mount()
定数ヘッダープラグイン = {
    インストール(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = タイトル
            document.body.prepend(vueInstance.$el)
            
        }
    }
}
Vue.use(ヘッダープラグイン)

このプロセスでは、引き続き DOM がドキュメントに繰り返し挿入されます。ただし、同じ vue インスタンスであるため、対応する DOM は変更されておらず、挿入される DOM は常に 1 つだけです。このようにして、複数のヘッダーコンポーネントを表示する問題を解決しました。 DOM への挿入操作の繰り返しを避けるために、最適化を行うこともできます。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

const vueInstance = new (Vue.extend(TheHeader))().$mount()
定数hasPrepend = false
定数ヘッダープラグイン = {
    インストール(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = タイトル
            if (!hasPrepend) {
                document.body.prepend(vueInstance.$el)
                先頭に追加 = true
            }
            
        }
    }
}
Vue.use(ヘッダープラグイン)

DOM が挿入されたかどうかを制御する変数を追加します。挿入されている場合は、挿入操作は実行されません。最適化後、このプラグインの実装はほぼ完了です。しかし、実装プロセス中にいくつかの問題に遭遇したので、ここで記録しておきます。

質問2: 別の実装アイデア

実装プロセス中に、突然、TheHeader コンポーネントのデータ関数を直接変更してこのコンポーネントを実装できるのではないかというアイデアが浮かびました。次のコードを見てください。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

el = nullとする
定数ヘッダープラグイン = {
    インストール(Vue) {
        Vue.prototype.$setHeader = function(title) {
            ヘッダー.data = 関数() {
                タイトル
            }
            const vueInstance = new (Vue.extend(TheHeader))().$mount()
            el = vueInstance.$el
            もし(エル){
                document.body.removeChild(el)
                document.body.prepend(el)
            }
            
        }
    }
}
Vue.use(ヘッダープラグイン)

何も問題はないようです。しかし、実際にやってみると、$setHeader メソッドを呼び出すと、渡された最初の値のみが有効になることがわかりました。たとえば、最初の入力が「私のホームページ」で、2 番目の入力が「個人情報」の場合、ヘッダー コンポーネントには常に個人情報ではなく私のホームページが表示されます。理由は何ですか?

Vue のソース コードを詳細に調べた結果、new Vue の最初の呼び出しの後に、Header に Ctor プロパティが追加され、Header コンポーネントに対応するコンストラクターがキャッシュされていることがわかりました。その後 new Vue(TheHeader) が呼び出されると、使用されるコンストラクターは常に最初のキャッシュなので、title の値は変更されません。 Vue ソースコードに対応するコードは次のとおりです。

Vue.extend = 関数 (extendOptions) {
    extendOptions = extendOptions || {};
    var Super = this;
    var SuperId = Super.cid;
    var cachedCtors = extendOptions._Ctor || (extendOptions._Ctor = {}); 
    if (cachedCtors[SuperId]) { // キャッシュがある場合は、キャッシュされたコンストラクタを直接返します return cachedCtors[SuperId]
    }

    var name = extendOptions.name || Super.options.name;
    process.env.NODE_ENV !== 'production' && name の場合 {
      コンポーネント名を検証します(名前);
    }

    var Sub = function VueComponent (オプション) {
      this._init(オプション);
    };
    Sub.prototype = Object.create(Super.prototype);
    Sub.prototype.constructor = Sub;
    Sub.cid = cid++;
    サブオプション = mergeOptions(
      スーパーオプション、
      拡張オプション
    );
    Sub['super'] = スーパー;

    // プロパティと計算プロパティについては、プロキシゲッターを定義します
    // 拡張時のVueインスタンス、拡張プロトタイプ上。これは
    // 作成されたインスタンスごとに Object.defineProperty の呼び出しを回避します。
    if (Sub.options.props) {
      初期化プロパティ$1(Sub);
    }
    if (Sub.options.computed) {
      initComputed$1(Sub);
    }

    // さらなる拡張機能/ミックスイン/プラグインの使用を許可する
    Sub.extend = Super.extend;
    Sub.mixin = Super.mixin;
    Sub.use = Super.use;

    // 資産レジスターを作成するので、拡張クラス
    // プライベートアセットも持つことができます。
    ASSET_TYPES.forEach(関数(型) {
      サブ[タイプ] = スーパー[タイプ];
    });
    // 再帰的な自己検索を有効にする
    if (名前) {
      Sub.options.components[名前] = Sub;
    }

    // 拡張時にスーパー オプションへの参照を保持します。
    // 後でインスタンス化するときにSuperのオプションが
    // 更新されました。
    Sub.superOptions = Super.options;
    Sub.extendOptions = extendOptions;
    Sub.sealedOptions = extend({}, Sub.options);

    // キャッシュコンストラクタ
    cachedCtors[SuperId] = Sub; // Ctorコンストラクタがキャッシュされる場所です。 return Sub
  }

理由がわかったら、この方法も可能であることがわかります。plugin.jsに1行のコードを追加するだけです。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

el = nullとする
定数ヘッダープラグイン = {
    インストール(Vue) {
        Vue.prototype.$setHeader = function(title) {
            ヘッダー.data = 関数() {
                タイトル
            }
            ヘッダー.Ctor = {}
            const vueInstance = 新しい Vue(TheHeader).$mount()
            el = vueInstance.$el
            もし(エル){
                document.body.removeChild(el)
                document.body.prepend(el)
            }
            
        }
    }
}
Vue.use(ヘッダープラグイン)

$setHeader メソッドを実行するたびに、キャッシュされたコンストラクターが削除されます。

質問3: Vue.extend を使用しないことは可能ですか?

実際、Vue.extend を使用せずに Vue を直接使用することも可能です。関連するコードは次のとおりです。

'./TheHeader.vue' から TheHeader をインポートします。
'vue' から Vue をインポートします

const vueInstance = 新しい Vue(TheHeader).$mount()
定数hasPrepend = false
定数ヘッダープラグイン = {
    インストール(Vue) {
        Vue.prototype.$setHeader = function(title) {
            vueInstance.title = タイトル
            if (!hasPrepend) {
                document.body.prepend(vueInstance.$el)
                先頭に追加 = true
            }
            
        }
    }
}
Vue.use(ヘッダープラグイン)

Vue を直接使用してインスタンスを作成する方が、Extend を使用してインスタンスを作成するよりも良い方法です。これは、Header.vue の Ctor プロパティがキャッシュされないためです。しかし、以前、Vant が Toast コンポーネントを実装しているのを見たことがあります。これは基本的に、Vue を直接使用するのではなく、Vue.extend メソッドを使用します。なぜでしょうか?

要約する

Vue プラグインの実装で発生した問題についての記事はこれで終わりです。Vue プラグインの実装に関するより関連性の高い問題については、123WORDPRESS.COM の過去の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vue プラグインの作成と使用の詳細な説明 (デモ付き)
  • Vue右クリックメニュープラグインはシンプルで拡張可能、カスタマイズ可能な右クリックメニューです
  • Vue カスタム グローバル コンポーネント (カスタム プラグイン) の使用
  • Vue3.0はプラグインのカプセル化を実装
  • Vue.jsプラグイン開発の詳細な説明
  • Vueプロジェクトにプラグインをインストールして保存します

<<:  Win Server 2019 サーバーの IIS 構成と Web サイトの簡単な公開

>>:  MySQL で置換操作を使用したときにデータ損失が発生する問題の解決策

推薦する

WeChatアプレットの下部にあるタブバーがコンテンツをブロックする問題に対処する簡単な方法

WeChatアプレットでタブバーを設定すると、重要なコンテンツがブロックされ、iPhoneXなどの異...

Linux で scp コマンドを使用してファイルをリモートでコピーする方法の詳細な説明

序文scp は secure copy の略です。scp は、Linux システムの ssh ログイ...

docker で nginx+php+mysql を設定する方法

まず、方法を理解します。 docker exec を使用して Docker コンテナに入るDocke...

MySQL の FIND_IN_SET() と IN の違いを簡単に分析します

以前、あるプロジェクトでMysql FIND_IN_SET関数を使用したことがありますが、非常に便利...

MySql 学習ノートにおけるトランザクション分離レベルの詳細な説明

背景トランザクションについて話すとき、誰もがそれに精通している必要があります。MySQL データベー...

JavaScript配列の一般的なメソッドの概要

目次1. はじめに2. フィルター() 3. マップ() 4. ソート() 5. 減らす() 6. ...

k8s に ingress-nginx をデプロイする手順

目次序文1. Ingressの展開と構成2. httpsを使用する序文k8sクラスタサービスがデプロ...

HTML での一般的なリダイレクト接続の例コード

コードをコピーコードは次のとおりです。 window.location.href="zcb...

Python スクリプトを Ubuntu で直接実行する方法

翻訳プログラムを例に挙げてみます。前回はWindowsでのアプリケーションのパッケージ化についてお話...

JavaScript DOM オブジェクト操作

目次1. コア1. Domノードを取得する2. ノードの更新2.1 実践演習3. Domノードを削除...

シンプルで簡単なJavaScript開発のためのSvelte実装原理の詳細な説明

目次デモ1フラグメントの作成スヴェルトコンポーネント状態を変更できるデモSvelte は長い間存在し...

Chrome 4.0 は GreaseMonkey スクリプトをサポートします

GreaseMokey (中国語では Grease Monkey Script と呼んでいます) は...

LeetCode の SQL 実装 (182. 重複するメールボックス)

[LeetCode] 182.重複メールPerson という名前のテーブル内のすべての重複メールを...

HTML ページをスクロールするときに一部のコンテンツを固定位置に固定する方法

この記事では主に、レイアウトに役立つ、HTML ページ内の一部のコンテンツを固定してスクロール時にス...

Linux touch コマンドの使用例

Linux touch コマンドの詳細な説明: 1. コマンド機能:ファイルまたはディレクトリの作成...