先ほど、計算プロパティについて説明しました。これは、レスポンシブ データの値を自動的に計算し、キャッシュすることができます。また、応答データが変更されたときにいくつかのプリセット操作を実行するだけでよい場合は、watch リスナーを使用できます。最初に最も単純な例を実装してから、少しずつ拡張してみましょう。
const data = {foo: 1} const obj = reactive(data) watch(obj, () => { console.log('obj已改变') })
watch リスナーを使用します。
obj のプロパティが変更されると、コンソールには
obj が出力されます。変更されました。計算されたプロパティの以前の実装に基づいて、ここではすでに一般的なアイデアが得られています。
watch を応答オブジェクトの副作用関数と考えると、応答オブジェクトが変更されると、副作用関数の実行がトリガーされます。
リアクティブデータが get の場合、副作用関数
が収集されます。したがって、まず、リアクティブ オブジェクトによって収集される watch を作成する必要があります。
function watch(getter, cb) { effect( () => getter.foo ) }
set の場合、副作用関数がトリガーされます。ここでトリガーしたいのは、
cb で渡されるコールバック関数です。ここでは、計算されたプロパティを実装するときにスケジューラーを使用できます。スケジューラーが存在する場合、
set は
trigger# をトリガーします## は最初にスケジューラ内の関数を実行します。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">function watch(getter, cb) {
effect(
() => getter.foo,
{
scheduler() {
cb()
}
}
)
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
シンプルなリスナーが完成しました。ここでは、簡単にするために関数を完全に記述し、
obj.foo のリッスンのみをサポートします。次に、応答オブジェクトのプロパティをリッスンする方法を考えなければなりません。 前の考え方によると、応答オブジェクトの属性をリッスンしたい場合は、オブジェクトの各属性に
する必要があります。これには、オブジェクトが走査されます。再帰的に。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">function traverse(value, seen = new Set()) { // (1)
if(typeof value !== &#39;object&#39; || value === null || seen.has(value)) return
seen.add(value)
for(const key in value) {
traverse(value[key], seen)
}
return value
}</pre><div class="contentsignin">ログイン後にコピー</div></div>
オブジェクトを再帰的に走査するときに循環参照によって引き起こされる無限ループを回避するために、
で Set
を作成しました。同じオブジェクトが繰り返し出現する場合、直接戻ります。 プロパティ値のリッスン
関数が必要です。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">const data = {
foo: 1
}
const obj = reactive(data)
watch(
() => obj.foo,
() => {
console.log(&#39;obj.foo已改变&#39;)
})</pre><div class="contentsignin">ログイン後にコピー</div></div>
属性を指定すると、現在のリスナーは指定された属性によってのみトリガーされ、応答オブジェクト全体を再帰的に走査する必要がなくなります。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) // (2) effect( () => getter(), { scheduler() { cb() } } ) }
(2) で判定を追加しました。受信関数がすでに
getter 関数である場合は、それを直接使用します。そうでない場合はgetter
関数は応答オブジェクトとみなされ、再帰的に走査する必要があります。 リッスンして新しい値と古い値を取得する
は古い値になります。 <div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;">const data = {
foo: 1
}
const obj = reactive(data)
watch(
() => obj.foo,
(newValue, oldValue) => {
console.log(newValue, oldValue)
})</pre><div class="contentsignin">ログイン後にコピー</div></div>
次の質問は、
と oldValue
を取得する方法です。 newValue
は簡単に解決できます。コールバック関数 cb()
を実行すると、得られるのは newValue
ですが、oldValue の値を取得する方法は次のとおりです。 ### ここ?
watch から古い値を取得するには、副作用関数をすぐに実行することはできません。ここで何が思い浮かびますか?はい、計算プロパティを実装するときに、副作用関数の自動実行を禁止できる
lazy を使用しました。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue const effectFn = effect( () => getter(), { lazy: true, // (3) scheduler() { cb(oldValue) } } ) oldValue = effectFn() // (4) }
(3) で
lazy スイッチを設定し、lazy を設定すると副作用関数の実行権限が自分に渡されます。 (4)では副作用関数を手動で実行します。ここで振り返る必要があります。前に渡した
getter は関数
() => obj.foo であり、関数
effect の 1 つのパラメーターは次のとおりです。実際に実行される副作用関数なので、手動で実行するのは実際には関数
() => obj.foo なので、古い値が取得されます。
新しい値を取得するにはどうすればよいですか?応答データの値が更新された後、副作用関数
effect
() => obj.foo を通じて変更された新しい値を取得できます。
function watch(getter, cb) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue const effectFn = effect( () => getter(), { lazy: true, scheduler() { newValue = effectFn() cb(newValue, oldValue) oldValue = newValue // (5) } } ) oldValue = effectFn() }
(5) では、コールバック関数
cb() を実行した後、後処理を行い、次のコールバックの準備のために oldValue の値を更新しました。 。
#コールバック関数が作成されたときに、リスナーがすぐにそれを実行するようにしたい場合もあります。
const data = { foo: 1 } const obj = reactive(data) watch( () => obj.foo, (newValue, oldValue) => { console.log('newValue:', newValue,', oldValue:', oldValue) }, { immediate: true } )
immediate
の値がtrue の場合、すぐに実行する必要があります。要件を明確にした後、
watch リスナーを改善しましょう。
function watch(getter, cb, options = {}) { if(typeof getter !== 'function') getter = traverse(getter) let oldValue, newValue function job() { // (6) newValue = effectFn() cb(newValue, oldValue) oldValue = newValue } const effectFn = effect( () => getter(), { lazy: true, scheduler: job, } ) if(options.immediate) { // (7) job() } else { oldValue = effectFn() } }
在(6)处,我们抽离了回调函数的执行逻辑,当options.immediate
存在时,直接触发执行。
以上がVue3リスナーの実装原理は何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。