foreword
In Vue2, there is a clichéd topic, how to avoid data
the process of a complex object (self or property object) being created as a non-reactive object by default? For example, there is a Vue2 component data
:
<script>
export default {
data() {
return {
list: [
{
title: 'item1'
msg: 'I am item1',
extData: {
type: 1
}
},
...
]
}
}
}
</script>
这里list.extData
不被创建为响应式的对象,相信很多同学都知道,我们可以通过Object.defineProperty
list.extData
的configurable
attribute is false
to achieve.
In Vue2, we can do this, but back to Vue3, how to solve the same problem? I think this should be the question that many students have in mind at this time. So, let us solve this problem from shallow to deep.
1 Know the basics of Reactivity Object
First, let's take a look at the Reactivity Object responsive object, which is based on the use of Proxy
to create a proxy object of the original object and the use of Reflect
to proxy the JavaScript operation method to complete the dependency. The process of collecting and distributing updates.
然后,我们可以根据需要通过使用Vue3 提供的ref
、 compute
、 reactive
、 readonly
API 来创建对应的响应式object.
Here, let's look at a simple example:
import { reactive } from '@vue/reactivity'
const list = reactive([
{
title: 'item1'
msg: 'I am item1',
extData: {
type: 1
}
}
])
As you can see, we created a reactive data list
reactive
And, by default list
the property value in each item will be processed as responsive , in this example is extData
, we can use Vue3 to provide The isReactive
function to verify it:
console.log(`extData is reactive: ${isReactive(list[0].extData)}`)
// 输出 true
Console output:
It can be seen that the corresponding object extData
is indeed processed as responsive. Suppose, list
is a very long array, and there is no need for the list
extData
property of each item in ---ec064c38e69aab56c5a6ce5a207d7b30--- to be reactive. Then the process of silently creating a reactive object will generate an unexpected performance overhead (Overhead) .
Since it is the behavior we do not want, we must find a way to solve it. So, let's figure out how to solve this problem from the source code level.
2 Processing of Non-reactivity Object in source code
First of all, we can establish a simple understanding that the processing of Non-reactivity Object must happen before the reactive object is created , I think this is also well understood. In the source code, the process of creating a responsive object is implemented by a function named createReactiveObject
in the packages/reactivity/src/reactive.ts
file.
2.1 createReactiveObject
Here, let's take a look at the signature of the createReactiveObject
function:
// core/packages/reactivity/reactive.ts
function createReactiveObject(
target: Target,
isReadonly: boolean,
baseHandlers: ProxyHandler<any>,
collectionHandlers: ProxyHandler<any>,
proxyMap: WeakMap<Target, any>
) {}
It can be seen that createReactiveObject
the function will receive a total of 5 parameters. Let's understand the meanings of these 5 function parameters:
-
target
represents the original object that needs to be created as a responsive object -
isReadonly
indicates that the created responsive object is to be set to read-only -
baseHandlers
Proxy
所需要的基础handler
,主要有get
、set
、deleteProperty
,has
andownKeys
etc. -
collectionHandlers
类型(Map
、Set
)所handler
,add
,delete
,forEach
and other prototype methods to avoid the original object being accessed in the invocation of the prototype method , resulting in the problem of unresponsiveness -
proxyMap
WeekMap
mapping representing the created responsive object and the original object, to avoid duplicating the creation of a responsive object based on an original object
Then, in the createReactiveObject
function, a series of pre-judgment processing will be done, such as judging whether target
is an object, target
whether a responsive object has been created (hereinafter collectively referred to as Proxy
instance), etc., and then finally create the Proxy
instance.
Then, obviously the processing of Non-reactivity Object also happens createReactiveObject
The pre-judgment processing of the function is at this stage, and its corresponding implementation will be like this (pseudo code):
// core/packages/reactivity/src/reactive.ts
function createReactiveObject(...) {
// ...
const targetType = getTargetType(target)
if (targetType === TargetType.INVALID) {
return target
}
// ...
}
It can be seen that as long as the getTargetType
function is used to obtain the incoming target
type targetType
, it will directly return the original object of TargetType.INVALID
target
, that is, the subsequent responsive object creation process will not be performed .
So, at this time, I think everyone will have 2 questions:
-
getTargetType
What does the function do? -
TargetType.INVALID
What does this mean, the meaning of this enumeration?
Now, let us solve these two questions one by one.
2.2 getTargetType and targetType
Likewise, let's first look at the implementation of the getTargetType
function:
// core/packages/reactivity/src/reactive.ts
function getTargetType(value: Target) {
return value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
? TargetType.INVALID
: targetTypeMap(toRawType(value))
}
Among them getTargetType
mainly do these 3 things:
- Judging that
target
exists on theReactiveFlags.SKIP
attribute, which is a string enumeration with a value of__v_ship
, and returnsTargetType.INVALID
if it exists -
target
f7df43b9b83252af7103749627e1d6cb---是否可扩展Object.isExtensible
返回true
false
,true
返回TargetType.INVALID
- When the above two conditions are not met, return
targetTypeMap(toRawType(value))
target
1、2 点可以得出,只要你在传入的---238bda88476cf77c5099f2a5674744a6---上设置了__v_ship
e34035aee18a77726dd62ff214979444---属性、或者使用Object.preventExtensions
、 Object.freeze
、 Object.seal
target
扩展,那么target
对应的响应式对象,即直接返回TargetType.INVALID
( TargetType
is a numeric enumeration, which will be introduced later).
In our example above is to set extData
:
{
type: 1,
__v_ship: true
}
or:
Object.freeze({
type: 1
})
Then, if the first and second points are not satisfied, it will return targetTypeMap(toRawType(value))
, of which toRawType
function is based on the package of Object.prototype.toString.call
, and it finally It will return a specific data type, for example, an object will return Object
:
// core/packages/shared/src/index.ts
const toRawType = (value: unknown): string => {
// 等于 Object.prototype.toString.call(value).slice(8, -1)
return toTypeString(value).slice(8, -1)
}
Then, followed by the targetTypeMap
function:
// core/packages/reactivity/src/reactive.ts
function targetTypeMap(rawType: string) {
switch (rawType) {
case 'Object':
case 'Array':
return TargetType.COMMON
case 'Map':
case 'Set':
case 'WeakMap':
case 'WeakSet':
return TargetType.COLLECTION
default:
return TargetType.INVALID
}
}
It can be seen that the targetTypeMap
function actually makes three classifications of the data types we know:
-
TargetType.COMMON
represents objectObject
, arrayArray
-
TargetType.COLLECTION
类型,Map
、Set
、WeakMap
、WeakSet
-
TargetType.INVALID
indicates an illegal type, not an object, an array, or a collection
Among them, TargetType
corresponds to the enumeration implementation:
const enum TargetType {
INVALID = 0,
COMMON = 1,
COLLECTION = 2
}
那么,回到我们上面的这个例子, list.extData
4e9ea03a10ff7e1aa02068279648d211---在toRawType
函数中返回的Array
, targetTypeMap
函数返回The type will be TargetType.COMMON
(not equal to TargetType.INVALID
), which will eventually create a reactive object for it.
So here we can draw a conclusion that if we need to skip the process of creating a reactive object, we must let target
satisfy value[ReactiveFlags.SKIP] || !Object.isExtensible(value)
or hit targetTypeMap
The default
logic in the function.
Epilogue
After reading this, I think everyone understands how to skip the process of creating a responsive object for some nested objects in the object when creating a responsive object of a complex object. Moreover, this little trick is undeniably a good optimization method in some scenarios, so it is also very important to do the necessary cognition in advance.
Finally, if there are inappropriate expressions or mistakes in the text, you are welcome to raise an Issue~
like
By reading this article, if you have gained something, you can give a like, this will become the motivation for me to continue to share, thank you~
I am Wuliu. I like to innovate and fiddle with source code. I focus on learning and sharing technologies such as source code (Vue 3, Vite), front-end engineering, and cross-end. Welcome to my WeChat public account Code center or GitHub .
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。