vue 源码学习之 面试那些事 ----vue组件的 data 为何是一个函数呢?

<script>
vue.component('comp', {
	 template: '<div @click="counter++">{{counter}}</div>',
	data() {
     return {
		 counter: 1
	 }
	}
})
	let a_vue = new Vue({
		el: '#app',
		data: {
            counter: 1
         }
	})
</script>

其实这个问题咱们在vue 源码中能够获得解答javascript

首先咱们去看 src/core/instance/init.js :31html

vm._isVue = true
    // merge options
    // 选项合并
    // 自定义组件的合并
    if (options && options._isComponent) {
      // optimize internal component instantiation
      // since dynamic options merging is pretty slow, and none of the
      // internal component options needs special treatment.
      initInternalComponent(vm, options)
    } else {
      // 根 实例
      vm.$options = mergeOptions(
        resolveConstructorOptions(vm.constructor),
        options || {},
        vm
      )
    }

这个部分主要是描述 自定义组件实例和根实例 在合并的时候会出发不一样方法vue

以后咱们看 src/core/instance/state.js :112 ---> initDatajava

function initData (vm: Component) {
  // 获取data选项
  let data = vm.$options.data

  // 若是 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/method/props不能重复
  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 */)
}

接下来看最重要的部分: src/core/util/options.js:121-----> strats.dataapp

// 策略合并 data 
strats.data = function (
 parentVal: any,
 childVal: any,
 vm?: Component // 只有是 vm = Vue 根实例的时候, 第三个参数才会调用到
): ?Function {
 // 第一次响应的时候 是 谁触发的 ?  根实例吗,no 
 // 若是是一个普通的实例
 if (!vm) {
   // run  --> vm = Vue 的时候才会走
   if (childVal && typeof childVal !== 'function') {
     process.env.NODE_ENV !== 'production' && warn(
       'The "data" option should be a function ' +
       'that returns a per-instance value in component ' +
       'definitions.',
       vm
     )

     return parentVal
   }
   // 并无传递 vm
   return mergeDataOrFn(parentVal, childVal)
 }
 // 若是是根实例会走下面的逻辑
 return mergeDataOrFn(parentVal, childVal, vm)
}

组件须要函数, 根实例不须要函数。

为啥呢,这么具体说说:

vue组件可能存在多个实例,若是使用对象形式定义 data, 则会致使他们公用一个data 对象, 那么状态变动将会影响全部的组件实例,这是不合理的; 采用函数形式定义, 在 initData时会将其做为工厂函数返回全新data 对象,有效的规避多实例之间状态污染的问题。而在 vue 根实例建立的过程当中则不应存在该限制,也是由于根实例只有一个,不须要担忧这种状况的发生ide

相关文章
相关标签/搜索