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

想知道computed实现原理 #51

Open
Been101 opened this issue Oct 12, 2020 · 1 comment
Open

想知道computed实现原理 #51

Been101 opened this issue Oct 12, 2020 · 1 comment

Comments

@Been101
Copy link

Been101 commented Oct 12, 2020

大佬, 能解答一下,为什么 Object.defineProperty 劫持一下 computed 中的属性,就可以实现 computed 的功能呢,

computed: {
   fullname () {
       return this.firstname + this.lastname
   }
}

firstname 或 lastname 更新怎么触发的 fullname, 这一块不是很明白, 为什么 lastname 更新时 set 方法里的 dep.notify(); 这个 dep 是 跟 fullname 相关的。

大佬方便微信或其他聊天工具联系一下吗

@Been101 Been101 changed the title 想知道为什么computed实现原理 想知道computed实现原理 Oct 12, 2020
@JasonXiang2014
Copy link

function initComputed (vm: Component, computed: Object) {
  // $flow-disable-line
  const watchers = vm._computedWatchers = Object.create(null)
  //...省略

  for (const key in computed) {
    const userDef = computed[key]
    const getter = typeof userDef === 'function' ? userDef : userDef.get
    //...省略

    // component-defined computed properties are already defined on the
    // component prototype. We only need to define computed properties defined
    // at instantiation here.
    if (!(key in vm)) {
      defineComputed(vm, key, userDef)
    } else if (process.env.NODE_ENV !== 'production') {
      if (key in vm.$data) {
        warn(`The computed property "${key}" is already defined in data.`, vm)
      } else if (vm.$options.props && key in vm.$options.props) {
        warn(`The computed property "${key}" is already defined as a prop.`, vm)
      }
    }
  }
}

export function defineComputed (
  target: any,
  key: string,
  userDef: Object | Function
) {
  const shouldCache = !isServerRendering()
  if (typeof userDef === 'function') {
    sharedPropertyDefinition.get = shouldCache
      ? createComputedGetter(key)
      : createGetterInvoker(userDef)
    sharedPropertyDefinition.set = noop
  } else {
    sharedPropertyDefinition.get = userDef.get
      ? shouldCache && userDef.cache !== false
        ? createComputedGetter(key)
        : createGetterInvoker(userDef.get)
      : noop
    sharedPropertyDefinition.set = userDef.set || noop
  }
  //...省略
  Object.defineProperty(target, key, sharedPropertyDefinition)
}

function createComputedGetter (key) {
  return function computedGetter () {
    const watcher = this._computedWatchers && this._computedWatchers[key]
    if (watcher) {
      if (watcher.dirty) {
        watcher.evaluate()
      }
      if (Dep.target) {
        watcher.depend()
      }
      return watcher.value
    }
  }
}

具体源码的位置是在 vue/src/core/instance/state.js
源码大致分析:
初始化的时候 vm 劫持了computed里面的key,而具体的赋值是在compile之后生成vnode之前 触发了computedGetter回调方法,
这里面会执行 watcher.evaluate()

 evaluate () {
    this.value = this.get()
    this.dirty = false
  }
  /**
   * Evaluate the getter, and re-collect dependencies.
   */
  get () {
   //..省略
    try {
      value = this.getter.call(vm, vm)
    } catch (e) {
     //..省略
    }
    return value
  }

其中this.getter就是computed对应key的回调函数,执行了 return this.firstname + this.lastname
然后生成vnode, 因为是第一次渲染所以不涉及diff过程,之后页面就渲染完并展示fullname。

当 lastname 更新时,触发dep.notify(),此时就进入了update过程,update过程其实就是将初始化得到语法树转换成虚拟dom的过程,这个过程会读取fullname的value, 又会重新触发computedGetter过程,而这个过程会依次读取this.firstname 和 this.lastname的value 并返回最终的结果,得到结果然后生成新的虚拟dom,再进入diff流程,最终更新视图。

这里再插一句: computed 会对应一个内部watcher, data 对应一个更新watcher,data每一个key对应一个dep,如果key对应的value是object,会递归defineReactive,也就是说每一层obj的key都会对应一个dep。当更改data中的某一项数据时,会触发dep.notify,
继而触发dep依赖的所有watcher, 这里只说computed + data的情况,这时其实有两个watcher,data对应的watcher是和真实更新虚拟dom相关的,computed的更新受益与虚拟dom的更新。

简单理解,有问题欢迎沟通交流。

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

No branches or pull requests

2 participants