Skip to content

如何对reactive定义的数组进行赋值

1 问题

js
const arr = reactive([1, 2, 3])

const handleClick = () => {
  // 方法一 失败
  // arr = [4, 5, 6]
  // console.log(arr) // 4 5 6

  // 方法二 失败
  arr.concat([4, 5, 6])
}

执行上面方法,虽然能正确打印出 arr,但页面视图并不会发送改变(假设视图使用了arr)。直接赋值(arr = [4, 5, 6])会让 arr 失去响应式。

reactive 方法将 target 对象生成一个 Proxy 代理对象,从而可以对操作 target 对象属性的一些行为进行拦截。直接赋值使 arr 由 Proxy 对象变成普通对象,从而实去拦截、代理功能。

同理,对 reactive 定义的对象也不能直接进行赋值,

就像 Commonjs 中模块导出 var exports = module.exports,可以对 module.exports 进行赋值,但是不能对 exports 赋值,只能通过 . 操作符添加属性。

js
let a = 1
let b = 2

// module.exports = {
//   a,
//   b
// }

// 错误,赋值前 exports 引用地址和 module.exports 相同,赋值后 exports 指向一个新地址,
// 本来 exports 只是相当于 module.exports 的别名,赋值后 exports 跟 module.exports 没关系了
exports = {
  a,
  b
}

// 正确
exports.b = b
exports.a = a

2 解决办法

方法一 通过 reactive 创建一个响应式对象,对象的属性是数组

js
const state = reactive({
  arr: [1, 2, 3]
})

const handleClick = () => {
  state.arr = [4, 5, 6]
}

方法二 使用 ref

js
const arr = ref([1, 2, 3])

const handleClick = () => {
  arr.value = [4, 5, 6]
}

推荐使用方法一,平时开发时,最好将组件中所有状态都作为一个对象的属性,然后用 reactive 包裹这个对象(如下),这样做的好处有:

  • 所有状态数据写在一个地方,便于管理、查找。
  • 解决了本文中所描述的问题,即可以对状态直接赋值而不用担心视图不变化。

这样做的缺点是,对于组合式API语法糖写法(<script setup>),如果想在模板中使用状态,都必须得带上state.,多写了些代码,有点麻烦。

js
const state = reactive({
  num: 0,
  info: {
    name: 'Jack',
    age: 20
  }
  arr: [1, 2, 3]
})