[深入vue3之响应性API] reactive、readonly、shallowReactive、shallowReadonly、toRaw、markRaw等使用与讲解

reactive

  • 返回对象的响应式副本
  • 响应式转换是“深层”的——它影响所有嵌套 property。在基于 ES2015 Proxy 的实现中,返回的 proxy 是不等于原始对象的。建议只使用响应式 proxy,避免依赖原始对象。
  • 只能传入引用类型,否则抛出警告。 reactive 将解包所有深层的 refs,同时维持 ref 的响应性。正确的讲应该是:当它通过代理访问时,会被自动解包:
<script lang='ts' setup>
import { ref,reactive } from 'vue';
let rea1 = reactive({ count: 0 }) // Proxy

let refData = ref('1') // RedfImpl 构造函数的对象

//! ***reactive 将解包所有深层的 refs,同时维持 ref 的响应性。***  
//! ***正确的讲应该是:当它通过代理访问时,会被自动解包:***  
let rea2 = reactive<any>({refData})

//! 当将 ref 分配给 reactive property 时,效果同上
rea2.re2_1 = refData // console.log(rea2.re2_1)  > 打印 '1'

// 因为访问 reactive 内的 ref 会自动解包,所以不需要 .value
rea2.refData = '测试'

// 任何类型的新属性都是 proxy 类型
rea2.newObj = {data1:'同样是响应式的'}
</script>

readonly

  • 接受一个对象 (响应式或纯对象) 或 ref 并返回原始对象的只读代理。只读代理是深层的:任何被访问的嵌套 property 也是只读的。
  • 与 reactive 一样,如果任何 property 使用了 ref,当它通过代理访问时,则被自动解包:
<script lang='ts' setup>
import { ref,readonly } from 'vue';
let refData = ref('1') // RedfImpl 构造函数的对象

const read1 = readonly({a:1})
console.log(read1); // read1.count = 123 会报错

const read2 = readonly({refData}) // console.log(read2.refData); // 1
</script>

shallowReactive

  • 创建一个响应式代理,它跟踪其自身 property 的响应性,但不执行嵌套对象的深层响应式转换 (暴露原始值)。
<script lang='ts' setup>
import { shallowReactive } from 'vue';
const state = shallowReactive({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的性质是响应式的
state.foo++
// ...但是不转换嵌套对象
isReactive(state.nested) // false
state.nested.bar++ // 非响应式
</script>

与 reactive 不同,任何使用 ref 的 property 都不会被代理自动解包。因为ref生成的是个对象!
例如 a = shallowReactive({ b:ref('1') }) 为 a.b.value,本质是嵌套的。


shallowReadonly

  • 跟上面的一样,浅只读转换,嵌套的对象可以进行操作
const state = shallowReadonly({
  foo: 1,
  nested: {
    bar: 2
  }
})

// 改变 state 本身的 property 将失败
state.foo++
// ...但适用于嵌套对象
isReadonly(state.nested) // false
state.nested.bar++ // 适用

与 readonly 不同,任何使用 ref 的 property 都不会被代理自动解包。因为ref生成的是个对象!

例如 a = shallowReadonly({ b:ref('1') }) 为 a.b.value,本质是嵌套的。


toRaw

  • 返回 reactive 或 readonly 代理的原始对象。
  • 这是一个“逃生舱”,可用于临时读取数据而无需承担代理访问/跟踪的开销,也可用于写入数据而避免触发更改。不建议保留对原始对象的持久引用。请谨慎使用。
<script lang='ts' setup>
import { toRaw } from 'vue';
const foo = {a:111}
const reactiveFoo = reactive(foo)
console.log(toRaw(reactiveFoo) === foo,reactiveFoo===foo) // true , 代理的则为false
</script>

markRaw

  • 标记一个对象,使其永远不会转换为 proxy。返回对象本身。
  • markRaw 只作用于根级别,因此内部的子级对象会被转为响应式
    .
  • markRaw 总结: (按照重点从上到下)
    • <原则>:1、markRaw 标记一个对象,使其永远不会转换为 proxy。
    • <原则>:2、返回对象本身。
    • 3、markRaw 只作用于根级别,因此内部的子级对象会被转为响应式
    • 4、markRaw() 虽然会使普通对象永远不会转换为 proxy ,但是如果传入的本身就是proxy ,那返回的也是proxy。
<script lang='ts' setup>
import { markRaw } from 'vue';
// markRaw 标记一个对象,使其永远不会转换为 proxy。返回对象本身。嵌套多深都不会转 proxy
// const foo = markRaw({}) 
// console.log(isReactive(reactive(foo))) // false
// // const bar = reactive({ foo }) // console.log(isReactive(bar.foo)) // false

const foo = markRaw({
  nested: {}
})
// markRaw 只作用于根级别,因此内部的子级对象会被转为响应式
// bar.nested是响应式的
const bar = reactive({
  // 虽然 `foo` 被标记为原始,但 foo.nested 不是。
  nested: foo.nested
})

let asx:any = reactive({a:123})
let as1 = markRaw({}) // 普通对象
let as2 = markRaw(asx) // 原始 asx; as2 === asx > true

</script>

其他更多vue3 选项式api、组合式api、全局/应用api学习经验分享

vue3 学习资料vue3学习资料记录,经验交流、分享https://oumae-kumiko.github.io/vue3.cn.community/

上一篇:【牛客网】KY118 继续畅通工程


下一篇:DPU(Data Processing Unit)数据处理器