首页 > 编程笔记 > JavaScript笔记 阅读:21

Vue3 toRaw()和markRaw()的用法(附带实例)

在 Vue 程序中,如果我们想得到一个 reactive 对象内部包含的原始对象,就可以选择使用 toRaw() 函数。如果我们不想让一个原始对象包装生成 reactive 响应式对象,就可以选择使用 markRaw() 函数。

toRaw() 函数接收的参数为一个 reactive 对象,返回值为内部包含的整个原始对象。

markRaw() 函数接收的参数为一个原始对象,返回值还是这个原始对象,但其被添加了不能转换为 reactive 对象的标识属性 __v_skip,该值为 true。当我们将这个对象传入 reactive 函数时,返回的就不是代理对象了,而是参数对象本身。也就是说,它并不是响应式对象。

下面通过代码来演示 toRaw() 与 markRaw() 函数的使用方法:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8" />
    <title>toRaw 与 markRaw 函数</title>
    <script src="https://cdn.bootcdn.net/ajax/libs/vue/3.2.40/vue.global.js"></script>
</head>
<body>
    <div id="app">
        <p>state.name: {{ state.name }}</p>
        <p>state.addr.city: {{ state.addr.city }}</p>
        <button @click="test1">测试 toRaw</button>
        <hr />
        <p>state2.name: {{ state2.name }}</p>
        <p>state2.addr.city: {{ state2.addr.city }}</p>
        <button @click="test2">测试 markRaw</button>
    </div>
    <script>
        const { createApp, reactive, toRaw, markRaw } = Vue;
        createApp({
            setup() {
                // 定义 reactive 对象
                const state = reactive({
                    name: '张三',
                    addr: {
                        city: '北京',
                    },
                });

                // 测试使用 toRaw 函数
                const test1 = () => {
                    // 通过 toRaw 函数得到 reactive 对象中包含的原始对象
                    const rawPerson = toRaw(state);
                    console.log(rawPerson);
                };

                // 原始对象
                const person2 = {
                    name: '李四',
                    addr: {
                        city: '上海',
                    },
                };

                // 标记一个原始对象不能生成 reactive 对象,并返回这个对象
                const markPerson2 = markRaw(person2);
                console.log(markPerson2); // 多了一个 __v_skip 为 true 的属性

                // 对 markRaw 函数的对象进行 reactive 处理,会被原样返回,它不是响应式的
                const state2 = reactive(markPerson2);
                console.log(state2);
                console.log(markPerson2 === person2, state2 === markPerson2);

                // 测试更新 markRaw 函数的 reactive 对象,页面不会自动更新
                const test2 = () => {
                    state2.name += '---1';
                    state2.addr.city += '---1';
                    console.log(state2.name, state2.addr.city);
                };

                return {
                    state,
                    state2,
                    test1,
                    test2,
                };
            },
        }).mount('#app');
    </script>
</body>
</html>
运行代码后的页面效果如下图所示:


图 1 运行代码后的页面效果

在上面的代码中先定义了一个 reactive 对象 state,然后在“测试toRaw”按钮的点击回调中,调用 toRaw() 函数,并传入 state 对象,得到的就是 state 对象中包含的原始对象。控制台输出如下图所示:


接着定义了一个原始对象 person2,调用 markRaw() 函数,并传入 person2 对象,返回的还是 person2 对象。只是对象被添加了 __v_skip 为 true 的属性,该属性用来标识此对象不能生成 reactive 对象。控制台输出如下图所示:


图 3 控制台输出(2)

随后调用 reactive() 函数,传入带 __v_skip 属性的对象,就不再返回一个代理对象,而是返回传入的对象本身,这就意味着此对象不能再进行响应式处理。

我们在 setup() 函数的返回对象中添加了 state2,模板可以通过 state2 读取里面任意层级的属性进行显示。但在“测试markRaw”按钮的点击回调 test2 中,通过 state2 来更新内部属性数据,页面不会自动更新,但通过控制台打印输出可以发现数据确实已经变了,如下图所示。


图 4 控制台输出(3)

这也就说明了,被 markRaw() 函数处理的对象,不能生成响应式的 reactive 对象。

那么,在什么情况下需要使用 toRaw() 与 markRaw() 函数呢?当我们想得到 reactive 对象中包含的整个原始对象时,toRaw() 函数就是一个不错的选择。当我们为其他模块提供一个包含多个数据的对象时,如果我们不需要,也不希望外部使用者将其处理为响应式对象,就可以先使用 markRaw() 函数来处理这个对象后再返回它。

相关文章