说说对Vue3 中 markRaw 的理解 ?

491 阅读3分钟

markRaw() 是 Vue 3 提供的一个 响应性系统工具函数,它的作用是:

👉 标记一个对象为 “不可被 Vue 响应式系统追踪” 的原始对象

使用 markRaw(obj) 后,这个对象就不会再被 reactive() 或 ref() 转换为响应式对象了。

例子

import { reactive, markRaw } from 'vue'

const myChart = markRaw(new EChartsInstance()) // 不需要被追踪

const state = reactive({
  count: 0,
  chart: myChart
})

为什么要这么做?🔍

如果你把一个 第三方库对象(比如 ECharts、Mapbox、Three.js 实例等)交给 Vue 的响应式系统去追踪,会导致:

  • 不必要的性能开销;
  • 某些库对象内部是非响应式友好的(含闭包、DOM 引用等),会报错或异常。

所以用 markRaw() 标记后,Vue 就 不再试图将它转换为响应式对象,你可以正常使用它而不引发副作用。


和 shallowReactive()的区别📌

特性markRawshallowReactive
作用范围完全不响应式只对顶层属性响应式
返回值原对象本身新的响应式代理对象
使用场景第三方实例 / DOM层级很浅的状态对象
能否被嵌套追踪❌ 永远不会✅ 嵌套层级不可追踪,但顶层可响应

源码分析(简化版)🧬

markRaw 的实现非常简单:

const RAW_FLAG = '__v_skip'

export function markRaw<T extends object>(value: T): T {
  def(value, RAW_FLAG, true)
  return value
}

它就是往对象上加一个 __v_skip 的标记,告诉 Vue 的响应式系统 跳过这个对象

function isRaw(obj) {
  return obj && obj.__v_skip === true
}

在执行 reactive(obj) 时,Vue 会检查这个标记,如果存在就跳过。


📦 常见使用场景

场景示例
📊 图表库实例ECharts、Highcharts、Chart.js
🗺️ 地图库对象Leaflet、Mapbox、Cesium
🎮 3D 渲染对象Three.js、Babylon.js
📦 大型数据缓存对象不需要 UI 响应的大对象缓存
🧱 DOM 元素markRaw(document.createElement('div'))

如果你在项目中使用到了 ECharts、音频 API 或者 Bluetooth 的实例对象,那一定要记得 markRaw 保护它们不被 Vue 干扰! shallowReactive() 是 Vue 3 中提供的一个创建“浅层响应式”对象的方法。它只会让对象的最外层属性变成响应式,但内部嵌套对象不会递归转为响应式


shallowReactive 🧠

shallowReactive:只追踪外层属性的响应式状态,内层对象保持原样(非响应式)


示例一:避免嵌套结构递归转响应式📦

import { shallowReactive } from 'vue'

const state = shallowReactive({
  count: 0,
  info: {
    name: 'Vue',
    age: 3
  }
})

// ✅ 外层 count 是响应式的
state.count++

// ❌ 内层 info.name 不是响应式的
state.info.name = 'Vue 3'
// 这个不会触发视图更新!

所以对 state.info.name 的修改,不会被 Vue 追踪到。只对 state.info = {} 这样的顶层替换才有效。

示例二:配合第三方库(比如 DOM)📦

const state = shallowReactive({
  el: document.createElement('div')
})

// el 是 DOM 元素,Vue 不会对它做响应式转换,防止副作用

示例三:避免不必要的性能开销 📦

const config = shallowReactive({
  // 大型配置对象,不需要深层响应式
  settings: {
    theme: 'dark',
    layout: 'grid',
    data: new Array(10000).fill(0)
  }
})

// 只关心 settings 这块被整体替换,不关心它里面的数据项是否响应式

🚫 注意事项:

  • 它不会深度追踪属性变更,比如数组项、对象属性更新;
  • 对象嵌套较深时,不要误以为所有属性都响应式;
  • 若需要深层追踪,请使用 reactive()。