Vue3中toRaw和MarkRaw

Vue3中toRaw和MarkRaw

一、这两个是什么东西?

在vue3中,响应式系统是通过proxy,它可以把一个普通对象变成一个响应式对象,这样数据变化的时候视图也会跟着变化;但是可能有的时候,我们并不希望一些对象被Proxy对象包装,我们只需要原始对象,而toRaw和MarkRaw就是做这个事情的;

二、toRaw获取原始对象

toRaw接受一个ref、reactive定义的响应式对象,返回其对象原始本身,如果传入的不是响应式对象,就会返回本身;

vue 复制代码
<template>
  <h3>{{ person }}</h3>
  <button @click="changePerson2Age">修改原始对象age</button>
</template>

<script setup>
import { arEg } from 'element-plus/es/locale/index.mjs';
import { reactive, toRaw } from 'vue';

const person = reactive({
  name: '张三',
  age: 18
})

const person2 = toRaw(person)

function changePerson2Age() {
  console.log(person2);
  person2.age += 1
}
</script>
使用场景

当你需要修改也给大对象的时候,不要频繁的更新视图,以此来减少开销,那么你就可以使用它;

这里假设我们又10000条记录的大表格,当用户点击"批量更新"的时候,你需要修改其5000条数据。如果每次修改数据都触发更新,界面就会卡顿。这时候我们可以使用toRaw直接修改原始数据,最后在整体替换

vue 复制代码
<template>
  <h3>{{ tableData }}</h3>
  <button @click="batchUpdata">触发更新</button>
</template>

<script setup>
import { reactive, toRaw } from 'vue';

const tableData = reactive([{ id: 1, name: 'Alice', score: 50 }, { id: 2, name: 'Bob', score: 92 }])

function batchUpdata() {
  // 1. 获取原始数据(注意:数组内的对象仍是响应式代理)
  const rawArray = toRaw(tableData);

  // 2. 深拷贝一份干净的数据(避免直接修改响应式对象)
  const newData = rawArray.map(item => ({ ...item })); // 浅拷贝已足够(本例中对象只有一层)

  // 3. 批量修改新数据
  for (let i = 0; i < newData.length; i++) {
    if (newData[i].score < 60) {
      newData[i].score += 10;
    }
  }

  // 4. 整体替换响应式数组(仅触发一次视图更新)
  tableData.length = 0;
  tableData.push(...newData);
}

</script>

三、markRaw------ 标记为永远不响应式

markRaw 返回对象本身,并给它添加一个内部标记,告诉 Vue 永远不要把这个对象转换成响应式对象。即使你把它放到 reactiveref 中,它也不会被包装。

这个在之前已经说过了,这里再举一个现实世界示例

例如一些地图实例、图标实例,一般这些对象内部状态非常复杂,Vue完全不需要去跟踪它们

假设你在组件中使用 ECharts 创建一个图表,ECharts 实例有很多内部属性和方法,Vue 完全不需要对这些属性做响应式处理。如果意外地将它变成了响应式,不仅性能差,还可能引发错误。

vue 复制代码
<template>
  <div class="chart-container">
    <!-- 图表容器,ref 用于获取 DOM 元素 -->
    <div ref="chartRef" class="chart"></div>

    <!-- 交互按钮:更新图表数据 -->
    <div class="controls">
      <button @click="updateChartData">更新数据(随机)</button>
      <button @click="resetChartData">重置数据</button>
    </div>
  </div>
</template>

<script setup>
import { markRaw, onMounted, onBeforeUnmount, ref } from 'vue'
import * as echarts from 'echarts'

// 1. 获取图表容器的 DOM 引用
const chartRef = ref(null)

// 2. 存储 ECharts 实例(使用 markRaw 防止被 Vue 响应化)
const chartInstance = ref(null)

// 3. 定义初始数据(也可以作为响应式数据,但图表更新直接调用实例方法)
const defaultData = [120, 200, 150, 80]
const currentData = ref([...defaultData])  // 仅用于展示,实际更新图表时直接操作实例

// 4. 更新图表数据的方法
function updateChart(newData) {
  if (!chartInstance.value) return
  // 直接调用 ECharts 实例的 setOption,不会触发 Vue 的响应式系统
  chartInstance.value.setOption({
    series: [{ data: newData }]
  })
  // 同步更新响应式数据(可选,如果需要在其他地方展示当前数据)
  currentData.value = [...newData]
}

// 随机生成数据
function updateChartData() {
  const randomData = Array.from({ length: 4 }, () => Math.floor(Math.random() * 300))
  updateChart(randomData)
}

function resetChartData() {
  updateChart(defaultData)
}

// 窗口尺寸变化时,调用 echarts 的 resize 方法使图表自适应
function handleResize() {
  if (chartInstance.value) {
    chartInstance.value.resize()
  }
}

// 5. 生命周期:挂载完成后初始化图表
onMounted(() => {
  if (chartRef.value) {
    // 创建 ECharts 实例
    const instance = echarts.init(chartRef.value)

    // 使用 markRaw 标记,避免 Vue 将其转换为响应式对象
    chartInstance.value = markRaw(instance)

    // 配置图表
    instance.setOption({
      title: {
        text: '销售趋势',
        left: 'center'
      },
      tooltip: {
        trigger: 'axis'
      },
      xAxis: {
        type: 'category',
        data: ['Q1', 'Q2', 'Q3', 'Q4']
      },
      yAxis: {
        type: 'value'
      },
      series: [
        {
          name: '销售额',
          type: 'line',
          data: defaultData,
          smooth: true,
          lineStyle: {
            width: 3,
            color: '#42b983'
          },
          areaStyle: {
            opacity: 0.1,
            color: '#42b983'
          }
        }
      ]
    })

    // 添加窗口 resize 监听
    window.addEventListener('resize', handleResize)
  }
})

// 6. 组件卸载前销毁图表实例并移除事件监听
onBeforeUnmount(() => {
  window.removeEventListener('resize', handleResize)
  if (chartInstance.value) {
    chartInstance.value.dispose()   // 释放 ECharts 实例占用的内存
    chartInstance.value = null
  }
})
</script>

<style scoped>
.chart-container {
  width: 100%;
  padding: 20px;
  box-sizing: border-box;
  background: #f5f7fa;
  border-radius: 8px;
}

.chart {
  width: 100%;
  height: 400px;
  background: white;
  border-radius: 8px;
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}

.controls {
  margin-top: 20px;
  text-align: center;
}

.controls button {
  margin: 0 10px;
  padding: 8px 20px;
  font-size: 14px;
  border: none;
  border-radius: 4px;
  cursor: pointer;
  transition: all 0.3s;
  background-color: #42b983;
  color: white;
}

.controls button:hover {
  background-color: #33a06f;
  transform: translateY(-1px);
}
</style>
相关推荐
郭wes代码2 小时前
2026年前端面试题及干货
javascript
时寒的笔记2 小时前
逆向入门05_yi恩网
javascript
李松桃2 小时前
01HTML-CSS-入门知识点
前端·css
广州华水科技2 小时前
北斗GNSS变形监测系统是什么?主要有哪几种应用?
前端
晴天162 小时前
【跨桌面应用开发】Neutralinojs快速入门指南
前端·javascript·electron·node.js
爱学习的程序媛3 小时前
【Web前端】深入解析JavaScript异步编程
开发语言·前端·javascript·ecmascript·web
梧桐1683 小时前
马克沁机枪上阵(二):前线开辟—Claude Code 如何用一天打通前端
前端
是上好佳佳佳呀3 小时前
【前端(一)】HTML 知识梳理:从结构到常用标签
前端·html
楚轩努力变强3 小时前
2026 年前端进阶:端侧大模型 + WebGPU,从零打造高性能 AI 原生前端应用
前端·typescript·大模型·react·webgpu·ai原生·高性能前端