<キープアライブ> <ルータービュー /> </キープアライブ> Vue に組み込まれている <keep-alive> コンポーネントは、すべてのルート ページをキャッシュすることで (もちろん、一部のページをターゲットにしてキャッシュすることもできます)、SPA アプリケーションを開発するときにセカンダリ ページ アクセスの速度を大幅に向上させるのに役立ちますが、いくつかのシナリオでは、2 つの主な矛盾を含む問題も発生します。
この記事では主にこれら 2 つの問題について議論します。これらの問題は、以下のテキストでは質問 1 と質問 2 と呼ばれます。
問題1: 破壊ビジネス ロジックが複雑になるにつれて、ルーティング スタックは徐々に増加します。理論的には、ユーザーは無限にルーティングできます。必然的に、メモリにキャッシュされたページ データを管理する必要があります。ページ データは、Vue インスタンスと対応する Vnode の 2 つの部分で構成されます。 Vueソースコードのsrc/core/components/keep-alive.jsのcacheの定義を参照してください。 this.cache = Object.create(null) //vnodeをキャッシュするために使用 cache[key] => Vnode this.keys = [] //キャッシュされたvnodeのキーを記録するために使用 キャッシュ後、Vnode は再利用されず、そこにマウントされた Vue インスタンスのみが使用されます。 if (cache[キー]) { vnode.componentInstance = cache[key].componentInstance // キャッシュされた vnode からのみ Vue インスタンスを取得し、新しい vnode に掛ける // 現在のキーを最新のものにする 削除(キー、キー) keys.push(キー) } なぜそうではないのでしょうか? それはバグがあるからです。実装の最も古いバージョンでは、キャッシュされた Vnode を直接使用します。 src/core/components/keep-alive.js の init バージョンから エクスポートデフォルト{ 作成された(){ this.cache = Object.create(null) }, 与える () { const childNode = this.$slots.default[0] 定数 cid = childNode.componentOptions.Ctor.cid if (this.cache[cid]) { const child = childNode.child = this.cache[cid].child //キャッシュされたvnodeを直接取得する childNode.elm = this.$el = child.$el } それ以外 { this.cache[cid] = 子ノード } childNode.data.keepAlive = true 子ノードを返す }, 破棄前() { for (const キー in this.cache) { this.cache[キー].child.$destroy() } } } 実際に管理する必要があるのは、キャッシュとキーです。Keep-alive は、キャッシュを動的に管理するための 3 つのパラメータを提供します。 include - 名前が一致するコンポーネントのみがキャッシュされます。 exclude - 名前に一致するコンポーネントはキャッシュされません。 max - キャッシュできるコンポーネントインスタンスの最大数。 機能は非常にシンプルで、ソースコードもシンプルで読みやすいです。 したがって、これらのキャッシュを管理する場合、簡単な解決策は、これらの 3 つのパラメータを操作し、include と exclude を変更して特定のキャッシュをキャッシュまたはクリアすることですが、これらはコンポーネントの名前と一致することに注意する必要があります。 src/core/components/keep-alive.js から 定数名: ?string = getComponentName(componentOptions) したがって、キャッシュをクリアすると、コンポーネントのすべてのインスタンスが無差別にクリアされるため、明らかにニーズを満たしません。 max のロジックは、最大値を超えたときにスタックの一番下にあるキャッシュをクリアすることです。 src/core/components/keep-alive.js から: this.max && keys.length > parseInt(this.max) の場合 { キャッシュエントリを削除します(キャッシュ、キー[0]、キー、this._vnode) } 問題 1 を解決する必要があります。公式 API は機能しないため、自分で解決する必要があります。2 つのサブ問題を解決する必要があります。
1. 破壊する方法まずは破棄方法を見てみましょう。インスタンスを破棄したい場合、それは非常に簡単です。直接 this.$destroy() を使用できます。これでいいのでしょうか?いいえ、元の vnode とキーはキャッシュとキーに保持されたままです。再度アクセスすると問題が発生します。vnode は常に保持されますが、その上のインスタンスは破棄されています。このとき、vue の更新プロセス中に vue インスタンスが再度作成されます。つまり、keep-alive ページが this.$destroy() を一度呼び出しても、キャッシュ配列がクリアされない限り、このページは再レンダリング時に必ず再作成され、もちろんライフサイクル全体が再開されます。最終的な現象は、ページがキャッシュされていないように見えることです。 this.$destroy(); //キープアライブコンポーネントには適していません したがって、破棄にはキャッシュとキーの両方をクリアする必要があります。以下は、キャッシュを同時にクリアする $keepAliveDestroy メソッドを定義します。 const dtmp = Vue.prototype.$destroy; 定数f = 関数() { (this.$vnode && this.$vnode.data.keepAlive) の場合 { (this.$vnode.parent && this.$vnode.parent.componentInstance && this.$vnode.parent.componentInstance.cache) の場合 { if (this.$vnode.componentOptions) { var キー = !isDef(this.$vnode.key) ? this.$vnode.componentOptions.Ctor.cid + (this.$vnode.componentOptions.tag ? `::${this.$vnode.componentOptions.tag}` : '') : this.$vnode.key; var キャッシュ = this.$vnode.parent.componentInstance.cache; var キー = this.$vnode.parent.componentInstance.keys; if (cache[キー]) { if (キーの長さ) { var インデックス = keys.indexOf(キー); (インデックス>-1)の場合{ キーを連結します(インデックス、1); } } キャッシュ[キー]を削除します。 } } } } dtmp.apply(これ、引数); } Vue.prototype.$keepAliveDestroy = f; 2. いつ破壊するかでは、いつ破壊されるのでしょうか? 破壊されるタイミングは 2 つあります。
replace は比較的簡単です。ルーターの replace メソッドを直接インターセプトし、このメソッドで現在のページをクリアすることができます。 (タブを切り替える場合など例外もありますが、これは最後に説明します) ルートバックの状況を詳しく見てみましょう。ページに戻るボタンがある場合は、キャッシュをクリアする適切なタイミングです。ただし、ブラウザに組み込まれている戻るボタンと Android フォンの物理的な戻るボタンを無視することはできません。これを考慮すると、戻るボタンのみを使用する解決策では不十分です。 2.1 解決策1: route.queryを使用して現在のページスタックの深さを記録する各プッシュまたは置換では、現在の深度を記録するためのパラメータがクエリに追加されます。 this.$router.push({ パス:"/target", クエリ:{ スタックレベル: 数値(this.$route.query.stackLevel) + 1 } }) このソリューションには明らかな欠点があります。パラメータを外部に公開するのは非常に見苦しく危険です。ユーザーはそれを自由に変更できます。Web サイトを宣伝する場合、企業が本番環境にコピーしたプロモーション リンクに、https://xxx.com/foo?bar=123&stackLevel=13 という奇妙なサフィックスが付くこともあります。廃止 2.2 ソリューション2は、Vueインスタンス自体を使用して現在のスタックの深さを記録します。ルーターのプッシュおよび置換メソッドをハッキングすると、ページがジャンプするたびに、_stackLevel をターゲット ページの vm にマウントできます。これにより、ソリューション 1 の問題が解決されます。これはユーザーに公開されず、URL にも表示されず、変更できません。ただし、ブラウザーの別の悪魔である更新ボタンを無視することはできません。更新すると、URL は変更されませんが、vm インスタンスを再作成する必要があるため、スタック深度マークは失われます。廃止 2.3 解決策3: history.stateを使用してスタックの深さを記録するすると、最終結果はユーザーには見えなくなり、更新時に保存できるようになります。これは history.state なので、スタックの深さを history.state に保存する必要があります。これにより、ルーティング チェーン全体を完全に保存できます。 ターゲット ページのスタック深度が現在のページよりも小さいことがわかった場合、現在のページを破棄できます。 (ターゲットスタック<現在のスタック){ 現在の$keepAliveDestroy(); } 問題2: 異なるパラメータを持つ同じページの複数のインスタンスをキャッシュするソースコード内のsrc/core/components/keep-alive.jsを参照できます。 定数キー: ?string = vnode.key == null // 同じコンストラクタが異なるローカルコンポーネントとして登録される可能性がある // cid だけでは不十分です (#3269) ? componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '') : vnode.キー if (cache[キー]) { vnode.componentInstance = キャッシュ[キー].componentInstance // 現在のキーを最新のものにする 削除(キー、キー) keys.push(キー) } それ以外 { キャッシュ[キー] = vnode keys.push(キー) // 最も古いエントリを削除する this.max && keys.length > parseInt(this.max) の場合 { キャッシュエントリを削除します(キャッシュ、キー[0]、キー、this._vnode) } } vnode にキーがない場合は、コンポーネント名が使用されます。したがって、デフォルトのキャッシュのキーはコンポーネント名です。コンポーネントが同じ場合は、各ページに独自のキーを与えることでこの問題を解決できます。各ページに独自のキーを持たせるにはどうすればよいでしょうか。 2 つのサブ質問があります:
1. ユニークになる方法1.1 タイムスタンプ、大きな乱数キー = Date.now() 1.2 ルーティングスタックの高さ + パス名key = vm._stack + router.currentRoute.path このソリューションでは、現在のスタックの高さ + パス名を使用します。パス名が必要なのはなぜでしょうか? 置き換えてもスタックの高さは変わらないため、パス名のみが変更されます。 2. ページのvnodeにキーを割り当てる方法現在、vue-router の現在の Vnode のキーに値を割り当てるには、次の 2 つの解決策があります。 2.1 route.query 経由でキーを動的にバインドするこのソリューションは実装が比較的簡単です // キーをバインド ... <router-view :key='$route.query.routerKey' /> ... //これをプッシュするとき。$router.push({ パス:"/foo", クエリ:{ routerKey: Date.now() //ランダムキー } }) この方法は非常にシンプルで効果的ですが、URLに奇妙なパラメータも公開されるという欠点があります。 2.2 Vnodeを取得して値を直接割り当てるVnodeのキーはどの段階で割り当てられるのでしょうか?答えは明らかです。キープアライブコンポーネントのレンダリング関数が入る前に、src/core/components/keep-alive.js ... 与える () { const スロット = this.$slots.default 定数 vnode: VNode = getFirstComponentChild(スロット) ... キープアライブ レンダリング関数をハックして、スロット内の最初の子ノードを取得し、そのキーに値を割り当ててから、キープアライブ レンダリングを呼び出すことができます。 const tmp = vm.$options.render //vmはキープアライブコンポーネントインスタンスです vm.$options.render = 関数() { const スロット = this.$slots.default; const vnode = getFirstComponentChild(slot) // vnode はキープアライブコンポーネント vnode です if (履歴が変更されるべき) { if (!isDef(vnode.key)) { if (isReplace) { vnode.key = genKey(router._stack) } それ以外の場合は (isPush()) { vnode.key = genKey(数値(router._stack) + 1) } それ以外 { vnode.key = genKey(数値(router._stack) - 1) } } } それ以外 { // historyShouldChange が false の場合は再レンダリングのみを行い、新しい VM は作成せず、同じ vnode.key を使用します (問題 #7) vnode.key = genKey(router._stack) } tmp.apply(this, 引数) を返す } 要約する上記の問題分析を通じて、自動キャッシュ管理の核心的な問題を解決しました。この記事は、オープンソース ライブラリ vue-router-keep-alive-helper の概要です。このライブラリは、シンプルで使いやすいキープアライブ キャッシュ自動化管理ツールであり、Vue キャッシュ管理の問題に別れを告げます。もしお役に立てましたら、ぜひスターをつけていただければ幸いです。 デモサンプルコード Bilibiliのデモビデオをありがとうございます。 上記は、Vue でキャッシュされたページを管理する方法の詳細です。Vue のキャッシュされたページの詳細については、123WORDPRESS.COM の他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: Linux での i3 ウィンドウ マネージャーの設定と使用に関するチュートリアル
>>: MySQL は、ユーザーの作成、ユーザーの承認、ユーザー権限の取り消し、ユーザー パスワードの変更、およびユーザーの削除を行います (実用的なヒント)
Vueでは、ローカルコンポーネントを自分で定義(登録)することができます。コンポーネント名を定義する...
目次特徴利点インストールとコマンド設定ファイルプロキシモードとリバースプロキシ構成フォワードプロキシ...
1. web01にzabbix-agentをインストールするZabbix ウェアハウスをデプロイする...
目次1. ヘルプコマンド2. ミラーコマンド3. コンテナコマンド1. ヘルプコマンド1. 現在のD...
解決 関数 mergeImgs(リスト) { const imgDom = document.cre...
JavaScript では、要素の removeAttribute() メソッドを使用して、指定され...
この記事では、3D テキストのホバー変更効果を実現するための CSS3 のサンプル コードを紹介しま...
目次1. js整数の演算2. ネイティブアラートを書き換えてポップアップボックスの数を記録する3. ...
DockerでOracle_11gをインストールする1. oracle_11gイメージを取得する d...
protobufの簡単な紹介Protobuf は、Google のオープンソースのシリアル化プロトコ...
目次序文keep-avlive フック関数keep-avliveはどのコンポーネントをキャッシュする...
序文デフォルトでは、MySQL はデータベース クエリ データをキャッシュするために大きなメモリ ブ...
MySQLのダウンロードとインストール(バージョン8.0.20)のチュートリアルは参考までに、具体的...
VUE は vue-seamless-scroll を使用して、自動的にスクロールしていいねします。...
目次序文REDOログの生成REDOログ送信REDOログの保存と通知ユーザースレッドに通知要約する序文...