Vueでキャッシュされたページを管理する方法

Vueでキャッシュされたページを管理する方法
 <キープアライブ>
 <ルータービュー />
</キープアライブ>

Vue に組み込まれている <keep-alive> コンポーネントは、すべてのルート ページをキャッシュすることで (もちろん、一部のページをターゲットにしてキャッシュすることもできます)、SPA アプリケーションを開発するときにセカンダリ ページ アクセスの速度を大幅に向上させるのに役立ちますが、いくつかのシナリオでは、2 つの主な矛盾を含む問題も発生します。

  1. キャッシュされたページを適切なタイミングで破棄する方法 (キープアライブ コンポーネントは、キャッシュの状態を動的に構成するための 3 つのパラメーターを提供しますが、効果は限られているため、後で分析します)
  2. 同じパスで複数の異なるページ(異なるパラメータを持つ同じページ)をキャッシュする方法(Taobaoの商品ページが別の商品ページにジャンプし続けるなど)

この記事では主にこれら 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. いつ破壊するか
  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 つあります。

  1. 置換する場合、ページ A --replace--> ページ B (ページ A をクリア)
  2. ルーティングバックする場合、ページ A --push--> ページ B --back--> ページ A (ページ B をクリア)

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. ユニークになる方法
  2. ページのvnodeにキーを割り当てる方法

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 の他の関連記事に注目してください。

以下もご興味があるかもしれません:
  • Vue キープアライブが $destory() を呼び出すとページがキャッシュされなくなる問題を解決します
  • VueはlocalStorageローカルキャッシュを使用して、検証コードをクリアせずに更新する機能を実装します。
  • ページキャッシュ操作を実現するためのVueページジャンプ
  • Vue シングルページフォールバックページの keeplive キャッシュ問題を解決する
  • キャッシュ ページ効果を実現するために vue-router で keep-Alive を実行するサンプル コード
  • Vue2.0 でページキャッシュと非キャッシュを実装する方法
  • Vue プロジェクトでページ キャッシュを強制的にクリアする例
  • Vueタブとキャッシュページを切り替えるいくつかの方法
  • Vueのページキャッシュの詳細な説明
  • Vue キャッシュでキープアライブ ページのデータをリフレッシュする方法

<<:  Linux での i3 ウィンドウ マネージャーの設定と使用に関するチュートリアル

>>:  MySQL は、ユーザーの作成、ユーザーの承認、ユーザー権限の取り消し、ユーザー パスワードの変更、およびユーザーの削除を行います (実用的なヒント)

推薦する

Vue のローカルコンポーネントの紹介

Vueでは、ローカルコンポーネントを自分で定義(登録)することができます。コンポーネント名を定義する...

高い同時実行性の下でNginxのパフォーマンスを最適化する方法をまとめます

目次特徴利点インストールとコマンド設定ファイルプロキシモードとリバースプロキシ構成フォワードプロキシ...

Linux zabbix エージェントの展開と設定方法の詳細な説明

1. web01にzabbix-agentをインストールするZabbix ウェアハウスをデプロイする...

Docker共通コマンドの詳しい解説 Study03

目次1. ヘルプコマンド2. ミラーコマンド3. コンテナコマンド1. ヘルプコマンド1. 現在のD...

jsはCanvasを使用して複数の画像を1つの実装コードにマージします

解決 関数 mergeImgs(リスト) { const imgDom = document.cre...

要素の属性を削除する JS removeAttribute() メソッド

JavaScript では、要素の removeAttribute() メソッドを使用して、指定され...

CSS3 を使用して 3D テキスト ホバー効果を実装するサンプル コード

この記事では、3D テキストのホバー変更効果を実現するための CSS3 のサンプル コードを紹介しま...

49 個の JavaScript のヒントとコツ

目次1. js整数の演算2. ネイティブアラートを書き換えてポップアップボックスの数を記録する3. ...

Dockerを使用してOracle_11gをインストールする方法

DockerでOracle_11gをインストールする1. oracle_11gイメージを取得する d...

protobuf の簡単な紹介と Ubuntu 16.04 環境でのインストールチュートリアル

protobufの簡単な紹介Protobuf は、Google のオープンソースのシリアル化プロトコ...

Vue フロントエンド開発における keepAlive の使用方法の詳細な説明

目次序文keep-avlive フック関数keep-avliveはどのコンポーネントをキャッシュする...

MySql でメモリ使用量を削減する方法の詳細な説明

序文デフォルトでは、MySQL はデータベース クエリ データをキャッシュするために大きなメモリ ブ...

MySQL 8.0.20 のインストールと設定方法のグラフィックチュートリアル

MySQLのダウンロードとインストール(バージョン8.0.20)のチュートリアルは参考までに、具体的...

vue-seamless-scrollがスクロールしていいねをするときのデータ同期の問題を解決する

VUE は vue-seamless-scroll を使用して、自動的にスクロールしていいねします。...

MySQL 8.0 redo ログの詳細な分析

目次序文REDOログの生成REDOログ送信REDOログの保存と通知ユーザースレッドに通知要約する序文...