Vue3响应式基础

发布于:
更新于:

#响应式基础

可以使用 ref()函数声明一个响应式状态。
可以使用 reactive()函数使一个对象具有响应性,使用 Proxy 拦截对响应式对象所有属性的访问和修改。当 ref 函数包装一个对象时,内部会调用 reactive 函数

#为什么要使用 ref

js 中不能检测基本类型变量的访问和修改,但是可以拦截对象的 get 和 set
因此可以把 ref 视作一个把变量包装为同名对象的函数,.value 是原始值,可以追踪.value 的 get set。
并且,当一个组件初次渲染时,Vue 会自动追踪渲染时使用的每一个 ref,当某个 ref 被修改时,Vue 会重新渲染追踪他的组件。

#reactive proxy

reactive 会返回一个 Proxy 对象,和原始对象并不相等,更改原始对象不会影响 Vue Proxy
  • 修改 origin obj,视图不更新
  • 修改代理对象,视图更新,原始对象更新
vue
<script setup lang="ts"> import { reactive } from 'vue' const obj = { a: 1, } const _obj = reactive(obj) </script> <template> <div class="m-2 w-32">origin:{{ obj.a }}</div> <div class="m-2 w-32">proxy: {{ _obj.a }}</div> <button className="btn btn-warning" @click="obj.a += 1">add origin</button> <button className="btn btn-warning" @click="_obj.a += 1">add proxy</button> <button class="btn btn-success" @click="console.log(obj)">click</button> </template>

#reactive 的局限性

  1. 不能用于基本类型
  2. 直接替换 reactive 的返回值,响应式会丢失
  3. 解构的值不具有响应性
下例中,使用 reactive,17 行视图不更新,响应丢失 使用 ref,17 行视图更新,响应保持,因为 obj 自身的指向没有被修改,而是内部的 value 被修改了,所以可以被 vue 追踪到,而 reactive 则不行。
vue
<script lang="ts" setup> import { reactive, ref } from 'vue' // let obj = reactive({ count: 1 }) let obj = ref({ count: 1 }) const btnClick = () => { // obj = reactive({ count: 2 }) obj.value = { count: 2 } console.log(obj) } </script> <template> <div class="wrapper"> <h2>{{ obj.count }}</h2> <button className="btn btn-warning" @click="btnClick">btnClick</button> </div> </template>

#怎么样更新整个 reactive 响应式对象

Vue 官方文档告诉我们:
我们一般更新 reactive 的属性是一个个更新的,但是如果有大量属性要更新会很麻烦,替换整个 reactive 的方法是使用 Object.assign,把新对象合并到旧的响应式对象里面,这样响应式才不会丢失
vue
<script setup> import { reactive } from 'vue' let obj = reactive({ msg: "hello world" }) function msgChange() { // obj = reactive({ msg: "你好世界" }) obj = Object.assign(obj, { msg: "你好世界" }) // 正确的方式 } </script> <template> <h1>{{ obj.msg }}</h1> <button @click="msgChange">msgChange</button> </template>

#ref、reactive 其他

ref
  1. 只有顶层的 ref,会被模板自动解包
  2. 嵌套的 ref 可以使用解构,解构为顶层 ref
reactive
  1. reactive 对象中可以放 ref 作为属性,不需要手动添加.value
  2. 给 reactive 对象的某个 ref 属性赋值新的 ref,旧 ref 会被新 ref 替换
  3. reactive 数组不包括上述两个特性

#dom 更新和数据更新

响应式数据更新是同步(立即发生)的
但是视图的更新不是同步的,而是一段时间发生一次
Vue 把这段时间称作next tick更新周期,一段周期只会发生一次视图更新
如果希望等待视图更新,可以使用 nextTick 函数
js
await nextTick()

#shallowRef

默认情况下 ref 会调用 reactive 处理引用类型,当修改深层属性时会被追踪
如果不希望递归追踪内部属性,可以使用 shallowRef
shallowRef 包裹的引用类型,只有当.value 自身发生改变才会被追踪。
可以优化性能
js
const state = shallowRef({ count: 1 }) // 不会触发更改 state.value.count = 2 // 会触发更改 state.value = { count: 2 }