markRaw 包裹引入的组件,是为了什么?

763 阅读4分钟

markRaw 的作用

在 Vue 中,markRaw 是一个函数,用于将一个对象标记为 “原始对象”,这意味着 Vue 将不会对该对象进行响应式转换。

使用场景

  1. 当你有一些对象,不希望 Vue 将其变成响应式对象时,可以使用 markRaw。例如,某些第三方库的实例或非常大的数据集,将其变成响应式对象会带来性能开销,此时可以使用 markRaw 来避免这种情况。
  2. 在某些情况下,你可能需要在 Vue 组件中存储一些非响应式的数据,这些数据不会影响视图的渲染,也不需要触发 Vue 的响应式更新机制,那么 markRaw 是一个很好的选择。

示例代码

<template>
  <div>
    <p>{{ rawObj.message }}</p>
    <button @click="updateObj">Update</button>
  </div>
</template>

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

export default {
  setup() {
    // 使用 markRaw 标记对象为原始对象
    const rawObj = markRaw({ message: 'Hello, World' });
    const reactiveObj = reactive({ message: 'Hello, Vue' });

    const updateObj = () => {
      // 修改原始对象的属性
      rawObj.message = 'Updated raw message';
      // 修改响应式对象的属性
      reactiveObj.message = 'Updated reactive message';
    };

    return {
      rawObj,
      reactiveObj,
      updateObj
    };
  }
};
</script>

代码解释

  • 首先,我们从 Vue 中导入 markRaw 和 reactive 函数。
  • 在 setup 函数中,使用 markRaw({ message: 'Hello, World' }) 创建了一个原始对象 rawObj
  • 同时,使用 reactive({ message: 'Hello, Vue' }) 创建了一个响应式对象 reactiveObj
  • updateObj 函数用于修改 rawObj 和 reactiveObj 的 message 属性。
  • 当点击按钮调用 updateObj 函数时,会发现修改 reactiveObj 的属性会触发 Vue 的响应式更新,从而更新视图中显示的 reactiveObj.message 的值;而修改 rawObj 的属性不会触发 Vue 的响应式更新,因为它被标记为原始对象,所以视图中显示的 rawObj.message 的值不会自动更新。

注意事项

  • 一旦一个对象被标记为 markRaw,它将始终是非响应式的,不能再通过 Vue 的响应式 API 使其变为响应式。
  • 对于大型数据集合或不需要响应式的对象,使用 markRaw 可以提高性能,但需要谨慎使用,确保不会在需要响应式的地方使用它,以免导致意外的行为。

markRaw 包裹引入的组件

在 Vue 中,将引入的组件用 markRaw 包裹通常是为了避免 Vue 将该组件实例转换为响应式对象。这主要基于以下几个方面的考虑:

性能优化

  • Vue 的响应式系统会对数据对象进行代理,以实现数据的响应式更新。对于组件实例来说,将其变成响应式对象可能会带来额外的性能开销,特别是对于一些复杂的组件。使用 markRaw 包裹组件实例可以避免 Vue 对其进行响应式处理,从而提高性能。

避免不必要的响应式更新

  • 有些组件在使用过程中并不需要响应式更新。例如,某些静态组件,它们的状态不会发生改变,或者它们的状态改变不会影响到父组件或其他相关组件的渲染逻辑。将这样的组件用 markRaw 包裹可以确保 Vue 不会对其内部的状态变化进行监听和响应式处理,避免不必要的渲染和性能消耗。

使用场景示例
假设你有一个自定义组件 MyComponent,你可能会在某些情况下这样使用 markRaw

<template>
  <div>
    <component :is="componentInstance" />
  </div>
</template>

<script>
import { markRaw, ref } from 'vue';
import MyComponent from './MyComponent.vue';

export default {
  setup() {
    // 将组件实例标记为原始对象,避免 Vue 对其进行响应式处理
    const componentInstance = markRaw(MyComponent);
    return {
      componentInstance
    };
  }
};
</script>

代码解释

  • 从 Vue 中导入 markRaw 和 ref 函数,并引入 MyComponent.vue 组件。
  • 在 setup 函数中,使用 markRaw(MyComponent) 将 MyComponent 组件实例标记为原始对象,得到 componentInstance
  • 在模板中使用动态组件 <component :is="componentInstance" /> 来渲染这个组件。
  • 因为 componentInstance 被标记为原始对象,Vue 不会对其进行响应式处理,所以在该组件实例的状态发生变化时,Vue 不会触发额外的响应式更新逻辑,这在一些场景下可以优化性能和避免不必要的更新。

注意事项

  • 要谨慎使用 markRaw 包裹组件,确保组件确实不需要 Vue 的响应式更新,否则可能会导致组件的状态改变无法正确反映在视图上。
  • 对于大多数常规组件,通常不需要使用 markRaw 包裹,因为 Vue 的响应式系统可以很好地处理它们的状态更新和渲染。只有在明确知道组件的状态更新不应该触发响应式更新时,才考虑使用 markRaw 包裹组件。