From: Evan You Date: Wed, 7 Jul 2021 16:33:37 +0000 (-0400) Subject: refactor: reduce bundle size X-Git-Tag: v3.2.0-beta.1~29 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=eae7c247af8dd177e28179cda3f06e2cdfe4baff;p=thirdparty%2Fvuejs%2Fcore.git refactor: reduce bundle size --- diff --git a/packages/reactivity/src/Dep.ts b/packages/reactivity/src/Dep.ts index 1d735466fc..546f99e0f4 100644 --- a/packages/reactivity/src/Dep.ts +++ b/packages/reactivity/src/Dep.ts @@ -1,4 +1,4 @@ -import { ReactiveEffect, getTrackOpBit } from './effect' +import { ReactiveEffect, trackOpBit } from './effect' export type Dep = Set & TrackedMarkers @@ -7,45 +7,51 @@ export type Dep = Set & TrackedMarkers * tracking recursion. One bit per level is used to define wheter the dependency * was/is tracked. */ -type TrackedMarkers = { wasTracked: number; newTracked: number } - -export function createDep(effects?: ReactiveEffect[]): Dep { +type TrackedMarkers = { + /** + * wasTracked + */ + w: number + /** + * newTracked + */ + n: number +} + +export const createDep = (effects?: ReactiveEffect[]): Dep => { const dep = new Set(effects) as Dep - dep.wasTracked = 0 - dep.newTracked = 0 + dep.w = 0 + dep.n = 0 return dep } -export function wasTracked(dep: Dep): boolean { - return hasBit(dep.wasTracked, getTrackOpBit()) -} - -export function newTracked(dep: Dep): boolean { - return hasBit(dep.newTracked, getTrackOpBit()) -} - -export function setWasTracked(dep: Dep) { - dep.wasTracked = setBit(dep.wasTracked, getTrackOpBit()) -} - -export function setNewTracked(dep: Dep) { - dep.newTracked = setBit(dep.newTracked, getTrackOpBit()) -} - -export function resetTracked(dep: Dep) { - const trackOpBit = getTrackOpBit() - dep.wasTracked = clearBit(dep.wasTracked, trackOpBit) - dep.newTracked = clearBit(dep.newTracked, trackOpBit) -} - -function hasBit(value: number, bit: number): boolean { - return (value & bit) > 0 -} - -function setBit(value: number, bit: number): number { - return value | bit -} - -function clearBit(value: number, bit: number): number { - return value & ~bit +export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0 + +export const newTracked = (dep: Dep): boolean => (dep.n & trackOpBit) > 0 + +export const initDepMarkers = ({ deps }: ReactiveEffect) => { + if (deps.length) { + for (let i = 0; i < deps.length; i++) { + deps[i].w |= trackOpBit // set was tracked + } + } +} + +export const finalizeDepMarkers = (effect: ReactiveEffect) => { + const { deps } = effect + if (deps.length) { + let ptr = 0 + for (let i = 0; i < deps.length; i++) { + const dep = deps[i] + if (wasTracked(dep) && !newTracked(dep)) { + dep.delete(effect) + } else { + deps[ptr++] = dep + } + // clear bits + dep.w &= ~trackOpBit + dep.n &= ~trackOpBit + } + deps.length = ptr + } } diff --git a/packages/reactivity/src/effect.ts b/packages/reactivity/src/effect.ts index 670b6b340c..3a72714270 100644 --- a/packages/reactivity/src/effect.ts +++ b/packages/reactivity/src/effect.ts @@ -4,10 +4,9 @@ import { EffectScope, recordEffectScope } from './effectScope' import { createDep, Dep, + finalizeDepMarkers, + initDepMarkers, newTracked, - resetTracked, - setNewTracked, - setWasTracked, wasTracked } from './Dep' @@ -18,6 +17,18 @@ import { type KeyToDepMap = Map const targetMap = new WeakMap() +// The number of effects currently being tracked recursively. +let effectTrackDepth = 0 + +export let trackOpBit = 1 + +/** + * The bitwise track markers support at most 30 levels op recursion. + * This value is chosen to enable modern JS engines to use a SMI on all platforms. + * When recursion depth is greater, fall back to using a full cleanup. + */ +const maxMarkerBits = 30 + export type EffectScheduler = () => void export type DebuggerEvent = { @@ -38,6 +49,7 @@ let activeEffect: ReactiveEffect | undefined export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') + export class ReactiveEffect { active = true deps: Dep[] = [] @@ -68,19 +80,21 @@ export class ReactiveEffect { effectStack.push((activeEffect = this)) enableTracking() - effectTrackDepth++ + trackOpBit = 1 << ++effectTrackDepth if (effectTrackDepth <= maxMarkerBits) { - this.initDepMarkers() + initDepMarkers(this) } else { - this.cleanup() + cleanupEffect(this) } return this.fn() } finally { if (effectTrackDepth <= maxMarkerBits) { - this.finalizeDepMarkers() + finalizeDepMarkers(this) } - effectTrackDepth-- + + trackOpBit = 1 << --effectTrackDepth + resetTracking() effectStack.pop() const n = effectStack.length @@ -89,45 +103,9 @@ export class ReactiveEffect { } } - initDepMarkers() { - const { deps } = this - if (deps.length) { - for (let i = 0; i < deps.length; i++) { - setWasTracked(deps[i]) - } - } - } - - finalizeDepMarkers() { - const { deps } = this - if (deps.length) { - let ptr = 0 - for (let i = 0; i < deps.length; i++) { - const dep = deps[i] - if (wasTracked(dep) && !newTracked(dep)) { - dep.delete(this) - } else { - deps[ptr++] = dep - } - resetTracked(dep) - } - deps.length = ptr - } - } - - cleanup() { - const { deps } = this - if (deps.length) { - for (let i = 0; i < deps.length; i++) { - deps[i].delete(this) - } - deps.length = 0 - } - } - stop() { if (this.active) { - this.cleanup() + cleanupEffect(this) if (this.onStop) { this.onStop() } @@ -136,18 +114,14 @@ export class ReactiveEffect { } } -// The number of effects currently being tracked recursively. -let effectTrackDepth = 0 - -/** - * The bitwise track markers support at most 30 levels op recursion. - * This value is chosen to enable modern JS engines to use a SMI on all platforms. - * When recursion depth is greater, fall back to using a full cleanup. - */ -const maxMarkerBits = 30 - -export function getTrackOpBit(): number { - return 1 << effectTrackDepth +function cleanupEffect(effect: ReactiveEffect) { + const { deps } = effect + if (deps.length) { + for (let i = 0; i < deps.length; i++) { + deps[i].delete(effect) + } + deps.length = 0 + } } export interface ReactiveEffectOptions { @@ -218,8 +192,7 @@ export function track(target: object, type: TrackOpTypes, key: unknown) { } let dep = depsMap.get(key) if (!dep) { - dep = createDep() - depsMap.set(key, dep) + depsMap.set(key, (dep = createDep())) } const eventInfo = __DEV__ @@ -240,7 +213,7 @@ export function trackEffects( let shouldTrack = false if (effectTrackDepth <= maxMarkerBits) { if (!newTracked(dep)) { - setNewTracked(dep) + dep.n |= trackOpBit // set newly tracked shouldTrack = !wasTracked(dep) } } else { diff --git a/packages/runtime-core/src/renderer.ts b/packages/runtime-core/src/renderer.ts index b879ed1b17..e021c761de 100644 --- a/packages/runtime-core/src/renderer.ts +++ b/packages/runtime-core/src/renderer.ts @@ -1395,33 +1395,28 @@ function baseCreateRenderer( isSVG, optimized ) => { - const componentUpdateFn = function(this: ReactiveEffect) { + const componentUpdateFn = () => { if (!instance.isMounted) { let vnodeHook: VNodeHook | null | undefined const { el, props } = initialVNode const { bm, m, parent } = instance - try { - // Disallow component effect recursion during pre-lifecycle hooks. - this.allowRecurse = false - - // beforeMount hook - if (bm) { - invokeArrayFns(bm) - } - // onVnodeBeforeMount - if ((vnodeHook = props && props.onVnodeBeforeMount)) { - invokeVNodeHook(vnodeHook, parent, initialVNode) - } - if ( - __COMPAT__ && - isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) - ) { - instance.emit('hook:beforeMount') - } - } finally { - this.allowRecurse = true + effect.allowRecurse = false + // beforeMount hook + if (bm) { + invokeArrayFns(bm) + } + // onVnodeBeforeMount + if ((vnodeHook = props && props.onVnodeBeforeMount)) { + invokeVNodeHook(vnodeHook, parent, initialVNode) + } + if ( + __COMPAT__ && + isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) + ) { + instance.emit('hook:beforeMount') } + effect.allowRecurse = true if (el && hydrateNode) { // vnode has adopted host node - perform hydration instead of mount. @@ -1547,27 +1542,23 @@ function baseCreateRenderer( next = vnode } - try { - // Disallow component effect recursion during pre-lifecycle hooks. - this.allowRecurse = false - - // beforeUpdate hook - if (bu) { - invokeArrayFns(bu) - } - // onVnodeBeforeUpdate - if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) { - invokeVNodeHook(vnodeHook, parent, next, vnode) - } - if ( - __COMPAT__ && - isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) - ) { - instance.emit('hook:beforeUpdate') - } - } finally { - this.allowRecurse = true + // Disallow component effect recursion during pre-lifecycle hooks. + effect.allowRecurse = false + // beforeUpdate hook + if (bu) { + invokeArrayFns(bu) + } + // onVnodeBeforeUpdate + if ((vnodeHook = next.props && next.props.onVnodeBeforeUpdate)) { + invokeVNodeHook(vnodeHook, parent, next, vnode) + } + if ( + __COMPAT__ && + isCompatEnabled(DeprecationTypes.INSTANCE_EVENT_HOOKS, instance) + ) { + instance.emit('hook:beforeUpdate') } + effect.allowRecurse = true // render if (__DEV__) {