通过模拟一个简单的例子来理解
通过 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
vue.js 最基本的架构,就是可以把一个普通对象转成可被观察 (observable) 的对象。
对上面的
对象实现一个简单的 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(
- 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
- 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)
person.status 的
被调用,并把
- get()
设为它的回调
- Dep.target
执行 person.status 的
里的计算函数
- get()
person.age 的
检查 Dep 是否有可用的 target
- get()
计算函数获取到新的值并返回。而且现在 person.age 的值只要变化就会通知给 person.status
来源: