首页 > 编程笔记 > JavaScript笔记
阅读:21
Vue3 toRaw()和markRaw()的用法(附带实例)
在 Vue 程序中,如果我们想得到一个 reactive 对象内部包含的原始对象,就可以选择使用 toRaw() 函数。如果我们不想让一个原始对象包装生成 reactive 响应式对象,就可以选择使用 markRaw() 函数。
toRaw() 函数接收的参数为一个 reactive 对象,返回值为内部包含的整个原始对象。
markRaw() 函数接收的参数为一个原始对象,返回值还是这个原始对象,但其被添加了不能转换为 reactive 对象的标识属性 __v_skip,该值为 true。当我们将这个对象传入 reactive 函数时,返回的就不是代理对象了,而是参数对象本身。也就是说,它并不是响应式对象。
下面通过代码来演示 toRaw() 与 markRaw() 函数的使用方法:

图 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() 函数来处理这个对象后再返回它。
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() 函数来处理这个对象后再返回它。