refactor: reduce bundle size

This commit is contained in:
Evan You 2021-07-07 12:33:37 -04:00
parent 6cf2377cd4
commit eae7c247af
3 changed files with 106 additions and 136 deletions

View File

@ -1,4 +1,4 @@
import { ReactiveEffect, getTrackOpBit } from './effect' import { ReactiveEffect, trackOpBit } from './effect'
export type Dep = Set<ReactiveEffect> & TrackedMarkers export type Dep = Set<ReactiveEffect> & TrackedMarkers
@ -7,45 +7,51 @@ export type Dep = Set<ReactiveEffect> & TrackedMarkers
* tracking recursion. One bit per level is used to define wheter the dependency * tracking recursion. One bit per level is used to define wheter the dependency
* was/is tracked. * was/is tracked.
*/ */
type TrackedMarkers = { wasTracked: number; newTracked: number } type TrackedMarkers = {
/**
* wasTracked
*/
w: number
/**
* newTracked
*/
n: number
}
export function createDep(effects?: ReactiveEffect[]): Dep { export const createDep = (effects?: ReactiveEffect[]): Dep => {
const dep = new Set<ReactiveEffect>(effects) as Dep const dep = new Set<ReactiveEffect>(effects) as Dep
dep.wasTracked = 0 dep.w = 0
dep.newTracked = 0 dep.n = 0
return dep return dep
} }
export function wasTracked(dep: Dep): boolean { export const wasTracked = (dep: Dep): boolean => (dep.w & trackOpBit) > 0
return hasBit(dep.wasTracked, getTrackOpBit())
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 function newTracked(dep: Dep): boolean { export const finalizeDepMarkers = (effect: ReactiveEffect) => {
return hasBit(dep.newTracked, getTrackOpBit()) const { deps } = effect
} if (deps.length) {
let ptr = 0
export function setWasTracked(dep: Dep) { for (let i = 0; i < deps.length; i++) {
dep.wasTracked = setBit(dep.wasTracked, getTrackOpBit()) const dep = deps[i]
} if (wasTracked(dep) && !newTracked(dep)) {
dep.delete(effect)
export function setNewTracked(dep: Dep) { } else {
dep.newTracked = setBit(dep.newTracked, getTrackOpBit()) deps[ptr++] = dep
} }
// clear bits
export function resetTracked(dep: Dep) { dep.w &= ~trackOpBit
const trackOpBit = getTrackOpBit() dep.n &= ~trackOpBit
dep.wasTracked = clearBit(dep.wasTracked, trackOpBit) }
dep.newTracked = clearBit(dep.newTracked, trackOpBit) deps.length = ptr
} }
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
} }

View File

@ -4,10 +4,9 @@ import { EffectScope, recordEffectScope } from './effectScope'
import { import {
createDep, createDep,
Dep, Dep,
finalizeDepMarkers,
initDepMarkers,
newTracked, newTracked,
resetTracked,
setNewTracked,
setWasTracked,
wasTracked wasTracked
} from './Dep' } from './Dep'
@ -18,6 +17,18 @@ import {
type KeyToDepMap = Map<any, Dep> type KeyToDepMap = Map<any, Dep>
const targetMap = new WeakMap<any, KeyToDepMap>() const targetMap = new WeakMap<any, KeyToDepMap>()
// 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 EffectScheduler = () => void
export type DebuggerEvent = { export type DebuggerEvent = {
@ -38,6 +49,7 @@ let activeEffect: ReactiveEffect | undefined
export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '') export const ITERATE_KEY = Symbol(__DEV__ ? 'iterate' : '')
export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '') export const MAP_KEY_ITERATE_KEY = Symbol(__DEV__ ? 'Map key iterate' : '')
export class ReactiveEffect<T = any> { export class ReactiveEffect<T = any> {
active = true active = true
deps: Dep[] = [] deps: Dep[] = []
@ -68,19 +80,21 @@ export class ReactiveEffect<T = any> {
effectStack.push((activeEffect = this)) effectStack.push((activeEffect = this))
enableTracking() enableTracking()
effectTrackDepth++ trackOpBit = 1 << ++effectTrackDepth
if (effectTrackDepth <= maxMarkerBits) { if (effectTrackDepth <= maxMarkerBits) {
this.initDepMarkers() initDepMarkers(this)
} else { } else {
this.cleanup() cleanupEffect(this)
} }
return this.fn() return this.fn()
} finally { } finally {
if (effectTrackDepth <= maxMarkerBits) { if (effectTrackDepth <= maxMarkerBits) {
this.finalizeDepMarkers() finalizeDepMarkers(this)
} }
effectTrackDepth--
trackOpBit = 1 << --effectTrackDepth
resetTracking() resetTracking()
effectStack.pop() effectStack.pop()
const n = effectStack.length const n = effectStack.length
@ -89,45 +103,9 @@ export class ReactiveEffect<T = any> {
} }
} }
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() { stop() {
if (this.active) { if (this.active) {
this.cleanup() cleanupEffect(this)
if (this.onStop) { if (this.onStop) {
this.onStop() this.onStop()
} }
@ -136,18 +114,14 @@ export class ReactiveEffect<T = any> {
} }
} }
// The number of effects currently being tracked recursively. function cleanupEffect(effect: ReactiveEffect) {
let effectTrackDepth = 0 const { deps } = effect
if (deps.length) {
/** for (let i = 0; i < deps.length; i++) {
* The bitwise track markers support at most 30 levels op recursion. deps[i].delete(effect)
* 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. deps.length = 0
*/ }
const maxMarkerBits = 30
export function getTrackOpBit(): number {
return 1 << effectTrackDepth
} }
export interface ReactiveEffectOptions { export interface ReactiveEffectOptions {
@ -218,8 +192,7 @@ export function track(target: object, type: TrackOpTypes, key: unknown) {
} }
let dep = depsMap.get(key) let dep = depsMap.get(key)
if (!dep) { if (!dep) {
dep = createDep() depsMap.set(key, (dep = createDep()))
depsMap.set(key, dep)
} }
const eventInfo = __DEV__ const eventInfo = __DEV__
@ -240,7 +213,7 @@ export function trackEffects(
let shouldTrack = false let shouldTrack = false
if (effectTrackDepth <= maxMarkerBits) { if (effectTrackDepth <= maxMarkerBits) {
if (!newTracked(dep)) { if (!newTracked(dep)) {
setNewTracked(dep) dep.n |= trackOpBit // set newly tracked
shouldTrack = !wasTracked(dep) shouldTrack = !wasTracked(dep)
} }
} else { } else {

View File

@ -1395,33 +1395,28 @@ function baseCreateRenderer(
isSVG, isSVG,
optimized optimized
) => { ) => {
const componentUpdateFn = function(this: ReactiveEffect) { const componentUpdateFn = () => {
if (!instance.isMounted) { if (!instance.isMounted) {
let vnodeHook: VNodeHook | null | undefined let vnodeHook: VNodeHook | null | undefined
const { el, props } = initialVNode const { el, props } = initialVNode
const { bm, m, parent } = instance const { bm, m, parent } = instance
try { effect.allowRecurse = false
// Disallow component effect recursion during pre-lifecycle hooks. // beforeMount hook
this.allowRecurse = false if (bm) {
invokeArrayFns(bm)
// 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
} }
// 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) { if (el && hydrateNode) {
// vnode has adopted host node - perform hydration instead of mount. // vnode has adopted host node - perform hydration instead of mount.
@ -1547,27 +1542,23 @@ function baseCreateRenderer(
next = vnode next = vnode
} }
try { // Disallow component effect recursion during pre-lifecycle hooks.
// Disallow component effect recursion during pre-lifecycle hooks. effect.allowRecurse = false
this.allowRecurse = false // beforeUpdate hook
if (bu) {
// beforeUpdate hook invokeArrayFns(bu)
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
} }
// 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 // render
if (__DEV__) { if (__DEV__) {