vue3 响应式 API 之 reactive

reactive 是继 ref 之后最常用的一个响应式 API 了,相对于 ref ,它的局限性在于只适合对象、数组。

TIP

使用 reactive 的好处就是写法跟平时的对象、数组几乎一模一样,但它也带来了一些特殊注意点,请留意赋值部分的特殊说明。

类型声明与定义
reactive 变量的声明方式没有 ref 的变化那么大,基本上和普通变量一样,它的 TS 类型如下:

function reactive<T extends object>(target: T): UnwrapNestedRefs<T>

可以看到其用法还是比较简单的,下面是一个 Reactive 对象的声明方式:

//声明对象的类型
interface Member{
  id:number
  name:string
}
//定义一个对象
const userInfo:Member=reactive({
 id:1,
 name:'Tom'
})

下面是 Reactive 数组的声明方式:

const uids: number[] = reactive([1, 2, 3])

还可以声明一个 Reactive 对象数组:

// 对象数组也是先声明其中的对象类型
interface Member {
  id: number
  name: string
}

// 再定义一个为对象数组
const userList: Member[] = reactive([
  {
    id: 1,
    name: 'Tom',
  },
  {
    id: 2,
    name: 'Petter',
  },
  {
    id: 3,
    name: 'Andy',
  },
])

变量的读取与赋值
虽然 reactive API 在使用上没有像 ref API 一样有 .value 的心智负担,但也有一些注意事项要留意。

处理对象
Reactive 对象在读取字段的值,或者修改值的时候,与普通对象是一样的,这部分没有太多问题。

// 声明对象的类型
interface Member {
  id: number
  name: string
}

// 定义一个对象
const userInfo: Member = reactive({
  id: 1,
  name: 'Tom',
})

// 读取用户名
console.log(userInfo.name)

// 修改用户名
userInfo.name = 'Petter'

处理数组#
但是对于 Reactive 数组,和普通数组会有一些区别。

普通数组在 “重置” 或者 “修改值” 时都是可以直接操作:

// 定义一个普通数组
let uids: number[] = [1, 2, 3]

// 从另外一个对象数组里提取数据过来
uids = api.data.map((item: any) => item.id)

// 合并另外一个数组
let newUids: number[] = [4, 5, 6]
uids = [...uids, ...newUids]

// 重置数组
uids = []

Vue 2 在操作数组的时候,也可以和普通数组这样处理数据的变化,依然能够保持响应性,但在 Vue 3 ,如果使用 reactive 定义数组,则不能这么处理,必须只使用那些不会改变引用地址的操作。

笔者刚开始接触时,按照原来的思维去处理 reactive 数组,于是遇到了 “数据变了,但模板不会更新的问题” ,如果开发者在学习的过程中也遇到了类似的情况,可以从这里去入手排查问题所在。

举个例子,比如要从服务端 API 接口获取翻页数据时,通常要先重置数组,再异步添加数据,如果使用常规的重置,会导致这个变量失去响应性:

let uids: number[] = reactive([1, 2, 3])

/**
 * 不推荐使用这种方式,会丢失响应性
 * 异步添加数据后,模板不会响应更新
 */
uids = []

// 异步获取数据后,模板依然是空数组
setTimeout(() => {
  uids.push(1)
}, 1000)

要让数据依然保持响应性,则必须在关键操作时,不破坏响应性 API ,以下是推荐的操作方式,通过重置数组的 length 长度来实现数据的重置:

const uids: number[] = reactive([1, 2, 3])

/**
 * 推荐使用这种方式,不会破坏响应性
 */
uids.length = 0

// 异步获取数据后,模板可以正确的展示
setTimeout(() => {
  uids.push(1)
}, 1000)

特别注意
不要对 Reactive 数据进行 ES6 的解构 操作,因为解构后得到的变量会失去响应性。

比如这些情况,在 2s 后都得不到新的 name 信息:

import { defineComponent, reactive } from 'vue'

interface Member {
  id: number
  name: string
}

export default defineComponent({
  setup() {
    // 定义一个带有响应性的对象
    const userInfo: Member = reactive({
      id: 1,
      name: 'Petter',
    })

    // 在 2s 后更新 `userInfo`
    setTimeout(() => {
      userInfo.name = 'Tom'
    }, 2000)

    // 这个变量在 2s 后不会同步更新
    const newUserInfo: Member = { ...userInfo }

    // 这个变量在 2s 后不会再同步更新
    const { name } = userInfo

    // 这样 `return` 出去给模板用,在 2s 后也不会同步更新
    return {
      ...userInfo,
    }
  },
})

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值