const data = {
flag: true,
firstName: "z",
lastName: "ym"
}
const computed = {
fullName() {
if (!data.flag) return "没有名字"
return data.firstName + data.lastName
}
}
We simulate the data and computed properties of vue, now we need to implement the following functions
- When flag is true, set the value of firstName or lastName or flag, and recalculate the value of fullName
- When the flag is false, set the value of firstName or lastName, the value of fullName will not be recalculated, only when the flag is set, the value of fullName will be recalculated
Think about it first, how to achieve it if you write it yourself?
First of all, when we want to modify the value of a certain attribute of data, update the attribute of comouted, then we must subscribe to their get and set methods
function observer(obj, key) {
let value = obj[key]
Object.defineProperty(obj, key, {
get() {
return value
},
set(newVal) {
value = newVal
// 在这里设置值的时候,重新调用 computed.fullName 的方法,从而更新 fullName 的值
data.fullName = computed.fullName()
}
})
}
Object.keys(data).forEach(key => {
observer(data, key)
})
At this time we will find a problem
We don't know which data attribute changes will update which computed attribute value, so we need to collect the corresponding dependencies
We first need to think about it, what dependencies does Dep collect?
Dep hopes to do some other things when a data attribute is modified, so we need to collect those things before set
First we create a Dep class to collect dependencies and distribute dependencies
class Dep {
constructor() {
this.subs = []
}
notify() {
this.subs.forEach(sub => sub())
}
}
Then we now need to do two things
- Add Dep instances to all data original attributes
- Collect dependencies when getting, and dispatch dependencies when setting
Since we want to collect dependencies when getting, we must know when the get of the attribute will be triggered
We assume that there is a vue template template
<template>
<p>{
{ fullName }}</p>
</template>
- During the rendering process of the page, the get method of the attribute data.fullName must be called
- 而 data.fullName = computed.fullName()
- Calling computed.fullName will trigger the get of flag, firstName, lastName
- Then naturally when these get are triggered, they know that what needs to be collected is
the dependency of data.fullName = computed.fullName()
Then, the complete code we got is as follows
const data = {
flag: true,
firstName: 'z',
lastName: 'ym'
}
const computed = {
fullName() {
if (!data.flag) return '没有任何东西'
return data.firstName + data.lastName
}
}
class Dep {
constructor() {
this.subs = []
}
notify() {
this.subs.forEach(sub => sub())
this.subs = []
}
}
let willCollectFunc = () => {
data.fullName = computed.fullName()
}
function observer(obj, key) {
let value = obj[key]
const dep = new Dep()
Object.defineProperty(obj, key, {
get() {
dep.subs.push(willCollectFunc)
return value
},
set(newVal) {
value = newVal
dep.notify()
}
})
}
Object.keys(data).forEach(key => {
observer(data, key)
})
data.fullName = computed.fullName()
console.log(data.fullName)
data.firstName = "h"
console.log(data.fullName)
data.flag = false
console.log(data.fullName)
data.firstName = "z"
console.log(data.fullName)
For more detailed content, you can read the article I wrote before. It was not well written before, so it is easier to understand if it is more basic
https://blog.csdn.net/weixin_42335036/article/details/109286637