面试官:讲一下vue3.0中的toRaw、markRaw 和 customRef

Vue3中,在某些情况下,开发者可能需要对 Vue 的响应式系统进行更细粒度的控制,这时 toRaw、markRaw 和 customRef 这三个 API 就派上了用场。今天这篇文章有uncle13提供,来详细讨论一些这三个API的用法。

以下正文:

在 Vue 3.0 中,toRawmarkRawcustomRef 是与响应式系统和引用(Refs)相关的 API。下面我将分别解释这些 API 的用途和它们如何与 Vue 3.0 的响应式系统交互。

1. toRaw

toRaw 是一个用于获取响应式对象原始版本的函数。在 Vue 3 中,当你使用 reactiveref 创建一个响应式对象或值时,Vue 会将其包装在一个代理对象(Proxy)中,以追踪其变化并触发适当的更新。但是,在某些情况下,你可能需要访问这个响应式对象的原始版本,而不是代理对象。这就是 toRaw 的用途。

  • 使用场景:当你需要访问响应式对象的原始版本,而不是代理对象时,可以使用toRaw。这通常在你希望暂时读取数据而不触发代理访问/跟踪的开销,或者写入数据而不触发更改时很有用。例如,在搜索框中,你可能绑定了一个响应式变量searchValue,但有一个请求数据的方法不需要该变量的代理跟踪访问。这时,你可以使用toRaw获取searchValue的原始值,对其进行操作而不会触发数据更改时的请求方法。
import { reactive, toRaw } from 'vue'

const state = reactive({ count0 })
const rawState = toRaw(state)

console.log(rawState === state) // false

在这个例子中,rawStatestate 的原始版本,它与 state 不相等,因为 state 是一个代理对象。

2. markRaw

markRaw 是一个用于将一个对象标记为非响应式的函数。当你有一个对象,并且你知道它不需要是响应式的(例如,它来自一个第三方库或插件),但你可能仍然想将它存储在 Vue 的响应式状态中时,这可能会很有用。通过标记一个对象为非响应式,你可以避免 Vue 尝试将其转换为代理对象,从而提高性能。

  • 使用场景:当你有一个对象,并且你确定它不需要成为响应式对象时,可以使用markRaw来标记它。这在一些场景中非常有用,比如当你需要集成一个第三方库或插件,并且这个库或插件的某些部分不需要是响应性的。使用markRaw可以跳过响应式转换,从而提高性能。
import { reactive, markRaw } from 'vue'

const nonReactiveObject = markRaw({ key'value' })
const state = reactive({
  obj: nonReactiveObject
})

// state.obj 仍然是非响应式的原始对象

在这个例子中,即使 state 是响应式的,但 state.obj 仍然是非响应式的原始对象。

3. customRef

customRef 是一个用于创建自定义引用的函数。在 Vue 3 中,ref 用于创建一个响应式引用,但 ref 的行为是固定的:它存储一个值,并在该值变化时触发更新。然而,在某些情况下,你可能需要更复杂的逻辑来跟踪引用的变化。这就是 customRef 的用途。

customRef 接收一个工厂函数作为参数,该工厂函数返回一个具有 getset 方法的对象。get 方法用于返回引用的当前值,而 set 方法用于更新引用的值并触发任何必要的副作用。

  • 使用场景customRef用于创建自定义的ref,并对其依赖项跟踪和更新触发进行显式控制。这在一些需要自定义引用行为的场景中很有用,比如实现防抖函数或自定义数据验证。
import { customRef } from 'vue'

function myCustomRef(value{
  let trigger = null
  const setValue = (newValue) => {
    if (value !== newValue) {
      value = newValue
      trigger()
    }
  }
  return customRef((track, triggerValue) => {
    trigger = triggerValue
    return value
  }, setValue)
}

const count = myCustomRef(0)

// 使用 count.value 读取和设置值,就像使用普通的 ref 一样
console.log(count.value) // 0
count.value = 1
console.log(count.value) // 1

在这个例子中,myCustomRef 创建了一个自定义引用,它允许你使用 count.value 来读取和设置值,但值的更新逻辑是自定义的。在这个简单的例子中,它只是简单地比较新旧值并在它们不同时更新值,但你可以根据需要在 setValue 函数中添加任何你想要的逻辑。

在一个复杂的Vue 3项目中,假设我们正在开发一个实时数据可视化应用,其中涉及到大量的数据处理和图形渲染。在这个场景中,我们可能会使用toRawmarkRawcustomRef来优化性能和自定义响应式行为。

项目场景

假设我们的应用有一个“实时数据流”组件,该组件负责接收来自服务器的实时数据,并在一个图表上进行展示。数据流的更新频率非常高,每次更新都可能导致图表的重绘。然而,由于数据处理的复杂性,我们不希望每次数据更新都立即触发图表的重新渲染,因为这可能会导致性能问题。

解决方案

  1. 使用toRaw读取原始数据

在数据流组件中,我们可能需要处理大量的实时数据,并对其进行一些预处理。由于这些处理操作不需要触发响应式更新,我们可以使用toRaw来获取响应式数据的原始版本,从而避免不必要的代理访问和跟踪开销。

import { reactive, toRaw } from 'vue';

// 假设我们有一个响应式的数据流状态
const dataStream = reactive({
  latestDatanull,
});

// 使用toRaw获取原始数据以进行预处理
const rawData = toRaw(dataStream.latestData);
// 进行一些复杂的预处理操作...
  1. 使用markRaw标记非响应式数据

在数据流组件中,我们可能还需要使用一些第三方库来处理数据或生成图表。这些库的数据通常不需要是响应式的,因为它们自己管理数据的更新和渲染。为了避免Vue尝试将这些数据转换为响应式对象,我们可以使用markRaw来标记它们。

import { markRaw } from 'vue';
import { SomeChartLibrary } from 'some-chart-library';

// 假设我们有一个从服务器获取的数据对象
const serverData = fetchDataFromServer(); // 返回一个普通对象

// 使用markRaw标记该对象为非响应式
const nonReactiveData = markRaw(serverData);

// 使用第三方库生成图表
const chart = new SomeChartLibrary(nonReactiveData);
  1. 使用customRef实现防抖功能

在实时数据流组件中,为了避免频繁的数据更新导致图表频繁重绘,我们可以使用customRef来实现一个防抖(debounce)功能。防抖功能可以确保在一段时间内多次触发数据更新时,只执行一次图表重绘操作。

import { customRef, ref } from 'vue';

// 自定义一个防抖ref
function debouncedRef(value, delay = 200{
  let timeout;
  return customRef((track, trigger) => {
    return {
      get() {
        track();
        return value;
      },
      set(newValue) {
        clearTimeout(timeout);
        timeout = setTimeout(() => {
          value = newValue;
          trigger();
        }, delay);
      }
    };
  });
}

// 在数据流组件中使用防抖ref
const debouncedData = debouncedRef(null500); // 设置防抖延迟为500毫秒

// 当数据更新时,更新防抖ref的值
dataStream.latestData = newDataFromServer; // 假设这是从服务器获取的新数据
debouncedData.value = dataStream.latestData; // 更新防抖ref的值,但不会立即触发图表重绘

// 在图表组件中,使用debouncedData作为数据源来绘制图表
// 由于防抖功能的作用,图表只会在一段时间内数据稳定后才进行重绘

通过上述方案,我们可以利用toRawmarkRawcustomRef来优化实时数据流组件的性能和响应式行为,从而提高应用的用户体验。

在Vue 3中,toRawmarkRawcustomRef都是非常有用的API,它们提供了对Vue响应式系统的更细粒度的控制,以便在特定场景下优化性能或实现自定义行为。

总结

  1. toRaw

    • 功能:用于获取响应式对象的原始版本,避免不必要的代理访问和跟踪开销。
    • 使用场景:当需要对响应式数据进行大量计算或处理,而这些操作不需要触发Vue的响应式更新时。
  2. markRaw

    • 功能:将一个对象标记为非响应式,Vue将不会对其进行响应式转换。
    • 使用场景:当使用不需要响应式特性的外部库或插件时,或者当对象明确不需要是响应式时。
  3. customRef

    • 功能:用于创建自定义的ref,可以控制其依赖项跟踪和更新触发。
    • 使用场景:当需要自定义引用行为时,比如实现防抖、节流、异步更新或其他复杂的响应式逻辑。

注意事项

  • 在使用这些API时,需要确保理解Vue的响应式原理,以避免潜在的错误或性能问题。
  • 谨慎使用toRawmarkRaw,确保不会意外地破坏了Vue的响应式系统。
  • customRef提供了很大的灵活性,但也需要更加小心地管理依赖项跟踪和更新触发,以避免意外的副作用。


最后

还没有使用过我们刷题网站(https://fe.ecool.fun/)或者刷题小程序的同学,如果近期准备或者正在找工作,千万不要错过,题库主打无广告和更新快哦~。

老规矩,也给我们团队的辅导服务打个广告。