Vue 3 中的 markRaw()
函数
markRaw()
是 Vue 3 响应式系统中的一个 API,用于标记一个对象,使其永远不会被转换为响应式代理。这在您希望确保对象保持原始状态且不触发响应式更新时非常有用。
工作中使用带的例子:
chartInstance.value = markRaw(echarts.init(chartRef.value as HTMLDivElement));
为什么需要 markRaw?
ECharts 实例本身是一个复杂的对象,如果被 Vue 的响应式系统代理,可能会导致性能下降或意外行为。
基本用法
import { markRaw, reactive } from 'vue'
const original = { foo: 1 }
const reactiveObj = reactive({
nested: markRaw(original) // 标记为原始对象
})
// original 不会被转为响应式
console.log(isReactive(reactiveObj.nested)) // 输出 false
使用场景
- 性能优化:处理不需要响应式的大型对象
- 第三方库对象:处理复杂的类实例或外部库的对象
- 不可变数据:如配置对象等不应改变的数据
- 避免响应式开销:当响应式转换会造成不必要开销时
重要特性
- 被标记的对象会被 Vue 的响应式系统跳过
- 被标记对象内部的嵌套对象也不会被转换
- 一旦标记,该对象后续无法再转为响应式
- 与
reactive()
或ref()
的功能相反
组件中使用示例
import { markRaw } from 'vue'
export default {
setup() {
// 标记配置对象为非响应式
const staticConfig = markRaw({
apiUrl: 'https://api.example.com',
timeout: 5000
})
return { staticConfig }
}
}
注意事项
- 被
markRaw()
标记的对象将完全脱离 Vue 的响应式系统 - 适用于那些确实不需要响应式更新的数据
- 误用可能导致数据变化无法被视图检测到
markRaw()
的实现原理
markRaw()
是 Vue 3 响应式系统的核心 API 之一,其实现原理主要基于以下几个关键机制:
1. 标记位机制
Vue 3 内部使用 __v_skip
标记属性来标识是否跳过响应式转换:
function markRaw(value) {
// 给对象添加不可枚举的 __v_skip 属性
def(value, '__v_skip', true)
return value
}
其中 def
是 Vue 内部工具函数,用于定义不可枚举的属性:
function def(obj, key, value) {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false, // 不可枚举
value
})
}
2. 响应式创建时的检查
在创建响应式代理时(reactive()
或 ref()
),Vue 会先检查这个标记:
function createReactiveObject(target) {
// 检查 __v_skip 标记
if (target && target.__v_skip) {
return target // 直接返回原始对象
}
// ...否则继续创建响应式代理
}
3. 嵌套对象处理
对于嵌套对象的处理同样遵循这个规则:
function get(target, key) {
// 获取值
const res = Reflect.get(target, key)
// 如果值是对象且没有被标记,则递归转为响应式
if (isObject(res) && !res.__v_skip) {
return reactive(res)
}
return res
}
4. 与响应式系统集成
整个机制与 Vue 的响应式系统深度集成:
- 响应式跟踪:被标记的对象不会被 track
- 触发更新:修改被标记对象不会 trigger 更新
- 性能优化:避免了不必要的 Proxy 创建和依赖收集
设计考量
这种实现方式具有以下优点:
- 轻量级:仅添加一个隐藏属性,几乎无额外开销
- 不可枚举:不会影响对象的正常使用和序列化
- 显式控制:开发者可以精确控制哪些对象不需要响应式
- 一致性:与 Vue 的其他响应式 API 保持一致的实现风格
实际应用示例
const obj = { a: 1 }
markRaw(obj)
// 内部实际变成了:
{
a: 1,
__v_skip: true // 不可枚举属性
}