Vue data 为什么必须是函数

理解 Vue 组件中 data 必须是函数的原因

问题

在 Vue 组件中,为什么 data 必须是一个函数,而不能直接是一个对象?

解答

问题复现

如果 data 是对象,多个组件实例会共享同一份数据:

// 错误示例:data 是对象
const Component = {
  template: '<button @click="count++">{{ count }}</button>',
  data: {
    count: 0
  }
}

// 创建两个实例
const vm1 = new Vue({ ...Component })
const vm2 = new Vue({ ...Component })

// vm1 和 vm2 的 data 指向同一个对象
vm1.count++
console.log(vm2.count) // 1,被影响了

正确做法

data 是函数时,每个实例都有独立的数据副本:

// 正确示例:data 是函数
const Component = {
  template: '<button @click="count++">{{ count }}</button>',
  data() {
    return {
      count: 0
    }
  }
}

// 创建两个实例
const vm1 = new Vue({ ...Component })
const vm2 = new Vue({ ...Component })

// 每个实例有独立的 data
vm1.count++
console.log(vm1.count) // 1
console.log(vm2.count) // 0,互不影响

模拟 Vue 的实现

// 简化版实现,展示原理
function VueComponent(options) {
  // 如果 data 是函数,调用它获取新对象
  this.data = typeof options.data === 'function' 
    ? options.data() 
    : options.data
}

const options = {
  data() {
    return { count: 0 }
  }
}

const instance1 = new VueComponent(options)
const instance2 = new VueComponent(options)

instance1.data.count = 10
console.log(instance1.data.count) // 10
console.log(instance2.data.count) // 0,独立的数据

根实例的特殊情况

根实例(new Vue())可以用对象,因为它只会被创建一次:

// 根实例可以用对象(但不推荐)
new Vue({
  el: '#app',
  data: {
    message: 'Hello'
  }
})

// 推荐统一使用函数形式
new Vue({
  el: '#app',
  data() {
    return {
      message: 'Hello'
    }
  }
})

关键点

  • 引用类型共享问题:对象是引用类型,多个实例会共享同一份数据
  • 函数返回新对象:每次调用函数都返回一个新对象,保证数据隔离
  • 组件复用场景:组件会被多次实例化,必须保证每个实例数据独立
  • 根实例例外:根实例只创建一次,可以用对象,但建议统一用函数
  • Vue 3 变化:Composition API 中使用 ref/reactive,不再有这个问题