Vue中 toRaw 和 markRaw 的使用
背景
针对一些特殊的需求,在项目里,需要将响应式数据变为普通原始类型数据,这种情况是有的
在 Vue 中,能够将普通数据类型的数据变为响应式数据,也能将响应式类型数据变为普通类型数据,用于提升数据的性能
toRaw()
用于获取响应式对象的原始版本,它能够从由 reactive 或 ref 创建的响应式代理对象中提取出原始的对象或值
使用场景: 用于提取响应式对象的原始对象或值,对这个普通对象的所有操作,不会引起页面的更新
const originalObject = { name: 'Vue', version: '3.0' }
const reactiveObject = reactive(originalObject)// 获取响应式对象的原始版本
const rawObject = toRaw(reactiveObject)
console.log(rawObject === originalObject) // true
注意事项:
1)对非响应式对象无效:如果 toRaw 的参数本来就不是响应式对象,它会直接返回该参数本身
2)适用于 reactive 或 ref:可以用于由 reactive 创建的响应式对象,也可以用于由 ref 创建的响应式引用对象
3)不会解除响应式:toRaw 只是获取原始对象的引用,并不会解除响应式对象的响应性,原始对象和响应式对象仍然共享相同的内存位置
markRaw()
用于标记一个对象或组件,使其不被 Vue 的响应式系统处理。换句话说,被 markRaw 标记的对象或组件不会被 Vue 转换为响应式对象,也不会被追踪其属性的变化
将一个对象标记为不可被转为代理,返回该对象本身
应用场景:
1)某些对象或组件本身不需要响应式功能,例如第三方库的实例、静态配置对象或动态加载的组件
2)当渲染具有不可变数据源的大列表时,跳过响应式转换可以提高性能
3)某些情况下,响应式转换可能会导致意外行为,例如某些库或框架内部的状态管理
const foo = markRaw({})
console.log(isReactive(reactive(foo))) // false// 也适用于嵌套在其他响应性对象
const bar = reactive({ foo })
console.log(isReactive(bar.foo)) // false
注意事项:
1)适用范围:markRaw 只适用于对象或组件,对基本数据类型(如字符串、数字)没有效果
2)已存在的响应式对象:如果对象已经被 Vue 转换为响应式对象,markRaw 不会将其变回非响应式
对象身份风险
Vue 的响应式系统默认会将对象及其所有嵌套属性都转换为响应式对象(深度响应式),使用markRaw()
与shallowReactive()
这样浅层式 API,可以选择性地避免这种深度转换,将某些对象标记为非响应式或仅对对象的第一层属性进行响应式处理。 通过这些 API,可以在响应式状态中嵌入原始的、未被代理的对象,以满足特定的性能优化或功能需求
1)嵌套对象的代理问题
如果将一个嵌套的、没有标记为原始的对象设置为响应式对象,Vue 会将其及其嵌套对象都转换为代理对象
- 当再次访问这个嵌套对象时,获取到的是代理版本,而不是原始对象
2)对象身份不一致的问题
- 当执行依赖于对象身份的操作时,如果同时使用了同一对象的原始版本和代理版本,可能会导致问题
通过以下例子来理解:
foo 被标记为原始对象,但它的嵌套属性 nested 并没有被标记。当将 foo.nested 设置为响应式对象 bar.nested 时,foo.nested 是原始对象,而 bar.nested 是代理对象
因此,foo.nested === bar.nested 返回 false,表明它们是不同的对象实例
const foo = markRaw({nested: {}
})const bar = reactive({// 尽管 `foo` 被标记为了原始对象,但 foo.nested 却没有nested: foo.nested
})console.log(foo.nested === bar.nested) // false