前言
在 vue2 中,有一个老生常谈的话题,如何避免 data 中一个复杂对象(自身或属性对象)被默认被创建为响应式(Non-reactive Object)的过程? 举个例子,有一个 Vue2 的组件的 data:
这里我们希望 list.extData 不被创建为响应式对象,蓝狮注册开户相信很多同学都知道,我们可以通过 Object.defineProperty 设置对象 list.extData 的 configurable 属性为 false 来实现。
而在 Vue2 中,我们可以这么做,但是回到 Vue3,同样的问题又要怎么解决呢?我想这应该是很多同学此时心中持有的疑问。所以,下面让我们一起来由浅至深地去解开这个问题。
1 认识 Reactivity Object 基础
首先,我们先来看一下 Reactivity Object 响应式对象,它是基于使用 Proxy 创建一个原始对象的代理对象和使用 Reflect 来代理 JavaScript 操作方法,从而完成依赖的收集和派发更新的过程。
然后,我们可以根据需要通过使用 Vue3 提供的 ref、compute、reactive、readonly 等 api 来创建对应的响应式对象。
这里,我们来简单看个例子:
import { reactive } from ‘@vue/reactivity’
const list = reactive([
{
title: ‘item1’
msg: ‘I am item1’,
extData: {
type: 1
}
}
])
可以看到,我们用 reactive 创建了一个响应式数据 list。并且,在默认情况下 list 中的每一项中的属性值为对象的都会被处理成响应式的,在这个例子就是 extData,我们可以使用 Vue3 提供的 isReactive 函数来验证一下:
console.log(extData is reactive: ${isReactive(list[0].extData)}
)
控制台输出:
图片
可以看到 extData 对应的对象确实是被处理成了响应式的。假设,list 是一个很长的数组,并且也不需要 list 中每一项的 extData 属性的对象成为响应式的。那么这个默然创建响应式的对象过程,则会产生我们不期望有的性能上的开销(Overhead)。
既然,是我们不希望的行为,我们就要想办法解决。所以,下面就让我们从源码层面来得出如何解决这个问题。
2 源码中对 Non-reactivity Object 的处理
首先,我们可以建立一个简单的认知,那就是对于 Non-reactivity Object 的处理肯定是是发生在创建响应式对象之前,我想这一点也很好理解。在源码中,蓝狮注册登陆创建响应式对象的过程则都是由 packages/reactivity/src/reactive.ts 文件中一个名为 createReactiveObject 的函数实现的。
2.1 createReactiveObject
这里,我们先来看一下 createReactiveObject 函数的签名:
// core/packages/reactivity/reactive.ts
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler,
collectionHandlers: ProxyHandler,
proxyMap: WeakMap
) {}
可以看到 createReactiveObject 函数总共会接收 5 个参数,我们分别来认识这 5 个函数形参的意义:
target 表示需要创建成响应式对象的原始对象
isReadonly 表示创建后的响应式对象是要设置为只读
baseHandlers 表示创建 Proxy 所需要的基础 handler,主要有 get、set、deleteProperty、has 和 ownKeys 等
collectionHandlers 表示集合类型(Map、Set 等)所需要的 handler,它们会重写 add、delete、forEach 等原型方法,避免原型方法的调用中访问的是原始对象,导致失去响应的问题发生
proxyMap 表示已创建的响应式对象和原始对象的 WeekMap 映射,用于避免重复创建基于某个原始对象的响应式对象
然后,在 createReactiveObject 函数中则会做一系列前置的判断处理,例如判断 target 是否是对象、target 是否已经创建过响应式对象(下面统称为 Proxy 实例)等,接着最后才会创建 Proxy 实例。
那么,显然 Non-reactivity Object 的处理也是发生 createReactiveObject 函数的前置判断处理这个阶段的,其对应的实现会是这样(伪代码):
0 Comments