ソースコードの観点からキープアライブコンポーネントのキャッシュ原理に答える

ソースコードの観点からキープアライブコンポーネントのキャッシュ原理に答える

今日は、早速本題に入り、面接中に尋ねられた質問、つまりキープアライブ コンポーネントのキャッシュ原理についてお話ししましょう。

公式APIの紹介と使用方法

  • <keep-alive> 動的コンポーネントをラップする場合、非アクティブなコンポーネント インスタンスは破棄されるのではなくキャッシュされます。
  • <transition> と同様に、<keep-alive> は抽象コンポーネントです。DOM 要素自体はレンダリングされず、コンポーネントの親コンポーネント チェーンにも表示されません。
  • <keep-alive> でコンポーネントが切り替えられると、それに応じてアクティブ化および非アクティブ化されたライフサイクル フック関数が実行されます。

公式サイトの例では、タブ切り替えによってユーザーの操作が保存されています。実際には、一覧ページから詳細ページにジャンプし、一覧ページに戻るという状況にも遭遇する可能性があります。ユーザーのフィルタリング操作を保存する必要があり、そのためには <keep-alive> を使用する必要があります。これにより、再レンダリングを回避し、ページのパフォーマンスを向上させることもできます。

使用方法と小道具の説明

// 動的コンポーネントでのキープアライブコンポーネントの使用。その他の使用方法については、公式ウェブサイト <keep-alive
 include="['コンポーネント名A', 'コンポーネント名B']"
 除外="'コンポーネント名C'"
 :max="10">
 <コンポーネント :is="view"></コンポーネント>
</キープアライブ>

  • include - 文字列または正規表現または配列、名前に一致するコンポーネントがキャッシュされます
  • exclude - 文字列または正規表現または配列。名前に一致するコンポーネントはキャッシュされません。
  • max - 文字列または数値、キャッシュされたコンポーネント インスタンスの最大数。最も長い時間アクセスされていないインスタンスは破棄されます。

知らせ:

  • <keep-alive> は直行の 1 つのコンポーネントのみをレンダリングするため、<keep-alive> 内で v-for を使用すると機能しません。条件を満たす条件が複数ある場合も同様です。
  • include と exclude が一致する場合、最初にコンポーネントの名前オプションがチェックされます。名前オプションが使用できない場合は、ローカルに登録された名前 (つまり、親コンポーネントの components オプションのキー値) が一致します。匿名コンポーネントは一致できません。
  • <keep-alive> はインスタンスをキャッシュしないため、機能コンポーネントでは正しく動作しません。

ソースコードの解釈

まずソースコードの画像を投稿する

全部で125列ありますが、片付けてみるとそんなにたくさんのものは入っていません。最初に、必要なメソッドをいくつか紹介し、次にキープアライブコンポーネント自体が使用するメソッドをいくつか定義します。最後に、keep-alive というコンポーネントオプションが外部に公開されます。abstract を除いて、これらすべてのオプションはよく知られています。その中でも、レンダリング関数はキャッシュ原則の最も重要な部分です。また、キープアライブコンポーネントは機能コンポーネントであることがわかります。

// isRegExp 関数は正規表現かどうかを判定し、remove は配列内のメンバーを削除します。// getFirstComponentChild は VNode 配列の最初の有効なコンポーネントを取得します。import { isRegExp, remove } from 'shared/util'
'core/vdom/helpers/index' から { getFirstComponentChild } をインポートします。
​
type VNodeCache = { [key: string]: ?VNode }; // キャッシュコンポーネントのキャッシュタイプ VNode​
// コンポーネント名またはコンポーネントタグでコンポーネント名を取得します (上記の 2 番目のポイント)
関数 getComponentName (opts: ?VNodeComponentOptions): ?string {
 opts && (opts.Ctor.options.name || opts.tag) を返します
}
​
// include または exclude がコンポーネント名に一致するかどうかを判定します。関数 matches (pattern: string | RegExp | Array<string>, name: string): boolean {
 if (Array.isArray(パターン)) {
 return pattern.indexOf(name) > -1 // include または exclude は配列です} else if (typeof pattern === 'string') {
 return pattern.split(',').indexOf(name) > -1 // include または exclude は文字列です } else if (isRegExp(pattern)) {
 return pattern.test(name) // include または exclude は正規表現です}
 false を返します // いずれも一致しません (上記の 2 番目と 3 番目の点に注意してください)
}
​
// キャッシュを破棄する function pruneCache (keepAliveInstance: any, filter: Function) {
 const { cache, keys, _vnode } = keepAliveInstance // キープアライブコンポーネントインスタンス for (const key in cache) {
 const cachedNode: ?VNode = cache[key] // キャッシュされたコンポーネント if (cachedNode) {
  定数名: ?string = getComponentName(cachedNode.componentOptions)
  // 名前が存在し、include または exclude に一致しない場合は、キャッシュされたコンポーネントを破棄します if (name && !filter(name)) {
  pruneCacheEntry(キャッシュ、キー、キー、_vnode)
  }
 }
 }
}
​
//キャッシュエントリを破棄する関数pruneCacheEntry(
 キャッシュ: VNodeCache、
 キー: 文字列、
 キー: 配列<文字列>,
 現在の?: VNode
){
 const cached = cache[key] // キャッシュされたコンポーネント // 変更があった場合に「コンポーネントのキャッシュを継続するかどうか」 // コンポーネントがキャッシュされていて、現在のコンポーネントが存在しないか、キャッシュされたコンポーネントのタグが現在のコンポーネントのタグと等しくない場合 if (cached && (!current || cached.tag !== current.tag)) {
 // これは、コンポーネントをキャッシュする必要がなくなったことを意味します。コンポーネントインスタンスを破棄します。cached.componentInstance.$destroy()
 }
 cache[key] = null // キャッシュ内のこのコンポーネントをnullに設定する
 remove(keys, key) // このコンポーネントのキーをキー配列から削除します}
​
// 例 type const patternTypes: Array<Function> = [String, RegExp, Array]
​
// キープアライブコンポーネントのいくつかのオプションを公開する export default {
 name: 'keep-alive', // コンポーネント名 abstract: true, // keep-alive は抽象コンポーネントです​
 // キープアライブコンポーネントを使用するときに渡される3つのプロパティ
 小道具: {
 含まれるもの: パターンタイプ、
 除外: パターンタイプ、
 最大: [文字列、数値]
 },
​
 作成された(){
 this.cache = Object.create(null) // キャッシュする必要があるコンポーネントを保存します this.keys = [] // キャッシュする必要がある各コンポーネントのキー、つまり this.cache オブジェクトに対応するキー値を保存します},
​
 // キープアライブコンポーネントを破棄するときは、キャッシュ内の各コンポーネントを破棄します。destroyed() {
 for (const キー in this.cache) {
  キャッシュエントリを削除します(this.cache、キー、this.keys)
 }
 },
​
 // キープアライブコンポーネントがマウントされると、include と exclude の変更を監視し、条件が満たされるとキャッシュされたコンポーネントを破棄します。mounted() {
 this.$watch('include', val => {
  pruneCache(this、name => matches(val、name))
 })
 this.$watch('exclude', val => {
  pruneCache(this、name => !matches(val、name))
 })
 },
​
 // ポイントはここです render () {
 const slot = this.$slots.default // キープアライブコンポーネントのデフォルトスロット const vnode: VNode = getFirstComponentChild(slot) // デフォルトスロットの最初の有効なコンポーネントを取得します // vnode が存在する場合は、vnode のオプションを取得します const componentOptions: ?VNodeComponentOptions = vnode && vnode.componentOptions
 if (コンポーネントオプション) {
  //最初の有効なコンポーネントの名前を取得します
  定数名: ?string = getComponentName(componentOptions)
  const { include, exclude } = this // props によって渡される include と exclude
  もし (
  // include が存在し、name が存在しないか、name が一致しない場合 (include && (!name || !matches(include, name))) ||
  // exclude が存在し、name が存在するか、name が一致する場合 (exclude && name && matches(exclude, name))
  ){
  return vnode // キャッシュは不要であり、このコンポーネントはレンダリングのために直接返されることを示します}
  
  // 一致した場合はキャッシュ操作が必要です const { cache, keys } = this // キープアライブコンポーネントのキャッシュコンポーネントと、キャッシュコンポーネントに対応するキー
  // 最初の有効なコンポーネントのキーを取得します
  定数キー: ?string = vnode.key == null
  // 同じコンストラクターを異なるローカル コンポーネントとして登録できます // cid だけでは不十分なので、連結してみましょう。componentOptions.Ctor.cid + (componentOptions.tag ? `::${componentOptions.tag}` : '')
  : vnode.キー
  // このコンポーネントがキャッシュにヒットした場合 if (cache[key]) {
  // このコンポーネントインスタンスはキャッシュ内のコンポーネントインスタンスに置き換えられます vnode.componentInstance = cache[key].componentInstance
  // keysremove(keys, key) 内の現在のキーの位置を更新します // keys から現在のキーを削除しますkeys.push(key) // それを keys の末尾に配置します} else {
  // キャッシュにヒットしない場合は、このコンポーネントをキャッシュに追加します。cache[key] = vnode
  keys.push(key) // このコンポーネントのキーをキーの末尾に配置します // キャッシュ内のコンポーネントの数が渡された最大値を超える場合、キャッシュ内の LRU コンポーネントを破棄します if (this.max && keys.length > parseInt(this.max)) {
   キャッシュエントリを削除します(キャッシュ、キー[0]、キー、this._vnode)
  }
  }
​
  vnode.data.keepAlive = true // このコンポーネントのkeepAliveプロパティをtrueに設定します
 }
 // 最初の有効なコンポーネントが存在するが、そのcomponentOptionsが存在しない場合は、レンダリングのためにこのコンポーネントを返します。 // または、有効な最初のコンポーネントが存在しないが、keep-aliveコンポーネントのデフォルトスロットが存在する場合は、レンダリングのためにデフォルトスロットの最初のコンポーネントを返します。 return vnode || (slot && slot[0])
 }
}

補充:

最初の古いキャッシュ コンポーネントを削除し、キャッシュ コンポーネント キーを更新する上記の順序では、実際には LRU キャッシュ削除戦略が使用されます。
LRU は Least Recently Used の略で、最も最近使われていないものを意味するメモリ管理アルゴリズムです。
このアルゴリズムは、「長期間使用されていないデータは将来も使用される可能性が低い」という仮定に基づいています。したがって、データが占有するメモリが特定のしきい値に達すると、最も最近使用されていないデータが削除されます。

要約する

簡単に要約すると次のようになります。

キープアライブコンポーネントがレンダリングされるとき、渡された include と exclude に従って、キープアライブでラップされた名前付きコンポーネントと一致します。一致しない場合は、レンダリングのために名前付きコンポーネントを直接返します。一致した場合は、キャッシュ操作を実行します。コンポーネントがすでにキャッシュに存在する場合は、そのインスタンスが置き換えられ、keys 内のコンポーネントのキーの位置が更新されます。コンポーネントがキャッシュに存在しない場合は、コンポーネントがキープアライブコンポーネントのキャッシュに配置され、keys にコンポーネントのキーが配置されます。include と exclude はマウント時に監視されるため、後でこの 2 つの属性の値が変化すると、条件が満たされているかどうかが再度判断され、コンポーネントが破棄されます。

これで、ソース コードの観点からキープアライブ コンポーネントのキャッシュの原理に答えるこの記事は終了です。キープアライブ コンポーネントのキャッシュに関するより関連性の高いコンテンツについては、123WORDPRESS.COM の以前の記事を検索するか、以下の関連記事を引き続き参照してください。今後とも 123WORDPRESS.COM をよろしくお願いいたします。

以下もご興味があるかもしれません:
  • Vue のキープアライブ組み込みコンポーネント キャッシュのサンプル コード
  • コンポーネント キャッシュを動的に削除する Vue キープアライブの例

<<:  MySQL 5.7.17 winx64 のインストールと設定のグラフィックチュートリアル

>>:  Windows が MySQL サービスを開始できず、エラー 1067 を報告する場合の解決策

推薦する

一般的な CSS プロパティのブラウザ互換性の概要 (推奨)

CSS プロパティのブラウザ互換性をまとめる必要があるのはなぜですか?使用する際は、Can I U...

MySQL でデータ復旧に binlog を使用する方法

序文最近、オンラインでデータが誤って操作されました。データベースが直接変更されたため、それを回復する...

Centos7.4 環境に lamp-php7.0 をインストールするチュートリアル

この記事では、Centos7.4 環境に lamp-php7.0 をインストールする方法について説明...

Linux でユーザーにルート権限を追加する方法の概要

1. ユーザーを追加します。まず、adduser コマンドを使用して共通ユーザーを追加します。コマン...

Mysql はテーブル内の古いデータを定期的にクリアし、いくつかのデータを保持します (推奨)

以下の目標を達成するため: Mysql データベースは、一定の間隔 (2 時間または 1 日、カスタ...

ディスク容量不足による MySQL レプリケーション障害の解決方法

目次ケースシナリオ問題を解決するまとめケースシナリオ本日、オンラインで問題が発見されました。監視範囲...

ウェブデザインのためのロイヤルブルーのカラーマッチング入門

古典的な色の組み合わせは力と権威を伝え、強いロイヤルブルーはあらゆる古典的な色の組み合わせの中心的な...

Vueは書籍ショッピングカートの機能を実現

この記事の例では、書籍ショッピングカート機能を実現するためのVueの具体的なコードを参考までに共有し...

Docker コンテナのネットワーク設定によく使われるコマンドの詳しい説明

基本的なネットワーク構成Docker はイメージに基づいて複数のコンテナを「開く」ことができ、各コン...

MySQLステートメントを監視する方法の詳細な説明

クイックリーディングSQL ステートメントを監視する必要があるのはなぜか、監視方法と監視手段について...

制限を使用すると、MySQL のページングがどんどん遅くなるのはなぜですか?

目次1. テスト実験2. 制限ページング問題に対するパフォーマンス最適化手法2.1 テーブルをカバー...

Nginx ストリーム構成プロキシ (Nginx TCP/UDP ロード バランシング)

序章nginx が優れたリバース プロキシ サービスであることは誰もが知っています。nginx を使...

階段効果を実現するためのWeChatアプレットカスタムメニューナビゲーション

設計意図ページを開発する際には、ページ上のナビゲーション メニューをクリックしたときにページを対応す...

vue-element-adminフレームワークを使用して、バックエンドからメニュー機能を動的に取得します。

目次2. 詳しい説明2.1. asyncRoutesルーティングを追加する2.2. 新しいpermi...