Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

49.理解 Vue.js 的计算属性 #54

Open
ccforward opened this issue Jan 20, 2017 · 1 comment
Open

49.理解 Vue.js 的计算属性 #54

ccforward opened this issue Jan 20, 2017 · 1 comment
Assignees

Comments

@ccforward
Copy link
Owner

ccforward commented Jan 20, 2017

理解 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
    }
  })
}

现在有两个问题

  1. 每次访问对象属性的时候都会调用 计算函数 computedFn
  2. 并不知道什么时候 更新属性 (没有执行回调 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 设为它的回调
computed1.png

Step 2

执行 person.status 的 get() 里的计算函数
computed2.png

Step 3

person.age 的 get() 检查 Dep 是否有可用的 target
computed3.png

Step 4

计算函数获取到新的值并返回。而且现在 person.age 的值只要变化就会通知给 person.status
computed4.png

@ccforward ccforward self-assigned this Jan 20, 2017
@ccforward ccforward changed the title 49.深入理解 Vue.js 的计算属性 49.理解 Vue.js 的计算属性 Jan 20, 2017
@lishuaixingNewBee
Copy link

你可以的

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants