49.理解 Vue.js 的计算属性
ccforward opened this issue · 1 comments
ccforward commented
理解 Vue.js 的计算属性
通过模拟一个简单的例子来理解 Vue.js 的计算属性
Object.defineProperty
通过 Object.defineProperty 的 getter
setter
来实现对象的数据劫持,例如:
const person = {}
Object.defineProperty(person, 'name', {
get: function(){
console.log('getting name')
return 'Tink'
}
})
console.log('this person is ', person.name)
// [console]: getting name
// this person is Tink
Observable 对象
Vue.js 最基本的架构,就是可以把一个普通对象转成可被观察(observable)的对象。
对上面的 person
对象实现一个简单的 Observer 如下:
function defineReactive(obj, key, val){
Object.defineProperty(obj, key, {
get: function(){
return val
},
set: function(newVal){
val = newVal
}
})
}
const person = {}
defineReactive(person, 'name', 'tink')
defineReactive(person, 'age', 20)
if(person.age >= 18){
console.log('man')
}else {
console.log('boy')
}
person.age = 10
定义一个计算属性
首先创建一个名为 defineComputed
的函数来定义计算属性
defineComputed
函数应该可以这样用
defineComputed(
person, // 计算属性所属的对象
'status', // 需要做计算的属性
function(){ // 执行计算的具体方法
console.log('status getter')
if(person.age > 18){
return 'man'
}else {
return 'boy'
}
},
function(newVal){
// 计算属性的值更新后执行
console.log('status is now: ' + newVal)
}
)
// 还可以像普通属性一样访问计算属性
console.log('The person is a ' + person.status)
然后,具体的实现一个简单的 defineComputed
函数
function defineComputed(obj, key, computedFn, updateCallBack){
Object.defineProperty(obj, key, {
get: function(){
// 调用 具体的计算函数 并返回计算后的值
return computedFn()
},
set: function(){
// nothing 不做 setter
}
})
}
现在有两个问题
- 每次访问对象属性的时候都会调用 计算函数
computedFn
- 并不知道什么时候 更新属性 (没有执行回调
updateCallBack
)
最后,计算属性的预期应是如下的方式
person.age = 20
// console: status == 'man'
person.age = 10
// console: status == 'boy'
增加依赖 Dependency
首先,添加一个全局的 Dep
对象
const Dep = {
target: null
}
这就是用来跟踪的 Dependency tracker
然后,给 defineComputed
函数添加依赖
function defineComputed(obj, key, computedFn, updateCallBack){
const onDependencyUpdate = () => {
}
Object.defineProperty(obj, key, {
get: function(){
Dep.target = onDependencyUpdate
const newVal = computedFn()
// reset target 其他属性就不会再添加这个依赖
Dep.target = null
return newVal
},
set: function(){
// nothing 不做 setter
}
})
}
我们再来改进最初实现 Observer 的 defineReactive
函数
function defineReactive(obj, key, val){
const deps = []
Object.defineProperty(obj, key, {
get: function(){
if(Dep.target && deps.indexOf(Dep.target) == -1){
deps.push(Dep.target)
}
return val
},
set: function(newVal){
val = newVal
// 通知所有 依赖器 执行计算函数: onDependencyUpdate
deps.forEach( updateCallBack => {
updateCallBack()
})
}
})
}
同时我们改进下 defineComputed
方法中的 onDependencyUpdate
来执行回调
const onDependencyUpdate = () => {
const val = computedFn()
updateCallBack(val)
}
图解
将 person.status
设置为计算属性
person.age = 20
defineComputed({
person,
'stauts',
function(){
if(person.age > 18){
console.log('this is man')
return 'man'
}else {
console.log('this is boy')
return 'boy'
}
},
function(newVal){
console.log('person.status now is ' + newVal)
}
})
console.log('person.status is ' + person.status)
Step 1
person.status 的 get()
被调用,并把 Dep.target
设为它的回调
Step 2
执行 person.status 的 get()
里的计算函数
Step 3
person.age 的 get()
检查 Dep 是否有可用的 target
Step 4
lishuaixingNewBee commented
你可以的