# data

对于data的处理也是在initState中,如果在options中传递了data,则会调用initData方法

export function initState(vm: Component) {
  const opts = vm.$options;
  if (opts.data) {
    initData(vm);
  } else {
    observe((vm._data = {}), true /* asRootData */);
  }
}

core/instance/state.js

function initData(vm: Component) {
  let data = vm.$options.data;
  // 组件中的data必须是一个函数
  data = vm._data = typeof data === "function" ? getData(data, vm) : data || {};
  if (!isPlainObject(data)) {
    data = {};
    process.env.NODE_ENV !== "production" &&
      warn(
        "data functions should return an object:\n" +
          "https://vuejs.org/v2/guide/components.html#data-Must-Be-a-Function",
        vm
      );
  }
  // proxy data on instance
  // 获取data中的所有属性
  const keys = Object.keys(data);
  const props = vm.$options.props;
  const methods = vm.$options.methods;
  let i = keys.length;
  // 判断data上的属性是否和props/methods 重名
  while (i--) {
    const key = keys[i];
    if (process.env.NODE_ENV !== "production") {
      if (methods && hasOwn(methods, key)) {
        warn(
          `Method "${key}" has already been defined as a data property.`,
          vm
        );
      }
    }
    if (props && hasOwn(props, key)) {
      process.env.NODE_ENV !== "production" &&
        warn(
          `The data property "${key}" is already declared as a prop. ` +
            `Use prop default value instead.`,
          vm
        );
      // 是否以_或$开头
    } else if (!isReserved(key)) {
      // 把_data中的数据注入到vm实例中
      proxy(vm, `_data`, key);
    }
  }
  // observe data
  // 响应式对象
  observe(data, true /* asRootData */);
}

根据分析可以得知,initData主要做的是:

  • # 获取 data

    由于之前的参数通过了mergeOptions,所有的data都会被转为function mergedInstanceDataFn,返回一个对像,

生成的函数通过getData来获取:

export function getData(data: Function, vm: Component): any {
  // 响应式相关
  pushTarget();
  try {
    return data.call(vm, vm);
  } catch (e) {
    handleError(e, vm, `data()`);
    return {};
  } finally {
    popTarget();
  }
}

通过一个try/catch来执行data函数并且返回,最终统一进行错误处理。

用户定义的data可以是函数或者对象,如果是子组件必须是函数返回一个对象,否则当多次引用子组件会出现引用类型的问题,会在开发环境报错误信息。

  • # 冲突处理

    data不能和props,methods重名,同时也不能以_$开头

  • # 代理 data

    proxy(vm, `_data`, key);
    

    _data中的数据直接代理到thisvm 实例下面

  • # observe 观察 data

    data所有的属性递归的进行响应式的defineReactive