Skip to content

Commit ab8bc71

Browse files
committed
refactor: reduce bundle size
1 parent a889895 commit ab8bc71

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
@@ -1406,33 +1406,28 @@ function baseCreateRenderer(
14061406
isSVG,
14071407
optimized
14081408
) => {
1409-
const componentUpdateFn = function(this: ReactiveEffect) {
1409+
const componentUpdateFn = () => {
14101410
if (!instance.isMounted) {
14111411
let vnodeHook: VNodeHook | null | undefined
14121412
const { el, props } = initialVNode
14131413
const { bm, m, parent } = instance
14141414

1415-
try {
1416-
// Disallow component effect recursion during pre-lifecycle hooks.
1417-
this.allowRecurse = false
1418-
1419-
// beforeMount hook
1420-
if (bm) {
1421-
invokeArrayFns(bm)
1422-
}
1423-
// onVnodeBeforeMount
1424-
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
1425-
invokeVNodeHook(vnodeHook, parent, initialVNode)
1426-
}
1427-
if (
1428-
__COMPAT__ &&
1429-
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1430-
) {
1431-
instance.emit('hook:beforeMount')
1432-
}
1433-
} finally {
1434-
this.allowRecurse = true
1415+
effect.allowRecurse = false
1416+
// beforeMount hook
1417+
if (bm) {
1418+
invokeArrayFns(bm)
1419+
}
1420+
// onVnodeBeforeMount
1421+
if ((vnodeHook = props && props.onVnodeBeforeMount)) {
1422+
invokeVNodeHook(vnodeHook, parent, initialVNode)
1423+
}
1424+
if (
1425+
__COMPAT__ &&
1426+
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1427+
) {
1428+
instance.emit('hook:beforeMount')
14351429
}
1430+
effect.allowRecurse = true
14361431

14371432
if (el && hydrateNode) {
14381433
// vnode has adopted host node - perform hydration instead of mount.
@@ -1558,27 +1553,23 @@ function baseCreateRenderer(
15581553
next = vnode
15591554
}
15601555

1561-
try {
1562-
// Disallow component effect recursion during pre-lifecycle hooks.
1563-
this.allowRecurse = false
1564-
1565-
// beforeUpdate hook
1566-
if (bu) {
1567-
invokeArrayFns(bu)
1568-
}
1569-
// onVnodeBeforeUpdate
1570-
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
1571-
invokeVNodeHook(vnodeHook, parent, next, vnode)
1572-
}
1573-
if (
1574-
__COMPAT__ &&
1575-
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1576-
) {
1577-
instance.emit('hook:beforeUpdate')
1578-
}
1579-
} finally {
1580-
this.allowRecurse = true
1556+
// Disallow component effect recursion during pre-lifecycle hooks.
1557+
effect.allowRecurse = false
1558+
// beforeUpdate hook
1559+
if (bu) {
1560+
invokeArrayFns(bu)
1561+
}
1562+
// onVnodeBeforeUpdate
1563+
if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) {
1564+
invokeVNodeHook(vnodeHook, parent, next, vnode)
1565+
}
1566+
if (
1567+
__COMPAT__ &&
1568+
isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance)
1569+
) {
1570+
instance.emit('hook:beforeUpdate')
15811571
}
1572+
effect.allowRecurse = true
15821573

15831574
// render
15841575
if (__DEV__) {

0 commit comments

Comments
 (0)