Vue応答式の原理の浅い分析
28123 ワード
Vue応答式の原理の浅い分析
次の状況を考慮します.
どのようにaを変化させる時、bはずっとaの10倍ですか?つまり次のようになります.
これはvueの応答式の原理であり、vueはObjectに依存する.definePropertyのgetとset関数はこの機能を完了します.
1.1 Getters and Setters
Goal
Implement a takes an Object as the argument converts the Object’s properties in-place into getter/setters using Object.defineProperty (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty) The converted object should retain original behavior, but at the same time log all the get/set operations.
expected usage:
solution
1.2 Dependency Tracking(依存トレース)
1.watchとcomputedはVueの依存追跡メカニズムに基づいており、あるデータ(依存データと呼ばれる)が変化すると、このデータに依存するすべての「関連」データが「自動」に変化し、関連する関数を自動的に呼び出してデータの変動を実現することを試みている.
2.methods:methodsでは関数を定義するために使用されます.実行するには手動で呼び出す必要があることは明らかです.watchやcomputedのように「自動実行」で定義された関数ではありません
Goal
出力が期待通りになるように、次のコードを完了します. Create a Depクラス代表依存 dependの役割は登録依存 である. notifyの役割は、依存を実行するサブスクライバです.現在の依存が変更され、以前のすべての依存Depというクラスの変数(関数、式など)が再実行される必要があります.つまり、Depが変更されたことを通知する(依存するサブスクライバ) です.
Create an Inside the updater function,you can explicitly depend on an instance of Later, you can trigger the updater function to run again by calling .
まずグローバル変数activeUpdateが宣言され、JSは単一スレッド言語であり、いつでも1つの関数だけが実行されます.mark自体が実行できる関数を作成すると、この関数が実行されているかどうか、つまりこの関数の内部にあるかどうかをいつでも簡単に知ることができます.
次にautorunの内部に関数wrappedUpateを作成し、wrappedUpateでupdate関数を実行します.このupdate関数の内部にあるかどうかを知りたいので、次のようにします.
wrappedUpdate関数を実行すると、update関数が実行され、update関数の内部でdep.depend()が呼び出されます.このdep.depend()はグローバル変数activeUpdateにアクセスするので、dep.depend()がactiveUpdateにアクセスする前にwrappedUpdateに値を割り当てます.
これにより、activeUpdateが空でなければ、activeUpdateが指す変数をdepの依存購読者として登録依存を完了する.notifyメソッドの役割は、すべての購読者を実行することです.したがって、Depはすべてのサブスクライバを格納する属性を必要とし、Setを使用して格納します.
1.3 Mini Observer
1.1と1.2を組み合わせて、最初に提起された問題を解決します.
Goal
Combine the previous two functions, renaming
They should support the following usage:
Solutions
1.1のconvert関数をobserver,autorunをcomputedと改名し,getとset関数でdep.depend()とdep.notify()を呼び出しただけで,残りは前述したコードであり,言うまでもない.
(finished)
次の状況を考慮します.
const state = {
a: 1
b: 10
}
どのようにaを変化させる時、bはずっとaの10倍ですか?つまり次のようになります.
state.a = 2 // "state.b is 20"
state.a = 3 // "state.b is 30"
これはvueの応答式の原理であり、vueはObjectに依存する.definePropertyのgetとset関数はこの機能を完了します.
1.1 Getters and Setters
Goal
Implement a
convert
function that: expected usage:
const obj = { foo: 123 }
convert(obj)
obj.foo // should log: 'getting key "foo": 123'
obj.foo = 234 // should log: 'setting key "foo" to: 234'
obj.foo // should log: 'getting key "foo": 234'
solution
<script>
function convert (obj) {
// Implement this!
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
Object.defineProperty(obj, key, {
get () {
console.log(`getting key "${key}": ${internalValue}`)
return internalValue
},
set (newValue) {
internalValue = newValue
console.log(`setting key "${key}" to: ${newValue}`)
}
})
})
}
</script>
1.2 Dependency Tracking(依存トレース)
1.watchとcomputedはVueの依存追跡メカニズムに基づいており、あるデータ(依存データと呼ばれる)が変化すると、このデータに依存するすべての「関連」データが「自動」に変化し、関連する関数を自動的に呼び出してデータの変動を実現することを試みている.
2.methods:methodsでは関数を定義するために使用されます.実行するには手動で呼び出す必要があることは明らかです.watchやcomputedのように「自動実行」で定義された関数ではありません
Goal
出力が期待通りになるように、次のコードを完了します.
Dep
class with two methods: depend
and notify
.(クラスDepを作成するには、dependとnotifyの2つの方法があります)autorun
function that takes an updater function.(パラメータとして更新関数を受信するautorun関数を作成)Dep
by calling dep.depend()
(更新関数の内部に呼び出しdep.depend()
が表示される)dep.notify()
.(その後、呼び出しdep.notify()
が更新関数をトリガすることを示すことができる)class Dep {
// Implement this!
}
function autorun (update) {
// Implement this!
}
const dep = new Dep()
const update1 = () => {
dep.depend()
console.log('111')
}
const update2 = () => {
dep.depend()
console.log('222')
}
autorun(update1)
autorun(update2)
dep.notify()
// :
// 111
// 222
// 111
// 222
まずグローバル変数activeUpdateが宣言され、JSは単一スレッド言語であり、いつでも1つの関数だけが実行されます.mark自体が実行できる関数を作成すると、この関数が実行されているかどうか、つまりこの関数の内部にあるかどうかをいつでも簡単に知ることができます.
次にautorunの内部に関数wrappedUpateを作成し、wrappedUpateでupdate関数を実行します.このupdate関数の内部にあるかどうかを知りたいので、次のようにします.
class Dep {
// Implement this!
}
let activeUpdate
function autorun (update) {
// Implement this!
function wrappedUpate () {
activeUpdate = wrappedUpate
unpdate()
activeUpdate = null
}
wrappedUpate()
}
const dep = new Dep()
const update1 = () => {
dep.depend()
console.log('111')
}
const update2 = () => {
dep.depend()
console.log('222')
}
autorun(update1)
autorun(update2)
dep.notify()
// :
// 111
// 222
// 111
// 222
wrappedUpdate関数を実行すると、update関数が実行され、update関数の内部でdep.depend()が呼び出されます.このdep.depend()はグローバル変数activeUpdateにアクセスするので、dep.depend()がactiveUpdateにアクセスする前にwrappedUpdateに値を割り当てます.
これにより、activeUpdateが空でなければ、activeUpdateが指す変数をdepの依存購読者として登録依存を完了する.notifyメソッドの役割は、すべての購読者を実行することです.したがって、Depはすべてのサブスクライバを格納する属性を必要とし、Setを使用して格納します.
class Dep {
// Implement this!
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpdate) {
// active update
this.subscribers.add(activeUpdate)
}
}
notify () {
//
this.subscribers.forEach(sub => sub())
}
}
let activeUpdate
function autorun (update) {
// Implement this!
function wrappedUpate () {
activeUpdate = wrappedUpate
update()
activeUpdate = null
}
wrappedUpate()
}
const dep = new Dep()
const update1 = () => {
dep.depend()
console.log('111')
}
const update2 = () => {
dep.depend()
console.log('222')
}
autorun(update1)
autorun(update2)
dep.notify
1.3 Mini Observer
1.1と1.2を組み合わせて、最初に提起された問題を解決します.
Goal
Combine the previous two functions, renaming
convert()
to observe()
and keeping autorun()
: observe()
converts the properties in the received object and make them reactive. For each converted property, it gets assigned a Dep
instance which keeps track of a list of subscribing update functions, and triggers them to re-run when its setter is invoked. autorun()
takes an update function and re-runs it when properties that the update function subscribes to have been mutated. An update function is said to be “subscribing” to a property if it relies on that property during its evaluation. They should support the following usage:
const state = {
a: 1
}
observe(state)
computed(() => {
state.b = state.a * 10
console.log(`state.b is: ${state.b}`)
})
state.a = 2
state.a = 3
Solutions
class Dep {
constructor () {
this.subscribers = new Set()
}
depend () {
if (activeUpate) {
this.subscribers.add(activeUpate)
}
}
notify () {
this.subscribers.forEach(sub => sub())
}
}
let activeUpate
function observe (obj) {
// Implement this!
let dep = new Dep()
Object.keys(obj).forEach(key => {
let internalValue = obj[key]
Object.defineProperty(obj, key, {
get () {
dep.depend()
return internalValue
},
set (newValue) {
let changed = internalValue !== newValue
internalValue = newValue
if (changed) {
dep.notify()
}
}
})
})
}
function computed (update) {
// Implement this!
function wrapperUpdate () {
activeUpate = wrapperUpdate
update()
activeUpate = null
}
wrapperUpdate()
}
1.1のconvert関数をobserver,autorunをcomputedと改名し,getとset関数でdep.depend()とdep.notify()を呼び出しただけで,残りは前述したコードであり,言うまでもない.
(finished)