Open ccforward opened 7 years ago
通过模拟一个简单的例子来理解 Vue.js 的计算属性
通过 Object.defineProperty 的 getter setter 来实现对象的数据劫持,例如:
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
Vue.js 最基本的架构,就是可以把一个普通对象转成可被观察(observable)的对象。
对上面的 person 对象实现一个简单的 Observer 如下:
person
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 函数应该可以这样用
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'
首先,添加一个全局的 Dep 对象
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 函数
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 来执行回调
onDependencyUpdate
const onDependencyUpdate = () => { const val = computedFn() updateCallBack(val) }
将 person.status 设置为计算属性
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)
person.status 的 get() 被调用,并把 Dep.target 设为它的回调
get()
Dep.target
执行 person.status 的 get() 里的计算函数
person.age 的 get() 检查 Dep 是否有可用的 target
计算函数获取到新的值并返回。而且现在 person.age 的值只要变化就会通知给 person.status
你可以的
理解 Vue.js 的计算属性
通过模拟一个简单的例子来理解 Vue.js 的计算属性
Object.defineProperty
通过 Object.defineProperty 的
getter
setter
来实现对象的数据劫持,例如:Observable 对象
Vue.js 最基本的架构,就是可以把一个普通对象转成可被观察(observable)的对象。
对上面的
person
对象实现一个简单的 Observer 如下:定义一个计算属性
首先创建一个名为
defineComputed
的函数来定义计算属性defineComputed
函数应该可以这样用然后,具体的实现一个简单的
defineComputed
函数现在有两个问题
computedFn
updateCallBack
)最后,计算属性的预期应是如下的方式
增加依赖 Dependency
首先,添加一个全局的
Dep
对象这就是用来跟踪的 Dependency tracker
然后,给
defineComputed
函数添加依赖我们再来改进最初实现 Observer 的
defineReactive
函数同时我们改进下
defineComputed
方法中的onDependencyUpdate
来执行回调图解
将
person.status
设置为计算属性Step 1
person.status 的![computed1.png](https://raw.githubusercontent.com/ccforward/cc/master/Blog/pic/computed1.png)
get()
被调用,并把Dep.target
设为它的回调Step 2
执行 person.status 的![computed2.png](https://raw.githubusercontent.com/ccforward/cc/master/Blog/pic/computed2.png)
get()
里的计算函数Step 3
person.age 的![computed3.png](https://raw.githubusercontent.com/ccforward/cc/master/Blog/pic/computed3.png)
get()
检查 Dep 是否有可用的 targetStep 4
计算函数获取到新的值并返回。而且现在 person.age 的值只要变化就会通知给 person.status![computed4.png](https://raw.githubusercontent.com/ccforward/cc/master/Blog/pic/computed4.png)