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
@ -7,45 +7,51 @@ export type Dep = Set<ReactiveEffect> & TrackedMarkers
* tracking recursion. One bit per level is used to define wheter the dependency
* 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
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 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 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 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
}
}

View File

@ -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<any, Dep>
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 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<T = any> {
active = true
deps: Dep[] = []
@ -68,19 +80,21 @@ export class ReactiveEffect<T = any> {
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<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() {
if (this.active) {
this.cleanup()
cleanupEffect(this)
if (this.onStop) {
this.onStop()
}
@ -136,18 +114,14 @@ export class ReactiveEffect<T = any> {
}
}
// 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 {

View File

@ -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__) {