Skip to content

Commit eae7c24

Browse files
committed
refactor: reduce bundle size
1 parent 6cf2377 commit eae7c24

File tree

3 files changed

+109
-139
lines changed

3 files changed

+109
-139
lines changed

packages/reactivity/src/Dep.ts

+44-38
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { ReactiveEffect, getTrackOpBit } from './effect'
1+
import { ReactiveEffect, trackOpBit } from './effect'
22

33
export type Dep = Set<ReactiveEffect> & TrackedMarkers
44

@@ -7,45 +7,51 @@ export type Dep = Set<ReactiveEffect> & TrackedMarkers
77
* tracking recursion. One bit per level is used to define wheter the dependency
88
* was/is tracked.
99
*/
10-
type TrackedMarkers = { wasTracked: number; newTracked: number }
11-
12-
export function createDep(effects?: ReactiveEffect[]): Dep {
10+
type TrackedMarkers = {
11+
/**
12+
* wasTracked
13+
*/
14+
w: number
15+
/**
16+
* newTracked
17+
*/
18+
n: number
19+
}
20+
21+
export const createDep = (effects?: ReactiveEffect[]): Dep => {
1322
const dep = new Set<ReactiveEffect>(effects) as Dep
14-
dep.wasTracked = 0
15-
dep.newTracked = 0
23+
dep.w = 0
24+
dep.n = 0
1625
return dep
1726
}
1827

19-
export function wasTracked(dep: Dep): boolean {
20-
return hasBit(dep.wasTracked, getTrackOpBit())
21-
}
22-
23-
export function newTracked(dep: Dep): boolean {
24-
return hasBit(dep.newTracked, getTrackOpBit())
25-
}
26-
27-
export function setWasTracked(dep: Dep) {
28-
dep.wasTracked = setBit(dep.wasTracked, getTrackOpBit())
29-
}
30-
31-
export function setNewTracked(dep: Dep) {
32-
dep.newTracked = setBit(dep.newTracked, getTrackOpBit())
33-
}
34-
35-
export function resetTracked(dep: Dep) {
36-
const trackOpBit = getTrackOpBit()
37-
dep.wasTracked = clearBit(dep.wasTracked, trackOpBit)
38-
dep.newTracked = clearBit(dep.newTracked, trackOpBit)
39-
}
40-
41-
function hasBit(value: number, bit: number): boolean {
42-
return (value & bit) > 0
43-
}
44-
45-
function setBit(value: number, bit: number): number {
46-
return value | bit
47-
}
48-
49-
function clearBit(value: number, bit: number): number {
50-
return value & ~bit
28+
export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0
29+
30+
export const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0
31+
32+
export const initDepMarkers = ({ deps }: ReactiveEffect) => {
33+
if (deps.length) {
34+
for (let i = 0; i < deps.length; i++) {
35+
deps[i].w |= trackOpBit // set was tracked
36+
}
37+
}
38+
}
39+
40+
export const finalizeDepMarkers = (effect: ReactiveEffect) => {
41+
const { deps } = effect
42+
if (deps.length) {
43+
let ptr = 0
44+
for (let i = 0; i < deps.length; i++) {
45+
const dep = deps[i]
46+
if (wasTracked(dep) && !newTracked(dep)) {
47+
dep.delete(effect)
48+
} else {
49+
deps[ptr++] = dep
50+
}
51+
// clear bits
52+
dep.w &= ~trackOpBit
53+
dep.n &= ~trackOpBit
54+
}
55+
deps.length = ptr
56+
}
5157
}

packages/reactivity/src/effect.ts

+33-60
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@ import { EffectScope, recordEffectScope } from './effectScope'
44
import {
55
createDep,
66
Dep,
7+
finalizeDepMarkers,
8+
initDepMarkers,
79
newTracked,
8-
resetTracked,
9-
setNewTracked,
10-
setWasTracked,
1110
wasTracked
1211
} from './Dep'
1312

@@ -18,6 +17,18 @@ import {
1817
type KeyToDepMap = Map<any, Dep>
1918
const targetMap = new WeakMap<any, KeyToDepMap>()
2019

20+
// The number of effects currently being tracked recursively.
21+
let effectTrackDepth = 0
22+
23+
export let trackOpBit = 1
24+
25+
/**
26+
* The bitwise track markers support at most 30 levels op recursion.
27+
* This value is chosen to enable modern JS engines to use a SMI on all platforms.
28+
* When recursion depth is greater, fall back to using a full cleanup.
29+
*/
30+
const maxMarkerBits = 30
31+
2132
export type EffectScheduler = () => void
2233

2334
export type DebuggerEvent = {
@@ -38,6 +49,7 @@ let activeEffect: ReactiveEffect | undefined
3849

3950
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
4051
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '')
52+
4153
export class ReactiveEffect<T = any> {
4254
active = true
4355
deps: Dep[] = []
@@ -68,19 +80,21 @@ export class ReactiveEffect<T = any> {
6880
effectStack.push((activeEffect = this))
6981
enableTracking()
7082

71-
effectTrackDepth++
83+
trackOpBit = 1 << ++effectTrackDepth
7284

7385
if (effectTrackDepth <= maxMarkerBits) {
74-
this.initDepMarkers()
86+
initDepMarkers(this)
7587
} else {
76-
this.cleanup()
88+
cleanupEffect(this)
7789
}
7890
return this.fn()
7991
} finally {
8092
if (effectTrackDepth <= maxMarkerBits) {
81-
this.finalizeDepMarkers()
93+
finalizeDepMarkers(this)
8294
}
83-
effectTrackDepth--
95+
96+
trackOpBit = 1 << --effectTrackDepth
97+
8498
resetTracking()
8599
effectStack.pop()
86100
const n = effectStack.length
@@ -89,45 +103,9 @@ export class ReactiveEffect<T = any> {
89103
}
90104
}
91105

92-
initDepMarkers() {
93-
const { deps } = this
94-
if (deps.length) {
95-
for (let i = 0; i < deps.length; i++) {
96-
setWasTracked(deps[i])
97-
}
98-
}
99-
}
100-
101-
finalizeDepMarkers() {
102-
const { deps } = this
103-
if (deps.length) {
104-
let ptr = 0
105-
for (let i = 0; i < deps.length; i++) {
106-
const dep = deps[i]
107-
if (wasTracked(dep) && !newTracked(dep)) {
108-
dep.delete(this)
109-
} else {
110-
deps[ptr++] = dep
111-
}
112-
resetTracked(dep)
113-
}
114-
deps.length = ptr
115-
}
116-
}
117-
118-
cleanup() {
119-
const { deps } = this
120-
if (deps.length) {
121-
for (let i = 0; i < deps.length; i++) {
122-
deps[i].delete(this)
123-
}
124-
deps.length = 0
125-
}
126-
}
127-
128106
stop() {
129107
if (this.active) {
130-
this.cleanup()
108+
cleanupEffect(this)
131109
if (this.onStop) {
132110
this.onStop()
133111
}
@@ -136,18 +114,14 @@ export class ReactiveEffect<T = any> {
136114
}
137115
}
138116

139-
// The number of effects currently being tracked recursively.
140-
let effectTrackDepth = 0
141-
142-
/**
143-
* The bitwise track markers support at most 30 levels op recursion.
144-
* This value is chosen to enable modern JS engines to use a SMI on all platforms.
145-
* When recursion depth is greater, fall back to using a full cleanup.
146-
*/
147-
const maxMarkerBits = 30
148-
149-
export function getTrackOpBit(): number {
150-
return 1 << effectTrackDepth
117+
function cleanupEffect(effect: ReactiveEffect) {
118+
const { deps } = effect
119+
if (deps.length) {
120+
for (let i = 0; i < deps.length; i++) {
121+
deps[i].delete(effect)
122+
}
123+
deps.length = 0
124+
}
151125
}
152126

153127
export interface ReactiveEffectOptions {
@@ -218,8 +192,7 @@ export function track(target: object, type: TrackOpTypes, key: unknown) {
218192
}
219193
let dep = depsMap.get(key)
220194
if (!dep) {
221-
dep = createDep()
222-
depsMap.set(key, dep)
195+
depsMap.set(key, (dep = createDep()))
223196
}
224197

225198
const eventInfo = __DEV__
@@ -240,7 +213,7 @@ export function trackEffects(
240213
let shouldTrack = false
241214
if (effectTrackDepth <= maxMarkerBits) {
242215
if (!newTracked(dep)) {
243-
setNewTracked(dep)
216+
dep.n |= trackOpBit // set newly tracked
244217
shouldTrack = !wasTracked(dep)
245218
}
246219
} else {

packages/runtime-core/src/renderer.ts

+32-41
Original file line numberDiff line numberDiff line change
@@ -1395,33 +1395,28 @@ function baseCreateRenderer(
13951395
isSVG,
13961396
optimized
13971397
) => {
1398-
const componentUpdateFn = function(this: ReactiveEffect) {
1398+
const componentUpdateFn = () => {
13991399
if (!instance.isMounted) {
14001400
let vnodeHook: VNodeHook | null | undefined
14011401
const { el, props } = initialVNode
14021402
const { bm, m, parent } = instance
14031403

1404-
try {
1405-
// Disallow component effect recursion during pre-lifecycle hooks.
1406-
this.allowRecurse = false
1407-
1408-
// beforeMount hook
1409-
if (bm) {
1410-
invokeArrayFns(bm)
1411-
}
1412-
// onVnodeBeforeMount
1413-
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
1414-
invokeVNodeHook(vnodeHook, parent, initialVNode)
1415-
}
1416-
if (
1417-
__COMPAT__ &&
1418-
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1419-
) {
1420-
instance.emit('hook:beforeMount')
1421-
}
1422-
} finally {
1423-
this.allowRecurse = true
1404+
effect.allowRecurse = false
1405+
// beforeMount hook
1406+
if (bm) {
1407+
invokeArrayFns(bm)
1408+
}
1409+
// onVnodeBeforeMount
1410+
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
1411+
invokeVNodeHook(vnodeHook, parent, initialVNode)
1412+
}
1413+
if (
1414+
__COMPAT__ &&
1415+
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1416+
) {
1417+
instance.emit('hook:beforeMount')
14241418
}
1419+
effect.allowRecurse = true
14251420

14261421
if (el && hydrateNode) {
14271422
// vnode has adopted host node - perform hydration instead of mount.
@@ -1547,27 +1542,23 @@ function baseCreateRenderer(
15471542
next = vnode
15481543
}
15491544

1550-
try {
1551-
// Disallow component effect recursion during pre-lifecycle hooks.
1552-
this.allowRecurse = false
1553-
1554-
// beforeUpdate hook
1555-
if (bu) {
1556-
invokeArrayFns(bu)
1557-
}
1558-
// onVnodeBeforeUpdate
1559-
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
1560-
invokeVNodeHook(vnodeHook, parent, next, vnode)
1561-
}
1562-
if (
1563-
__COMPAT__ &&
1564-
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1565-
) {
1566-
instance.emit('hook:beforeUpdate')
1567-
}
1568-
} finally {
1569-
this.allowRecurse = true
1545+
// Disallow component effect recursion during pre-lifecycle hooks.
1546+
effect.allowRecurse = false
1547+
// beforeUpdate hook
1548+
if (bu) {
1549+
invokeArrayFns(bu)
1550+
}
1551+
// onVnodeBeforeUpdate
1552+
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
1553+
invokeVNodeHook(vnodeHook, parent, next, vnode)
1554+
}
1555+
if (
1556+
__COMPAT__ &&
1557+
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1558+
) {
1559+
instance.emit('hook:beforeUpdate')
15701560
}
1561+
effect.allowRecurse = true
15711562

15721563
// render
15731564
if (__DEV__) {

0 commit comments

Comments
 (0)