像专家一样分析vue3响应式 - shallowRef 篇

1,144 阅读2分钟

vue3中ref()和reactive()深层作用形式监听,然而响应式API进阶版中出的shallowRef()和shallowReactive()是浅层作用形式的。shallow的英文是的意思。

浅层监听响应式在必要时浅层响应减少大型不可变结构的响应式开销


考虑: vue的响应性系统默认是深度的。虽然这让状态管理变得更直观,但在数据变大时它也的确创造了不小的性能负担,因为每个属性访问都将处罚代理的依赖追踪。

背景: 试想一下,当一次渲染需要访问10w+属性的时候,这个开销在处理大型数组或层级很深的对象时变得无法忽略,因此,我们应该控制它只影响非常具体的使用情况。


使用: vue为此提供一中解决方案,通过使用shallowRef()和shallowReactive()来选择退出深度响应。浅层式API创建的状态只在其顶层是响应式的,并原封不动地显示所有下面层级的对象。

影响: 这样,使得对深层级属性的访问变得更快,但代价是,我们现在必须将所有深层及对象视为不可变的,并且只能通过替换整个根状态来触发更新。

shallowRef()

ref()的浅层作用形式。

  1. 类型
function shallowRef<T>(value: T): ShallowRef<T>

interface ShallowRef<T> {
    value: T
}
  1. 详细信息 和 ref() 不同, 浅层ref的内部值将会原样存储和暴露,并且不会被深层递归地转为响应式。只有对.value的访问是响应式的。

shallowRef() 常常用于对大型数据结构的性能优化或是与外部的状态管理系统集成。

  1. 示例
const state = shallowRef({ count: 1 })

// 不会触发更改
state.value.count = 2

// 会触发更改
state.value = { count: 2 }

总结 触发更新的写法:

const shallowArray = shallowRef([
    /* 巨大的列表,里面包含深层的对象 */
])

// 这不会触发更新
shallowArray.value.push(newObject)
// 这才会触发更新
shallowArray.value = [...shallowArr.value, newObject]

// 这不会触发更新
shallowArray.value[0].foo = 1
// 这才会触发更新
shallowArray.value = [    {        ...shallowArray.value[0],
        foo: 1
    },
    ...shallowArray.value.slice(1)
]

用例子来巩固一下,使用shallowRef进行非深度监听。

<script setup>
import { shallowRef } from "vue";
let state = shallowRef({
  a: 'a',
  b: {
    b1: 'b1',
    c: {
      c1: 'c1',
      d1: {
        e1: 'e1',
        f1: {
          g1: 'g1',
        }
      }
    }
  }
})

const change = () => {
  // 不触发 【如若需要生效 需要做完这一步,再加上triggerRef(state) 即可生效】
  state.value.b.c.d1.f1.g1 = 'g2'

  // 生效写法
  state.value = {
    ...state.value,
    b: {
      ...state.value.b,
      c: {
        ...state.value.b.c,
        d1: {
          ...state.value.b.c.d1,
          f1: {
            ...state.value.b.c.d1.f1,
            g1: 'g2'
          }
        }
      }
    }
  }
}
</script>

Kapture 2022-05-04 at 18.13.46.gif

triggerRef

vue3文档中,写到triggerRef() 这个api强制触发依赖于一个 浅层ref 的副作用,常常用在浅层ref的内部值做了变更之后。

// 不触发 【如若需要生效 需要做完这一步,再加上triggerRef(state) 即可生效】
state.value.b.c.d1.f1.g1 = 'g2'
triggerRef(state)

<!-- 等同于 -->

👆上面那种写法

浅层shallowRef什么时候用:如果有一个对象数据,后续功能不会修改该对象中的属性,而是生新的对象来替换,一般就可以用shallowRef来定义

截屏2022-05-04下午6.45.28.png

像定义注册组件这种情况,一般不需要太深层改变内部的属性,用shallowRef可以减少响应式开销节约性能,诸如此类。