Vue 中 data 的初始化主要过程

本文详细解释了Vue.js中props和data的代理原理,通过Object.defineProperty实现响应式绑定,以及在初始化时如何将这些属性代理到Vue实例上,包括getter和setter的作用及只读props的处理。
摘要由CSDN通过智能技术生成

3d77f2d903ffaf0c7a70c5e42caa9a83.png

在 Vue.js 中,props 和 data 是组件的两个重要概念。props 是父组件向子组件传递数据的方式,而 data 是组件自身的状态数据。这两者都会被代理到 Vue 实例上,也就是说我们可以通过 this 访问到它们。那么,这个代理是如何实现的呢?

Vue.js 使用了 ES5 提供的 Object.defineProperty 方法来实现数据的响应式绑定。这个方法可以让我们在读取或者修改一个对象的属性时,执行一些额外的操作。Vue.js 利用这个方法在读取或者修改 props 和 data 时,进行依赖收集和派发更新。

当我们在组件中访问 this.message 时,实际上是在访问 this._data.message 或者 this._props.message。Vue.js 在初始化实例时,会遍历 data 和 props 对象,使用 Object.defineProperty 为每一个属性设置 getter 和 setter,然后将这些属性代理到 Vue 实例上。

当我们访问 this.message 时,实际上触发了 getter,返回了 this._data.message 或者 this._props.message 的值。当我们修改 this.message 时,实际上触发了 setter,修改了 this._data.message 或者 this._props.message 的值,并且触发视图的更新。

这就是 Vue.js 如何将 props 和 data 上的属性代理到 vm 实例上的基本原理。

在 Vue.js 的源码中,props 和 data 的代理实现是在初始化实例时进行的。以下是一个简化的示例,展示了如何使用 Object.defineProperty 来实现这个代理:

function Vue (options) {
  this._data = options.data;
  this._props = options.props;


  var self = this;


  // 遍历 data 对象
  Object.keys(this._data).forEach(function(key) {
    self.proxyData(key);
  });


  // 遍历 props 对象
  Object.keys(this._props).forEach(function(key) {
    self.proxyProps(key);
  });
}


Vue.prototype.proxyData = function(key) {
  var self = this;
  Object.defineProperty(self, key, {
    enumerable: true,
    configurable: true,
    get: function proxyGetter() {
      return self._data[key];
    },
    set: function proxySetter(newVal) {
      self._data[key] = newVal;
    }
  });


};


Vue.prototype.proxyProps = function(key) {
  var self = this;
  Object.defineProperty(self, key, {
    enumerable: true,
    configurable: true,
    get: function proxyGetter() {
      return self._props[key];
    },
    set: function proxySetter(newVal) {
      console.warn('You are trying to modify a read only value');
    }
  });
};

在这个示例中,我们在 Vue 的构造函数中遍历 data 和 props 对象,然后使用 Object.defineProperty 为每一个属性设置 getter 和 setter。当我们访问 this.message 时,实际上触发了 getter,返回了 this._data.message 或者 this._props.message 的值。当我们修改 this.message 时,实际上触发了 setter,修改了 this._data.message 的值,并且触发视图的更新。对于 props,由于它是只读的,所以我们在 setter 中打印了一个警告消息。

源码中 Vue 的初始化阶段,_init 方法执行的时候,会执行 initState(vm) 方法,方法主要是对 props、methods、data、computed 和 wathcer 等属性做了初始化操作。

data 的初始化主要过程也是做两件事,一个是对定义 data 函数返回对象的遍历,通过 proxy 把每一个值 vm._data.xxx 都代理到 vm.xxx 上;另一个是调用 observe 方法观测整个 data 的变化。

function initData (vm: Component) {
  let data = vm.$options.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
  const keys = Object.keys(data)
  const props = vm.$options.props
  const methods = vm.$options.methods
  let i = keys.length
  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)) {
      proxy(vm, `_data`, key)
    }
  }
  // observe data
  observe(data, true /* asRootData */)
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值