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

推薦する

CSS3 ベジェ曲線の例: リンクホバーアニメーション効果の作成

CSS3 アニメーション トランジションを使用して、リンクの上にマウスを移動すると小さなポップアップ...

ubuntu16.04 で nginx を完全にアンインストールするための関連コマンド

nginx の概要nginx は、無料のオープンソースの高性能 HTTP サーバーおよびリバース プ...

Dockerコンテナでは、イメージを簡素化してサイズを縮小する方法を詳しく説明しています

目次1.画像レイヤーの数を減らす1. 命令の統合2. 多段階ビルド3. スクワッシュ機能を有効にする...

Dockerのセキュリティについて Docker-TLS暗号化通信の問題

目次1. Dockerのセキュリティ問題2. Dockerアーキテクチャの欠陥とセキュリティメカニズ...

MySQL 全文あいまい検索 MATCH AGAINST メソッドの例

MySQL 4.x 以降では、全文検索 MATCH ... AGAINST モード (大文字と小文字...

MySql 8.0.11 のインストール プロセスと Navicat とのリンク時に発生する問題の概要

私のシステムとソフトウェアのバージョンは次のとおりです。システム環境: win7、64ビットMySQ...

MySQL は、現在のデータ テーブル内のすべての時間に対して指定された時間間隔を増加または減少させます (推奨)

DATE_ADD() 関数は、指定された時間間隔を日付に追加します。現在のテーブル内のすべてのデー...

dockerfile における ENTRYPOINT と CMD の組み合わせと違い

前回の記事【dockerコンテナのためのdockerfileを詳しく解説】では、dockerfile...

JavaScript で最も高速なループはどれですか?

どの for ループまたは反復子がニーズに適しているかを知ることで、アプリケーションのパフォーマンス...

私のCSSアーキテクチャのコンセプト - それは人によって異なり、ベストなものはなく、適切なものだけがある

はじめに<br />私はフロントエンド分野でかなり長い間働いており、CSS分野でも長い間...

type="file" の入力ボックスのスタイル変更の概要

入力タイプ「file」とは何ですか?これが何なのかは説明する必要はないと思います。誰もが知っているこ...

Linux と最もよく使用されるコマンドの紹介 (習得は簡単ですが、問題の 95% 以上を解決できます)

Linux は現在最も広く使用されているサーバー オペレーティング システムです。Unix をベー...

Vue で Axios 非同期リクエスト API を使用する方法

目次基本的なHTTPリクエストの設定async/await を使用した Axios Axios によ...

CSS での配置の使用方法の詳細な研究 (要約)

CSS における位置指定の概要position属性は英語で位置を意味し、 CSSでの主な機能は要素...

Mysql でサーバーの UUID を変更する方法

問題の原因:スレーブサーバーがクローンマスターサーバーである場合、server-uuidの値は同じで...