• vue3 响应式 API 之 reactive


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

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

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

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

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

    //声明对象的类型
    interface Member{
      id:number
      name:string
    }
    //定义一个对象
    const userInfo:Member=reactive({
     id:1,
     name:'Tom'
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

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

    // 对象数组也是先声明其中的对象类型
    interface Member {
      id: number
      name: string
    }
    
    // 再定义一个为对象数组
    const userList: Member[] = reactive([
      {
        id: 1,
        name: 'Tom',
      },
      {
        id: 2,
        name: 'Petter',
      },
      {
        id: 3,
        name: 'Andy',
      },
    ])
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    变量的读取与赋值
    虽然 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'
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    处理数组#
    但是对于 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 = []
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

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

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

    let uids: number[] = reactive([1, 2, 3])
    
    /**
     * 不推荐使用这种方式,会丢失响应性
     * 异步添加数据后,模板不会响应更新
     */
    uids = []
    
    // 异步获取数据后,模板依然是空数组
    setTimeout(() => {
      uids.push(1)
    }, 1000)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    const uids: number[] = reactive([1, 2, 3])
    
    /**
     * 推荐使用这种方式,不会破坏响应性
     */
    uids.length = 0
    
    // 异步获取数据后,模板可以正确的展示
    setTimeout(() => {
      uids.push(1)
    }, 1000)
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    特别注意
    不要对 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,
        }
      },
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
  • 相关阅读:
    和鲸科技创始人范向伟受邀出席“凌云出海,来中东吧”2023华为云上海路演活动
    《MLB棒球创造营》:走近棒球运动·明尼苏达双城队
    JavaFX Scene Builder 工具详解
    tab点击切换不使用判断条件进行不同tab的切换刷新
    2022-06-29 银河麒麟V10 Nginx-rtmp+FFmpeg流媒体服务器环境搭建
    Socket通信之网络协议基本原理
    推导式(生成式)
    阿里巴巴编程规范实战(一):编程规约之OOP规约
    Maven学习记录
    postgresql教程
  • 原文地址:https://blog.csdn.net/m0_49515138/article/details/128040057