markRaw 用于显式标记对象永不转为响应式,避免 Vue 响应式系统干扰第三方库(如 ECharts、Three.js)的自有状态管理或引用判断逻辑。

在 Vue 3 中,markRaw 是一个关键的底层 API,用于显式标记一个对象“永不转为响应式”,从而跳过 Vue 的响应式系统劫持(如 Proxy 包装)。它不是用来“提升性能”的通用优化手段,而是在**集成第三方库(尤其是依赖原始引用、内部状态或自有响应机制的库)时,避免 Vue 响应式干扰的必要措施**。
什么情况下必须用 markRaw?
当第三方对象或实例本身具有以下特征时,Vue 默认将其转为响应式反而会破坏其行为:
- 自身已具备状态管理逻辑:例如 Map、Set、Date、RegExp、CanvasRenderingContext2D 等原生类实例,或像 D3、Three.js、ECharts 实例等;
-
依赖严格的身份引用(identity):比如某些库通过
===判断是否是同一实例,而响应式代理会改变引用; - 内部持有不可被 Proxy 正确拦截的方法或属性:如访问器属性(getter/setter)、Symbol 键、原型链方法调用等;
- 需要直接修改其属性且不触发 Vue 更新:比如渲染上下文、WebGL 状态对象等,频繁响应式触发更新毫无意义甚至引发错误。
常见集成场景与写法
以 ECharts 为例(典型需 markRaw 的第三方可视化库):
✅ 正确做法:在 setup 中创建实例后立即 markRaw
立即学习“前端免费学习笔记(深入)”;
const chartRef = ref(null)const echartsInstance = markRaw(echarts.init(chartRef.value))
❌ 错误做法:让 Vue 尝试代理实例
const echartsInstance = ref(echarts.init(chartRef.value)) // 触发 Proxy 包装,可能报错或失灵其他典型场景还包括:
• 使用 new THREE.Scene() / renderer 等 Three.js 对象
• 保存 ctx = canvas.getContext('2d') 渲染上下文
• 封装 WebSocket 实例并手动管理消息收发
markRaw 不是性能银弹
它不会让对象“更快”,只是跳过响应式初始化开销。对普通数据对象滥用 markRaw 反而会导致响应失效——比如你 markRaw 了一个配置对象,又想在模板中用 {{ config.title }} 动态更新,那就不会响应了。
判断依据很简单:这个对象你是否希望它的变化驱动视图更新?如果否,且它来自外部库,就 markRaw;如果是业务状态数据,不要用。
配合 shallowRef 和 shallowReactive 更精准控制
当需要部分跳过响应式时,可组合使用:
-
shallowRef:只对 .value 做响应式,内部值不递归代理 → 适合存大型、无需深层响应的对象(如整个图表配置); -
shallowReactive:仅代理第一层属性 → 适合浅层结构明确、深层不需响应的场景; -
markRaw+ref:最彻底地排除响应式 → 适合完全交由外部库管理的对象。
三者不是替代关系,而是按需选择:目标越明确,响应式边界越清晰,集成越稳健。










