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 で置換操作を使用したときにデータ損失が発生する問題の解決策

推薦する

MySQL 並列レプリケーションの簡単な分析

01 並列レプリケーションの概念MySQL のマスター スレーブ レプリケーション アーキテクチャで...

Linux のソフトリンクとハードリンクの詳細な説明

目次1. ファイルとディレクトリの基本的な保存2. Inコマンドの紹介(1)lnコマンドの基本情報を...

win10 mysql 5.6.35 winx64 無料インストールバージョン設定チュートリアル

mysql 5.6.35 winx64無料インストールバージョン構成チュートリアルwin10、具体的...

Linux で特定のユーザーまたはユーザー グループに対して SSH を有効または無効にする方法

会社の基準により、特定のユーザーだけに Linux システムへのアクセスを許可することができます。あ...

Mysql 主キー UUID と自動増分主キーの違いと利点と欠点

導入私はしばらくの間、postgresql データベースを使用していました。クラウドに移行した後、自...

HTMLとリソースがどのように読み込まれるかを理解します

このブログのすべてのコンテンツは、クリエイティブ コモンズ ライセンスの下でライセンスされています。...

mysql 8.0.20 winx64.zip 圧縮版のインストールと設定方法のグラフィックチュートリアル

mysql 8.0.20 winx64.zip圧縮版のインストールチュートリアルは以下のように記録さ...

Linux運用保守ツールSupervisor(プロセス管理ツール)のインストールと使用

1. はじめにSupervisor は Python で開発された汎用プロセス管理プログラムです。通...

ES9の新機能の詳細な説明: 非同期反復

目次非同期トラバーサル非同期反復可能トラバーサル非同期反復生成非同期メソッドと非同期ジェネレーター非...

ウェブページの再設計の7つの主要要素 ウェブページの再設計の7つの主要要素を共有する

Shopify Plus は、私たちが設立した e コマース プラットフォームのエンタープライズ バ...

WeChatアプレットコンポーネントライフサイクルの落とし穴の記録

通常、コンポーネントのライフサイクルは、ビジネス ロジックが始まる場所です。ビジネスシナリオが複雑で...

MySQL 5.7.13 winx64 のインストールと設定方法のグラフィック チュートリアル (win10)

この記事では、参考までにMySQL 5.7.13 winx64のインストールと設定方法のグラフィック...

マークアップ言語 - テキストの CSS スタイルを指定する

123WORDPRESS.COM HTML チュートリアル セクションに戻るには、ここをクリックして...

Win10+Ubuntu 20.04 LTS デュアル システム インストール (UEFI + GPT) (画像とテキスト、複数の画像には注意)

Win10 のインストール (すでにインストールされている場合はスキップしてください) win10...