この記事では、計算された初期化と更新のプロセスを説明し、計算されたプロパティがどのようにキャッシュされ、依存関係がどのように収集されるかを確認するために、次の例に焦点を当てます。 <div id="アプリ"> <span @click="change">{{sum}}</span> </div> <script src="./vue2.6.js"></script> <スクリプト> 新しいVue({ el: "#app", データ() { 戻る { カウント: 1, } }, メソッド: { 変化() { このカウント = 2 }, }, 計算: { 合計() { this.count + 1 を返す }, }, }) </スクリプト> 計算結果を初期化するvueが初期化されると、まずinitメソッドが実行され、その中のinitStateが計算されたプロパティを初期化します。 opts.computed の場合、{initComputed(vm, opts.computed);} 以下はinitComputedのコードです var ウォッチャー = vm._computedWatchers = Object.create(null); // 計算されたプロパティごとに計算されたウォッチャーを順番に定義します for (const キー in 計算済み) { const userDef = computed[キー] watchers[キー] = 新しいウォッチャー( vm、// インスタンス ゲッター、// ユーザーが評価関数の合計に渡した値 noop, // コールバック関数は最初に無視できます { lazy: true } // 計算されたウォッチャーをマークするために lazy 属性を宣言します ) // ユーザーが this.sum を呼び出すと何が起こるか defineComputed(vm, key, userDef) } 各計算プロパティに対応する計算ウォッチャーの初期状態は次のとおりです。 { 依存関係: [], 汚い: 本当、 ゲッター: ƒ sum(), 怠惰: 本当、 値: 未定義 } 最初は値が undefined であり、lazy が true であることがわかります。これは、値が遅延計算され、テンプレートで実際に値が読み取られるまで計算されないことを意味します。 このダーティ属性は実際にはキャッシュの鍵となるので、まず覚えておいてください。 次に、ユーザーが計算プロパティ this.sum の値を読み取った後に何が起こるかを決定する、より重要な defineComputed を見てみましょう。引き続き、プロセスに影響しないロジックを簡素化して除外します。 Object.defineProperty(ターゲット、キー、{ 得る() { // 先ほど述べたコンポーネントインスタンスから計算されたウォッチャーを取得します const ウォッチャー = this._computedWatchers && this._computedWatchers[キー] if (ウォッチャー) { // ダーティの場合にのみ再評価されます if (watcher.dirty) { // これは評価し、getを呼び出し、Dep.targetを設定します ウォッチャー.評価() } // これも重要なポイントなので、後で詳しく説明します if (Dep.target) { ウォッチャー.depend() } //最後に計算された値を返します return watcher.value } } }) この関数は、詳しく見る必要があります。いくつかのことを行います。初期化プロセスで説明しましょう。 まず、ダーティの概念はダーティなデータを表します。つまり、ユーザーが渡した合計関数を再度呼び出してデータを評価する必要があることを意味します。今のところ、更新ロジックは無視しましょう。テンプレートで {{sum}} が最初に読み取られるときは、必ず true になるはずなので、初期化は評価を経ることになります。 評価する() { //get関数を呼び出して評価します。this.value = this.get() // ダーティを false としてマークする this.dirty = 偽 } この関数は実際には非常に明確で、最初に評価してから dirty を false に設定します。 Object.defineProperty のロジックを振り返ってみましょう。次回、特別な状況なしに sum が読み取られるときに、dirty が false の場合は、watcher.value の値を返すだけで済みます。これは、実際には計算プロパティ キャッシュの概念です。 依存関係の収集初期化が完了するとレンダリングのために render が呼び出され、render 関数はウォッチャーのゲッターとして機能します。このとき、ウォッチャーはレンダリング ウォッチャーです。 更新コンポーネント = () => { vm._update(vm._render(), ハイドレーション) } // レンダリング ウォッチャーを作成します。レンダリング ウォッチャーが初期化されると、その get() メソッド、つまりレンダリング関数が呼び出され、依存関係が収集されます。new Watcher(vm, updateComponent, noop, {}, true /* isRenderWatcher */) ウォッチャーのgetメソッドを見てみましょう 得る () { //現在のウォッチャーをスタックの一番上に置き、Dep.target に設定します pushTarget(これ) 値を与える 定数 vm = this.vm // ユーザー定義関数を呼び出すと this.count にアクセスし、そのゲッターメソッドにアクセスします。これについては後述します。 value = this.getter.call(vm, vm) // 評価が完了したら、現在のウォッチャーがスタックからポップアウトされます popTarget() this.cleanupDeps() 戻り値 } レンダリング ウォッチャーのゲッターが実行されると (レンダリング関数)、this.sum にアクセスし、計算属性のゲッター、つまり initComputed で定義されたメソッドがトリガーされます。sum にバインドされた計算ウォッチャーを取得した後、初期化中に dirty が true であるため、その assess メソッドが呼び出され、最後に get() メソッドが呼び出されて、計算ウォッチャーがスタックの一番上に配置されます。このとき、Dep.target も計算ウォッチャーです。 次に、get メソッドを呼び出すと this.count にアクセスし、count 属性のゲッターがトリガーされ (以下に示すように)、現在の Dep.target に格納されているウォッチャーが count 属性に対応する dep に収集されます。この時点で評価は終了し、ウォッチャーをスタックからポップするために popTarget() が呼び出されます。この時点で、以前のレンダリング ウォッチャーがスタックの一番上にあり、Dep.target が再びレンダリング ウォッチャーになります。 // クロージャでは、キーカウントに定義された依存関係が保持されます 定数 dep = 新しい Dep() // クロージャは最後のset関数によって設定された値も保持します valを Object.defineProperty(obj, キー, { 取得: 関数reactiveGetter() { 定数値 = val // Dep.target は現在ウォッチャーを計算しています if (依存ターゲット) { // 依存関係を収集 dep.depend() } 戻り値 }, }) // 依存() 依存する() { if (依存ターゲット) { Dep.target.addDep(これ) } } // ウォッチャーの addDep 関数 addDep (dep: Dep) { // ここでは、簡素化のために一連の重複排除操作が実行されます // ここでは、count の dep も独自の deps に格納されます this.deps.push(dep) // ウォッチャー自体をパラメータとして // dep の addSub 関数に戻る dep.addSub(this) } クラス Dep { サブ = [] addSub (サブ: ウォッチャー) { this.subs.push(サブ) } } これら 2 つのコードを通じて、計算されたウォッチャーは属性バインド dep によって収集されます。 Watcher は dep に依存し、dep も watcher に依存します。この相互依存データ構造により、watcher がどの dep に依存し、dep がどの watcher に依存しているかを簡単に知ることができます。 次にwatcher.depend()を実行します。 // ウォッチャー依存 依存する() { i = this.deps.length とします (i--) { this.deps[i].depend() } } 先ほどのウォッチャーフォームの計算を覚えていますか?その deps には count の dep が格納されます。つまり、countのdep.depend()が再度呼び出されることになります。 クラス Dep { サブ = [] 依存する() { if (依存ターゲット) { Dep.target.addDep(これ) } } } 今回は Dep.target がすでにレンダリング ウォッチャーであるため、このカウントの dep はレンダリング ウォッチャーを独自のサブに格納します。 最後に、count の依存関係が収集され、その dep は次のようになります。 { サブ: [合計計算ウォッチャー、レンダリングウォッチャー] } アップデートを配布するここで、この質問の重要なポイントに移ります。カウントが更新されたら、ビューの更新をどのようにトリガーするのでしょうか? count のレスポンシブ ハイジャック ロジックに戻りましょう。 // クロージャでは、キーカウントに定義された依存関係が保持されます 定数 dep = 新しい Dep() // クロージャは最後のset関数によって設定された値も保持します valを Object.defineProperty(obj, キー, { 設定: 関数 reactiveSetter (newVal) { val = 新しい値 // カウントの依存関係の通知をトリガーします dep.notify() } }) }) さて、ここで、先ほど慎重に準備した count の dep の通知関数がトリガーされます。 クラス Dep { サブ = [] 通知(){ (i = 0, l = subs.length; i < l; i++) の場合 { subs[i].update() } } } ここでのロジックは非常に単純です。サブに保存されたウォッチャーの更新メソッドを順番に呼び出します。つまり、
ウォッチャーの更新の計算 アップデート () { if (this.lazy) { this.dirty = true } } 計算ウォッチャーの dirty プロパティを true に設定し、次の読み取りを静かに待つだけです (render 関数が再度実行されると、sum プロパティに再度アクセスされますが、このとき dirty は true になっているため、再度評価されます)。 レンダリングウォッチャーの更新 ここでは実際に vm._update(vm._render()) 関数を呼び出して、レンダリング関数によって生成された vnode に従ってビューを再レンダリングします。 Object.defineProperty(ターゲット、キー、{ 得る() { const ウォッチャー = this._computedWatchers && this._computedWatchers[キー] if (ウォッチャー) { // 前のステップで、dirty はすでに true に設定されていたため、if (watcher.dirty) { で再評価されます。 ウォッチャー.評価() } if (依存ターゲット) { ウォッチャー.depend() } //最後に計算された値を返します return watcher.value } } }) 前の手順でのレスポンシブ プロパティの更新により、計算されたウォッチャーのダーティ アップデートが true にトリガーされます。そのため、ユーザーが渡した sum 関数が再度呼び出され、最新の値が計算され、当然ながら最新の値がページに表示されます。 これまでに、属性更新を計算するプロセス全体が終了しました。 総括する
以上がvue computedのキャッシュ実装原理の詳細な説明です。vue computedのキャッシュ実装の詳細については、123WORDPRESS.COMの他の関連記事に注目してください。 以下もご興味があるかもしれません:
|
<<: mysql5.7.19 winx64 解凍版のインストールと設定のチュートリアル
>>: Docker プライベートリポジトリの管理とローカルリポジトリ内のイメージの削除
現在、ほとんどのプロジェクトが Docker 上にデプロイされ始めていますが、デプロイのプロセスはま...
SQL文 /* MySQL で重複行を削除するいくつかの方法 ---Chu Minfei ---20...
考えてみてください。なぜcss 、 javascriptのようにbodyタグの末尾ではなく、 hea...
1. Ansibleのプレイブックを使用してhttpdを自動的にインストールする1) まず、Ansi...
仮想マシンを使用する人は通常、操作と使用を容易にするために仮想マシン用の共有ディレクトリを設定します...
画像内に下線付きのリンクが表示されても驚かないでください。実はとても簡単なので、あなたにもできるので...
おすすめの記事: CSS 疑似クラスの右下隅をクリックすると、選択を示すチェックマークが表示されます...
animation-name アニメーション名。複数のアニメーションがバインドされていることを示す...
<TR> タグの属性は、次の表に示すように、テーブル内の各行のプロパティを設定するために...
Nginx は、リバース プロキシ機能を使用して負荷分散を実装できるほか、フォワード プロキシ機能を...
目次1. マスタースレーブ同期原理マスタースレーブ同期アーキテクチャ図(非同期同期)マスタースレーブ...
序文スロー クエリ ログは、MySQL で非常に重要な機能です。MySQL のスロー クエリ ログ機...
1.fullpage.js ダウンロードアドレスhttps://github.com/alvarot...
この問題について話すとき、垂直方向の中央揃えを設定するための vertical-align 属性が ...
v-model は、入力とフォーム データ間、または 2 つのコンポーネント間の双方向データ バイ...