Vue 中 data 的初始化主要过程

前端精髓

共 4002字,需浏览 9分钟

 · 2023-08-22


在 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 */)}


浏览 109
点赞
评论
收藏
分享

手机扫一扫分享

举报
评论
图片
表情
推荐
点赞
评论
收藏
分享

手机扫一扫分享

举报