-import { ReactiveEffect, getTrackOpBit } from './effect'
+import { ReactiveEffect, trackOpBit } from './effect'
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 }
-
-export function createDep(effects?: ReactiveEffect[]): Dep {
+type TrackedMarkers = {
+ /**
+ * wasTracked
+ */
+ w: number
+ /**
+ * newTracked
+ */
+ n: number
+}
+
+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 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
+ }
}
import {
createDep,
Dep,
+ finalizeDepMarkers,
+ initDepMarkers,
newTracked,
- resetTracked,
- setNewTracked,
- setWasTracked,
wasTracked
} from './Dep'
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 = {
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[] = []
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
}
}
- 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()
}
}
}
-// 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 {
}
let dep = depsMap.get(key)
if (!dep) {
- dep = createDep()
- depsMap.set(key, dep)
+ depsMap.set(key, (dep = createDep()))
}
const eventInfo = __DEV__
let shouldTrack = false
if (effectTrackDepth <= maxMarkerBits) {
if (!newTracked(dep)) {
- setNewTracked(dep)
+ dep.n |= trackOpBit // set newly tracked
shouldTrack = !wasTracked(dep)
}
} else {
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.
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__) {