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

Vue 响应式的递归和 $set 逻辑 #27

Open
sl1673495 opened this issue May 19, 2020 · 0 comments
Open

Vue 响应式的递归和 $set 逻辑 #27

sl1673495 opened this issue May 19, 2020 · 0 comments

Comments

@sl1673495
Copy link
Owner

sl1673495 commented May 19, 2020

如果 data 是 { data: { msg: 'Hello ' } } 这种对象形式的话,在初始化 initData 的时候,会在这个对象上挂上一个 new Obsever

Observer 的形态是这样的:

{
  value: { data: { msg: 'Hello' } },
  dep: new Dep(),
  vmCount: 0,
}

这里也有一个 dep 用来收集依赖。

var Observer = function Observer(value) {
  this.value = value;
  this.dep = new Dep();
  this.vmCount = 0;
  def(value, "__ob__", this);
  if (Array.isArray(value)) {
    if (hasProto) {
      protoAugment(value, arrayMethods);
    } else {
      copyAugment(value, arrayMethods, arrayKeys);
    }
    this.observeArray(value);
  } else {
    this.walk(value);
  }
};

这里接受的参数 value 其实就是 { data: { msg: 'Hello ' } } 这个对象,之后会走进 this.walk 开始递归的定义响应式。

 walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
}

这里因为只有一个 key,其实就是对 data 这个 key 开始定义响应式,

export function defineReactive (
  obj: Object,
  key: string,
  val: any,
  customSetter?: ?Function,
  shallow?: boolean
) {
  const dep = new Dep()
  // 这里 ↓
  let childOb = !shallow && observe(val)

  Object.defineProperty(obj, key, {
    enumerable: true,
    configurable: true,
    get: function reactiveGetter () {
    },
    set: function reactiveSetter (newVal) {
    })
}

进入了 defineReactive 后,有一段判断是这样的:

let childOb = !shallow && observe(val);

而在 observe 里,如果你观察的响应式的值是个对象的话,又会进入的 new Observer(value)

export function observe(value: any, asRootData: ?boolean): Observer | void {
  if (!isObject(value) || value instanceof VNode) {
    return;
  }
  let ob = new Observer(value);
  return ob;
}

这是一个递归的流程,无论你的 data 里的某个字段有嵌套多深的对象,它都会被定义成响应式的模型。

挂在对象上的 __ob__

这个 new Observer 返回的对象挂在了 { msg: 'Hello' } 这个对象上,那么它有什么作用呢?

还记得它的身上有个 dep 用来收集依赖吗,来看看假如我们在模板里访问了它的父级对象 data: { msg: 'Hello' },也就是访问 data 这个字段会怎么样。

首先它会触发 data 这个字段的 get 函数:

get: function reactiveGetter () {
        var value = getter ? getter.call(obj) : val;
        if (Dep.target) {
          dep.depend();
          if (childOb) {
            childOb.dep.depend();
            if (Array.isArray(value)) {
              dependArray(value);
            }
          }
        }
        return value
      },

注意 childOb 这一段,其实它会拿到 { msg: 'Hello' } 上的 __ob__,并且会调用它的 dep.depend() 去收集依赖。

那么在对 { msg: 'Hello' } 进行 $set 的时候。

function set (target, key, val) {
  if (Array.isArray(target) && isValidArrayIndex(key)) {
    target.length = Math.max(target.length, key);
    target.splice(key, 1, val);
    return val
  }
  var ob = (target).__ob__;
  if (!ob) {
    target[key] = val;
    return val
  }
  defineReactive(ob.value, key, val);
  ob.dep.notify();
  return val
}

就会去找它身上的 __ob__.dep,然后里面会装有 renderWatcher,只需要调用 __ob__.dep.notify() 即可做到在增加一个新字段的时候也去触发响应。

@sl1673495 sl1673495 changed the title 再探Vue响应式 Vue响应式的递归和 $set 逻辑 May 19, 2020
@sl1673495 sl1673495 changed the title Vue响应式的递归和 $set 逻辑 Vue 响应式的递归和 $set 逻辑 May 19, 2020
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

1 participant