From 80d1eb748079ab33ed7d03531740eb267de15d8e Mon Sep 17 00:00:00 2001 From: Evan You Date: Tue, 13 Nov 2018 11:03:35 -0500 Subject: [PATCH] refactor: rename things --- .../__tests__/collections/Map.spec.ts | 34 ++-- .../__tests__/collections/Set.spec.ts | 42 ++-- .../__tests__/collections/WeakMap.spec.ts | 12 +- .../__tests__/collections/WeakSet.spec.ts | 12 +- packages/observer/__tests__/computed.spec.ts | 18 +- .../{autorun.spec.ts => effect.spec.ts} | 138 ++++++------- packages/observer/__tests__/immutable.spec.ts | 34 ++-- packages/observer/src/autorun.ts | 180 ----------------- packages/observer/src/baseHandlers.ts | 2 +- packages/observer/src/collectionHandlers.ts | 2 +- packages/observer/src/computed.ts | 21 +- packages/observer/src/effect.ts | 183 ++++++++++++++++++ packages/observer/src/index.ts | 34 ++-- packages/observer/src/state.ts | 4 +- .../runtime-core/__tests__/fragment.spec.ts | 13 +- packages/runtime-core/src/component.ts | 8 +- .../runtime-core/src/componentComputed.ts | 2 +- packages/runtime-core/src/componentUtils.ts | 2 +- packages/runtime-core/src/componentWatch.ts | 4 +- packages/runtime-core/src/createRenderer.ts | 52 ++--- .../scheduler/__tests__/scheduler.spec.ts | 16 +- packages/scheduler/src/index.ts | 24 +-- 22 files changed, 423 insertions(+), 414 deletions(-) rename packages/observer/__tests__/{autorun.spec.ts => effect.spec.ts} (84%) delete mode 100644 packages/observer/src/autorun.ts create mode 100644 packages/observer/src/effect.ts diff --git a/packages/observer/__tests__/collections/Map.spec.ts b/packages/observer/__tests__/collections/Map.spec.ts index b69c1f82..6ba047b8 100644 --- a/packages/observer/__tests__/collections/Map.spec.ts +++ b/packages/observer/__tests__/collections/Map.spec.ts @@ -1,4 +1,4 @@ -import { observable, autorun, unwrap, isObservable } from '../../src' +import { observable, effect, unwrap, isObservable } from '../../src' describe('observer/collections', () => { describe('Map', () => { @@ -13,7 +13,7 @@ describe('observer/collections', () => { it('should observe mutations', () => { let dummy const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = map.get('key') }) @@ -29,7 +29,7 @@ describe('observer/collections', () => { it('should observe size mutations', () => { let dummy const map = observable(new Map()) - autorun(() => (dummy = map.size)) + effect(() => (dummy = map.size)) expect(dummy).toBe(0) map.set('key1', 'value') @@ -44,7 +44,7 @@ describe('observer/collections', () => { it('should observe for of iteration', () => { let dummy const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = 0 // eslint-disable-next-line no-unused-vars for (let [key, num] of map) { @@ -67,7 +67,7 @@ describe('observer/collections', () => { it('should observe forEach iteration', () => { let dummy: any const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = 0 map.forEach((num: any) => (dummy += num)) }) @@ -86,7 +86,7 @@ describe('observer/collections', () => { it('should observe keys iteration', () => { let dummy const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = 0 for (let key of map.keys()) { dummy += key @@ -107,7 +107,7 @@ describe('observer/collections', () => { it('should observe values iteration', () => { let dummy const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = 0 for (let num of map.values()) { dummy += num @@ -128,7 +128,7 @@ describe('observer/collections', () => { it('should observe entries iteration', () => { let dummy const map = observable(new Map()) - autorun(() => { + effect(() => { dummy = 0 // eslint-disable-next-line no-unused-vars for (let [key, num] of map.entries()) { @@ -151,7 +151,7 @@ describe('observer/collections', () => { it('should be triggered by clearing', () => { let dummy const map = observable(new Map()) - autorun(() => (dummy = map.get('key'))) + effect(() => (dummy = map.get('key'))) expect(dummy).toBe(undefined) map.set('key', 3) @@ -163,7 +163,7 @@ describe('observer/collections', () => { it('should not observe custom property mutations', () => { let dummy const map: any = observable(new Map()) - autorun(() => (dummy = map.customProp)) + effect(() => (dummy = map.customProp)) expect(dummy).toBe(undefined) map.customProp = 'Hello World' @@ -174,7 +174,7 @@ describe('observer/collections', () => { let dummy const map = observable(new Map()) const mapSpy = jest.fn(() => (dummy = map.get('key'))) - autorun(mapSpy) + effect(mapSpy) expect(dummy).toBe(undefined) expect(mapSpy).toHaveBeenCalledTimes(1) @@ -198,7 +198,7 @@ describe('observer/collections', () => { it('should not observe raw data', () => { let dummy const map = observable(new Map()) - autorun(() => (dummy = unwrap(map).get('key'))) + effect(() => (dummy = unwrap(map).get('key'))) expect(dummy).toBe(undefined) map.set('key', 'Hello') @@ -229,7 +229,7 @@ describe('observer/collections', () => { const observed = observable(new Map()) observed.set('key', { a: 1 }) let dummy - autorun(() => { + effect(() => { dummy = observed.get('key').a }) observed.get('key').a = 2 @@ -239,7 +239,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (forEach)', () => { const map = observable(new Map([[1, { foo: 1 }]])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 map.forEach(value => { expect(isObservable(value)).toBe(true) @@ -254,7 +254,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (values)', () => { const map = observable(new Map([[1, { foo: 1 }]])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const value of map.values()) { expect(isObservable(value)).toBe(true) @@ -270,7 +270,7 @@ describe('observer/collections', () => { const key = {} const map = observable(new Map([[key, { foo: 1 }]])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const [key, value] of map.entries()) { key @@ -288,7 +288,7 @@ describe('observer/collections', () => { const key = {} const map = observable(new Map([[key, { foo: 1 }]])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const [key, value] of map) { key diff --git a/packages/observer/__tests__/collections/Set.spec.ts b/packages/observer/__tests__/collections/Set.spec.ts index 4d6fa845..d2d999c2 100644 --- a/packages/observer/__tests__/collections/Set.spec.ts +++ b/packages/observer/__tests__/collections/Set.spec.ts @@ -1,4 +1,4 @@ -import { observable, autorun, isObservable, unwrap } from '../../src' +import { observable, effect, isObservable, unwrap } from '../../src' describe('observer/collections', () => { describe('Set', () => { @@ -13,7 +13,7 @@ describe('observer/collections', () => { it('should observe mutations', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = set.has('value'))) + effect(() => (dummy = set.has('value'))) expect(dummy).toBe(false) set.add('value') @@ -25,7 +25,7 @@ describe('observer/collections', () => { it('should observe for of iteration', () => { let dummy const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 for (let num of set) { dummy += num @@ -45,7 +45,7 @@ describe('observer/collections', () => { it('should observe forEach iteration', () => { let dummy: any const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 set.forEach(num => (dummy += num)) }) @@ -63,7 +63,7 @@ describe('observer/collections', () => { it('should observe values iteration', () => { let dummy const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 for (let num of set.values()) { dummy += num @@ -83,7 +83,7 @@ describe('observer/collections', () => { it('should observe keys iteration', () => { let dummy const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 for (let num of set.keys()) { dummy += num @@ -103,7 +103,7 @@ describe('observer/collections', () => { it('should observe entries iteration', () => { let dummy const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 // eslint-disable-next-line no-unused-vars for (let [key, num] of set.entries()) { @@ -125,7 +125,7 @@ describe('observer/collections', () => { it('should be triggered by clearing', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = set.has('key'))) + effect(() => (dummy = set.has('key'))) expect(dummy).toBe(false) set.add('key') @@ -137,7 +137,7 @@ describe('observer/collections', () => { it('should not observe custom property mutations', () => { let dummy const set: any = observable(new Set()) - autorun(() => (dummy = set.customProp)) + effect(() => (dummy = set.customProp)) expect(dummy).toBe(undefined) set.customProp = 'Hello World' @@ -147,7 +147,7 @@ describe('observer/collections', () => { it('should observe size mutations', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = set.size)) + effect(() => (dummy = set.size)) expect(dummy).toBe(0) set.add('value') @@ -163,7 +163,7 @@ describe('observer/collections', () => { let dummy const set = observable(new Set()) const setSpy = jest.fn(() => (dummy = set.has('value'))) - autorun(setSpy) + effect(setSpy) expect(dummy).toBe(false) expect(setSpy).toHaveBeenCalledTimes(1) @@ -187,7 +187,7 @@ describe('observer/collections', () => { it('should not observe raw data', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = unwrap(set).has('value'))) + effect(() => (dummy = unwrap(set).has('value'))) expect(dummy).toBe(false) set.add('value') @@ -197,7 +197,7 @@ describe('observer/collections', () => { it('should not observe raw iterations', () => { let dummy = 0 const set = observable(new Set()) - autorun(() => { + effect(() => { dummy = 0 for (let [num] of unwrap(set).entries()) { dummy += num @@ -227,7 +227,7 @@ describe('observer/collections', () => { it('should not be triggered by raw mutations', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = set.has('value'))) + effect(() => (dummy = set.has('value'))) expect(dummy).toBe(false) unwrap(set).add('value') @@ -242,7 +242,7 @@ describe('observer/collections', () => { it('should not observe raw size mutations', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = unwrap(set).size)) + effect(() => (dummy = unwrap(set).size)) expect(dummy).toBe(0) set.add('value') @@ -252,7 +252,7 @@ describe('observer/collections', () => { it('should not be triggered by raw size mutations', () => { let dummy const set = observable(new Set()) - autorun(() => (dummy = set.size)) + effect(() => (dummy = set.size)) expect(dummy).toBe(0) unwrap(set).add('value') @@ -264,7 +264,7 @@ describe('observer/collections', () => { const key = {} const set = observable(new Set()) const setSpy = jest.fn(() => (dummy = set.has(key))) - autorun(setSpy) + effect(setSpy) expect(dummy).toBe(false) expect(setSpy).toHaveBeenCalledTimes(1) @@ -290,7 +290,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (forEach)', () => { const set = observable(new Set([{ foo: 1 }])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 set.forEach(value => { expect(isObservable(value)).toBe(true) @@ -307,7 +307,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (values)', () => { const set = observable(new Set([{ foo: 1 }])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const value of set.values()) { expect(isObservable(value)).toBe(true) @@ -324,7 +324,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (entries)', () => { const set = observable(new Set([{ foo: 1 }])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const [key, value] of set.entries()) { expect(isObservable(key)).toBe(true) @@ -342,7 +342,7 @@ describe('observer/collections', () => { it('should observe nested values in iterations (for...of)', () => { const set = observable(new Set([{ foo: 1 }])) let dummy: any - autorun(() => { + effect(() => { dummy = 0 for (const value of set) { expect(isObservable(value)).toBe(true) diff --git a/packages/observer/__tests__/collections/WeakMap.spec.ts b/packages/observer/__tests__/collections/WeakMap.spec.ts index f20a1961..ec6550dc 100644 --- a/packages/observer/__tests__/collections/WeakMap.spec.ts +++ b/packages/observer/__tests__/collections/WeakMap.spec.ts @@ -1,4 +1,4 @@ -import { observable, autorun, unwrap, isObservable } from '../../src' +import { observable, effect, unwrap, isObservable } from '../../src' describe('observer/collections', () => { describe('WeakMap', () => { @@ -14,7 +14,7 @@ describe('observer/collections', () => { let dummy const key = {} const map = observable(new WeakMap()) - autorun(() => { + effect(() => { dummy = map.get(key) }) @@ -30,7 +30,7 @@ describe('observer/collections', () => { it('should not observe custom property mutations', () => { let dummy const map: any = observable(new WeakMap()) - autorun(() => (dummy = map.customProp)) + effect(() => (dummy = map.customProp)) expect(dummy).toBe(undefined) map.customProp = 'Hello World' @@ -42,7 +42,7 @@ describe('observer/collections', () => { const key = {} const map = observable(new WeakMap()) const mapSpy = jest.fn(() => (dummy = map.get(key))) - autorun(mapSpy) + effect(mapSpy) expect(dummy).toBe(undefined) expect(mapSpy).toHaveBeenCalledTimes(1) @@ -64,7 +64,7 @@ describe('observer/collections', () => { let dummy const key = {} const map = observable(new WeakMap()) - autorun(() => (dummy = unwrap(map).get(key))) + effect(() => (dummy = unwrap(map).get(key))) expect(dummy).toBe(undefined) map.set(key, 'Hello') @@ -98,7 +98,7 @@ describe('observer/collections', () => { const key = {} observed.set(key, { a: 1 }) let dummy - autorun(() => { + effect(() => { dummy = observed.get(key).a }) observed.get(key).a = 2 diff --git a/packages/observer/__tests__/collections/WeakSet.spec.ts b/packages/observer/__tests__/collections/WeakSet.spec.ts index 23b05342..1ec2a464 100644 --- a/packages/observer/__tests__/collections/WeakSet.spec.ts +++ b/packages/observer/__tests__/collections/WeakSet.spec.ts @@ -1,4 +1,4 @@ -import { observable, isObservable, autorun, unwrap } from '../../src' +import { observable, isObservable, effect, unwrap } from '../../src' describe('observer/collections', () => { describe('WeakSet', () => { @@ -14,7 +14,7 @@ describe('observer/collections', () => { let dummy const value = {} const set = observable(new WeakSet()) - autorun(() => (dummy = set.has(value))) + effect(() => (dummy = set.has(value))) expect(dummy).toBe(false) set.add(value) @@ -26,7 +26,7 @@ describe('observer/collections', () => { it('should not observe custom property mutations', () => { let dummy const set: any = observable(new WeakSet()) - autorun(() => (dummy = set.customProp)) + effect(() => (dummy = set.customProp)) expect(dummy).toBe(undefined) set.customProp = 'Hello World' @@ -38,7 +38,7 @@ describe('observer/collections', () => { const value = {} const set = observable(new WeakSet()) const setSpy = jest.fn(() => (dummy = set.has(value))) - autorun(setSpy) + effect(setSpy) expect(dummy).toBe(false) expect(setSpy).toHaveBeenCalledTimes(1) @@ -60,7 +60,7 @@ describe('observer/collections', () => { const value = {} let dummy const set = observable(new WeakSet()) - autorun(() => (dummy = unwrap(set).has(value))) + effect(() => (dummy = unwrap(set).has(value))) expect(dummy).toBe(false) set.add(value) @@ -71,7 +71,7 @@ describe('observer/collections', () => { const value = {} let dummy const set = observable(new WeakSet()) - autorun(() => (dummy = set.has(value))) + effect(() => (dummy = set.has(value))) expect(dummy).toBe(false) unwrap(set).add(value) diff --git a/packages/observer/__tests__/computed.spec.ts b/packages/observer/__tests__/computed.spec.ts index 973e3c0f..e07eb8e7 100644 --- a/packages/observer/__tests__/computed.spec.ts +++ b/packages/observer/__tests__/computed.spec.ts @@ -1,4 +1,4 @@ -import { computed, observable, autorun, stop } from '../src' +import { computed, observable, effect, stop } from '../src' describe('observer/computed', () => { it('should return updated value', () => { @@ -52,11 +52,11 @@ describe('observer/computed', () => { expect(callArg).toBe(ctx) }) - it('should trigger autorun', () => { + it('should trigger effect', () => { const value: any = observable({}) const cValue = computed(() => value.foo) let dummy - autorun(() => { + effect(() => { dummy = cValue() }) expect(dummy).toBe(undefined) @@ -75,7 +75,7 @@ describe('observer/computed', () => { expect(c1()).toBe(1) }) - it('should trigger autorun when chained', () => { + it('should trigger effect when chained', () => { const value: any = observable({ foo: 0 }) const getter1 = jest.fn(() => value.foo) const getter2 = jest.fn(() => { @@ -85,7 +85,7 @@ describe('observer/computed', () => { const c2 = computed(getter2) let dummy - autorun(() => { + effect(() => { dummy = c2() }) expect(dummy).toBe(1) @@ -98,7 +98,7 @@ describe('observer/computed', () => { expect(getter2).toHaveBeenCalledTimes(2) }) - it('should trigger autorun when chained (mixed invocations)', () => { + it('should trigger effect when chained (mixed invocations)', () => { const value: any = observable({ foo: 0 }) const getter1 = jest.fn(() => value.foo) const getter2 = jest.fn(() => { @@ -108,7 +108,7 @@ describe('observer/computed', () => { const c2 = computed(getter2) let dummy - autorun(() => { + effect(() => { dummy = c1() + c2() }) expect(dummy).toBe(1) @@ -126,13 +126,13 @@ describe('observer/computed', () => { const value: any = observable({}) const cValue = computed(() => value.foo) let dummy - autorun(() => { + effect(() => { dummy = cValue() }) expect(dummy).toBe(undefined) value.foo = 1 expect(dummy).toBe(1) - stop(cValue.runner) + stop(cValue.effect) value.foo = 2 expect(dummy).toBe(1) }) diff --git a/packages/observer/__tests__/autorun.spec.ts b/packages/observer/__tests__/effect.spec.ts similarity index 84% rename from packages/observer/__tests__/autorun.spec.ts rename to packages/observer/__tests__/effect.spec.ts index 31f0470f..fcbc31fb 100644 --- a/packages/observer/__tests__/autorun.spec.ts +++ b/packages/observer/__tests__/effect.spec.ts @@ -1,25 +1,25 @@ import { observable, - autorun, + effect, stop, unwrap, OperationTypes, DebuggerEvent, markNonReactive } from '../src/index' -import { ITERATE_KEY } from '../src/autorun' +import { ITERATE_KEY } from '../src/effect' -describe('observer/autorun', () => { - it('should run the passed function once (wrapped by a autorun)', () => { +describe('observer/effect', () => { + it('should run the passed function once (wrapped by a effect)', () => { const fnSpy = jest.fn(() => {}) - autorun(fnSpy) + effect(fnSpy) expect(fnSpy).toHaveBeenCalledTimes(1) }) it('should observe basic properties', () => { let dummy const counter = observable({ num: 0 }) - autorun(() => (dummy = counter.num)) + effect(() => (dummy = counter.num)) expect(dummy).toBe(0) counter.num = 7 @@ -29,18 +29,18 @@ describe('observer/autorun', () => { it('should observe multiple properties', () => { let dummy const counter = observable({ num1: 0, num2: 0 }) - autorun(() => (dummy = counter.num1 + counter.num1 + counter.num2)) + effect(() => (dummy = counter.num1 + counter.num1 + counter.num2)) expect(dummy).toBe(0) counter.num1 = counter.num2 = 7 expect(dummy).toBe(21) }) - it('should handle multiple autoruns', () => { + it('should handle multiple effects', () => { let dummy1, dummy2 const counter = observable({ num: 0 }) - autorun(() => (dummy1 = counter.num)) - autorun(() => (dummy2 = counter.num)) + effect(() => (dummy1 = counter.num)) + effect(() => (dummy2 = counter.num)) expect(dummy1).toBe(0) expect(dummy2).toBe(0) @@ -52,7 +52,7 @@ describe('observer/autorun', () => { it('should observe nested properties', () => { let dummy const counter = observable({ nested: { num: 0 } }) - autorun(() => (dummy = counter.nested.num)) + effect(() => (dummy = counter.nested.num)) expect(dummy).toBe(0) counter.nested.num = 8 @@ -62,7 +62,7 @@ describe('observer/autorun', () => { it('should observe delete operations', () => { let dummy const obj = observable({ prop: 'value' }) - autorun(() => (dummy = obj.prop)) + effect(() => (dummy = obj.prop)) expect(dummy).toBe('value') delete obj.prop @@ -72,7 +72,7 @@ describe('observer/autorun', () => { it('should observe has operations', () => { let dummy const obj: any = observable({ prop: 'value' }) - autorun(() => (dummy = 'prop' in obj)) + effect(() => (dummy = 'prop' in obj)) expect(dummy).toBe(true) delete obj.prop @@ -86,7 +86,7 @@ describe('observer/autorun', () => { const counter = observable({ num: 0 }) const parentCounter = observable({ num: 2 }) Object.setPrototypeOf(counter, parentCounter) - autorun(() => (dummy = counter.num)) + effect(() => (dummy = counter.num)) expect(dummy).toBe(0) delete counter.num @@ -102,7 +102,7 @@ describe('observer/autorun', () => { const counter = observable({ num: 0 }) const parentCounter = observable({ num: 2 }) Object.setPrototypeOf(counter, parentCounter) - autorun(() => (dummy = 'num' in counter)) + effect(() => (dummy = 'num' in counter)) expect(dummy).toBe(true) delete counter.num @@ -125,8 +125,8 @@ describe('observer/autorun', () => { } }) Object.setPrototypeOf(obj, parent) - autorun(() => (dummy = obj.prop)) - autorun(() => (parentDummy = parent.prop)) + effect(() => (dummy = obj.prop)) + effect(() => (parentDummy = parent.prop)) expect(dummy).toBe(undefined) expect(parentDummy).toBe(undefined) @@ -142,7 +142,7 @@ describe('observer/autorun', () => { it('should observe function call chains', () => { let dummy const counter = observable({ num: 0 }) - autorun(() => (dummy = getNum())) + effect(() => (dummy = getNum())) function getNum() { return counter.num @@ -156,7 +156,7 @@ describe('observer/autorun', () => { it('should observe iteration', () => { let dummy const list = observable(['Hello']) - autorun(() => (dummy = list.join(' '))) + effect(() => (dummy = list.join(' '))) expect(dummy).toBe('Hello') list.push('World!') @@ -168,7 +168,7 @@ describe('observer/autorun', () => { it('should observe implicit array length changes', () => { let dummy const list = observable(['Hello']) - autorun(() => (dummy = list.join(' '))) + effect(() => (dummy = list.join(' '))) expect(dummy).toBe('Hello') list[1] = 'World!' @@ -181,7 +181,7 @@ describe('observer/autorun', () => { let dummy const list: any[] = observable([]) list[1] = 'World!' - autorun(() => (dummy = list.join(' '))) + effect(() => (dummy = list.join(' '))) expect(dummy).toBe(' World!') list[0] = 'Hello' @@ -193,7 +193,7 @@ describe('observer/autorun', () => { it('should observe enumeration', () => { let dummy = 0 const numbers: any = observable({ num1: 3 }) - autorun(() => { + effect(() => { dummy = 0 for (let key in numbers) { dummy += numbers[key] @@ -211,8 +211,8 @@ describe('observer/autorun', () => { const key = Symbol('symbol keyed prop') let dummy, hasDummy const obj = observable({ [key]: 'value' }) - autorun(() => (dummy = obj[key])) - autorun(() => (hasDummy = key in obj)) + effect(() => (dummy = obj[key])) + effect(() => (hasDummy = key in obj)) expect(dummy).toBe('value') expect(hasDummy).toBe(true) @@ -227,7 +227,7 @@ describe('observer/autorun', () => { const key = Symbol.isConcatSpreadable let dummy const array: any = observable([]) - autorun(() => (dummy = array[key])) + effect(() => (dummy = array[key])) expect(array[key]).toBe(undefined) expect(dummy).toBe(undefined) @@ -242,7 +242,7 @@ describe('observer/autorun', () => { let dummy const obj = observable({ func: oldFunc }) - autorun(() => (dummy = obj.func)) + effect(() => (dummy = obj.func)) expect(dummy).toBe(oldFunc) obj.func = newFunc @@ -255,8 +255,8 @@ describe('observer/autorun', () => { const getSpy = jest.fn(() => (getDummy = obj.prop)) const hasSpy = jest.fn(() => (hasDummy = 'prop' in obj)) - autorun(getSpy) - autorun(hasSpy) + effect(getSpy) + effect(hasSpy) expect(getDummy).toBe('value') expect(hasDummy).toBe(true) @@ -270,7 +270,7 @@ describe('observer/autorun', () => { it('should not observe raw mutations', () => { let dummy const obj: any = observable() - autorun(() => (dummy = unwrap(obj).prop)) + effect(() => (dummy = unwrap(obj).prop)) expect(dummy).toBe(undefined) obj.prop = 'value' @@ -280,7 +280,7 @@ describe('observer/autorun', () => { it('should not be triggered by raw mutations', () => { let dummy const obj: any = observable() - autorun(() => (dummy = obj.prop)) + effect(() => (dummy = obj.prop)) expect(dummy).toBe(undefined) unwrap(obj).prop = 'value' @@ -299,8 +299,8 @@ describe('observer/autorun', () => { } }) Object.setPrototypeOf(obj, parent) - autorun(() => (dummy = obj.prop)) - autorun(() => (parentDummy = parent.prop)) + effect(() => (dummy = obj.prop)) + effect(() => (parentDummy = parent.prop)) expect(dummy).toBe(undefined) expect(parentDummy).toBe(undefined) @@ -313,7 +313,7 @@ describe('observer/autorun', () => { const counter = observable({ num: 0 }) const counterSpy = jest.fn(() => counter.num++) - autorun(counterSpy) + effect(counterSpy) expect(counter.num).toBe(1) expect(counterSpy).toHaveBeenCalledTimes(1) counter.num = 4 @@ -329,18 +329,18 @@ describe('observer/autorun', () => { numSpy() } }) - autorun(numSpy) + effect(numSpy) expect(counter.num).toEqual(10) expect(numSpy).toHaveBeenCalledTimes(10) }) - it('should avoid infinite loops with other autoruns', () => { + it('should avoid infinite loops with other effects', () => { const nums = observable({ num1: 0, num2: 1 }) const spy1 = jest.fn(() => (nums.num1 = nums.num2)) const spy2 = jest.fn(() => (nums.num2 = nums.num1)) - autorun(spy1) - autorun(spy2) + effect(spy1) + effect(spy2) expect(nums.num1).toBe(1) expect(nums.num2).toBe(1) expect(spy1).toHaveBeenCalledTimes(1) @@ -361,12 +361,12 @@ describe('observer/autorun', () => { function greet() { return 'Hello World' } - const autorun1 = autorun(greet) - const autorun2 = autorun(greet) - expect(typeof autorun1).toBe('function') - expect(typeof autorun2).toBe('function') - expect(autorun1).not.toBe(greet) - expect(autorun1).not.toBe(autorun2) + const effect1 = effect(greet) + const effect2 = effect(greet) + expect(typeof effect1).toBe('function') + expect(typeof effect2).toBe('function') + expect(effect1).not.toBe(greet) + expect(effect1).not.toBe(effect2) }) it('should discover new branches while running automatically', () => { @@ -376,7 +376,7 @@ describe('observer/autorun', () => { const conditionalSpy = jest.fn(() => { dummy = obj.run ? obj.prop : 'other' }) - autorun(conditionalSpy) + effect(conditionalSpy) expect(dummy).toBe('other') expect(conditionalSpy).toHaveBeenCalledTimes(1) @@ -395,7 +395,7 @@ describe('observer/autorun', () => { let dummy let run = false const obj = observable({ prop: 'value' }) - const runner = autorun(() => { + const runner = effect(() => { dummy = run ? obj.prop : 'other' }) @@ -416,7 +416,7 @@ describe('observer/autorun', () => { const conditionalSpy = jest.fn(() => { dummy = obj.run ? obj.prop : 'other' }) - autorun(conditionalSpy) + effect(conditionalSpy) expect(dummy).toBe('value') expect(conditionalSpy).toHaveBeenCalledTimes(1) @@ -428,9 +428,9 @@ describe('observer/autorun', () => { expect(conditionalSpy).toHaveBeenCalledTimes(2) }) - it('should not double wrap if the passed function is a autorun', () => { - const runner = autorun(() => {}) - const otherRunner = autorun(runner) + it('should not double wrap if the passed function is a effect', () => { + const runner = effect(() => {}) + const otherRunner = effect(runner) expect(runner).not.toBe(otherRunner) expect(runner.raw).toBe(otherRunner.raw) }) @@ -444,7 +444,7 @@ describe('observer/autorun', () => { } dummy = obj.prop }) - autorun(fnSpy) + effect(fnSpy) expect(fnSpy).toHaveBeenCalledTimes(1) obj.prop = 16 @@ -452,33 +452,33 @@ describe('observer/autorun', () => { expect(fnSpy).toHaveBeenCalledTimes(2) }) - it('should allow nested autoruns', () => { + it('should allow nested effects', () => { const nums = observable({ num1: 0, num2: 1, num3: 2 }) const dummy: any = {} const childSpy = jest.fn(() => (dummy.num1 = nums.num1)) - const childautorun = autorun(childSpy) + const childeffect = effect(childSpy) const parentSpy = jest.fn(() => { dummy.num2 = nums.num2 - childautorun() + childeffect() dummy.num3 = nums.num3 }) - autorun(parentSpy) + effect(parentSpy) expect(dummy).toEqual({ num1: 0, num2: 1, num3: 2 }) expect(parentSpy).toHaveBeenCalledTimes(1) expect(childSpy).toHaveBeenCalledTimes(2) - // this should only call the childautorun + // this should only call the childeffect nums.num1 = 4 expect(dummy).toEqual({ num1: 4, num2: 1, num3: 2 }) expect(parentSpy).toHaveBeenCalledTimes(1) expect(childSpy).toHaveBeenCalledTimes(3) - // this calls the parentautorun, which calls the childautorun once + // this calls the parenteffect, which calls the childeffect once nums.num2 = 10 expect(dummy).toEqual({ num1: 4, num2: 10, num3: 2 }) expect(parentSpy).toHaveBeenCalledTimes(2) expect(childSpy).toHaveBeenCalledTimes(4) - // this calls the parentautorun, which calls the childautorun once + // this calls the parenteffect, which calls the childeffect once nums.num3 = 7 expect(dummy).toEqual({ num1: 4, num2: 10, num3: 7 }) expect(parentSpy).toHaveBeenCalledTimes(3) @@ -497,7 +497,7 @@ describe('observer/autorun', () => { } const model = observable(new Model()) let dummy - autorun(() => { + effect(() => { dummy = model.count }) expect(dummy).toBe(0) @@ -511,7 +511,7 @@ describe('observer/autorun', () => { runner = _runner }) const obj = observable({ foo: 1 }) - autorun( + effect( () => { dummy = obj.foo }, @@ -537,7 +537,7 @@ describe('observer/autorun', () => { events.push(e) }) const obj = observable({ foo: 1, bar: 2 }) - const runner = autorun( + const runner = effect( () => { dummy = obj.foo dummy = 'bar' in obj @@ -549,19 +549,19 @@ describe('observer/autorun', () => { expect(onTrack).toHaveBeenCalledTimes(3) expect(events).toEqual([ { - runner, + effect: runner, target: unwrap(obj), type: OperationTypes.GET, key: 'foo' }, { - runner, + effect: runner, target: unwrap(obj), type: OperationTypes.HAS, key: 'bar' }, { - runner, + effect: runner, target: unwrap(obj), type: OperationTypes.ITERATE, key: ITERATE_KEY @@ -576,7 +576,7 @@ describe('observer/autorun', () => { events.push(e) }) const obj = observable({ foo: 1 }) - const runner = autorun( + const runner = effect( () => { dummy = obj.foo }, @@ -587,7 +587,7 @@ describe('observer/autorun', () => { expect(dummy).toBe(2) expect(onTrigger).toHaveBeenCalledTimes(1) expect(events[0]).toEqual({ - runner, + effect: runner, target: unwrap(obj), type: OperationTypes.SET, key: 'foo', @@ -599,7 +599,7 @@ describe('observer/autorun', () => { expect(dummy).toBeUndefined() expect(onTrigger).toHaveBeenCalledTimes(2) expect(events[1]).toEqual({ - runner, + effect: runner, target: unwrap(obj), type: OperationTypes.DELETE, key: 'foo', @@ -610,7 +610,7 @@ describe('observer/autorun', () => { it('stop', () => { let dummy const obj = observable({ prop: 1 }) - const runner = autorun(() => { + const runner = effect(() => { dummy = obj.prop }) obj.prop = 2 @@ -619,7 +619,7 @@ describe('observer/autorun', () => { obj.prop = 3 expect(dummy).toBe(2) - // stopped runner should still be manually callable + // stopped effect should still be manually callable runner() expect(dummy).toBe(3) }) @@ -631,7 +631,7 @@ describe('observer/autorun', () => { }) }) let dummy - autorun(() => { + effect(() => { dummy = obj.foo.prop }) expect(dummy).toBe(0) diff --git a/packages/observer/__tests__/immutable.spec.ts b/packages/observer/__tests__/immutable.spec.ts index 71ce192a..07c7fe93 100644 --- a/packages/observer/__tests__/immutable.spec.ts +++ b/packages/observer/__tests__/immutable.spec.ts @@ -8,7 +8,7 @@ import { markImmutable, lock, unlock, - autorun + effect } from '../src' describe('observer/immutable', () => { @@ -75,10 +75,10 @@ describe('observer/immutable', () => { expect(warn).not.toHaveBeenCalled() }) - it('should not trigger autoruns when locked', () => { + it('should not trigger effects when locked', () => { const observed = immutable({ a: 1 }) let dummy - autorun(() => { + effect(() => { dummy = observed.a }) expect(dummy).toBe(1) @@ -87,10 +87,10 @@ describe('observer/immutable', () => { expect(dummy).toBe(1) }) - it('should trigger autoruns when unlocked', () => { + it('should trigger effects when unlocked', () => { const observed = immutable({ a: 1 }) let dummy - autorun(() => { + effect(() => { dummy = observed.a }) expect(dummy).toBe(1) @@ -161,10 +161,10 @@ describe('observer/immutable', () => { expect(warn).not.toHaveBeenCalled() }) - it('should not trigger autoruns when locked', () => { + it('should not trigger effects when locked', () => { const observed = immutable([{ a: 1 }]) let dummy - autorun(() => { + effect(() => { dummy = observed[0].a }) expect(dummy).toBe(1) @@ -176,10 +176,10 @@ describe('observer/immutable', () => { expect(dummy).toBe(1) }) - it('should trigger autoruns when unlocked', () => { + it('should trigger effects when unlocked', () => { const observed = immutable([{ a: 1 }]) let dummy - autorun(() => { + effect(() => { dummy = observed[0].a }) expect(dummy).toBe(1) @@ -220,11 +220,11 @@ describe('observer/immutable', () => { expect(isImmutable(original.get(key1))).toBe(false) }) - test('should not allow mutation & not trigger autorun', () => { + test('should not allow mutation & not trigger effect', () => { const map = immutable(new Collection()) const key = {} let dummy - autorun(() => { + effect(() => { dummy = map.get(key) }) expect(dummy).toBeUndefined() @@ -234,12 +234,12 @@ describe('observer/immutable', () => { expect(warn).toHaveBeenCalledTimes(1) }) - test('should allow mutation & trigger autorun when unlocked', () => { + test('should allow mutation & trigger effect when unlocked', () => { const map = immutable(new Collection()) const isWeak = Collection === WeakMap const key = {} let dummy - autorun(() => { + effect(() => { dummy = map.get(key) + (isWeak ? 0 : map.size) }) expect(dummy).toBeNaN() @@ -289,11 +289,11 @@ describe('observer/immutable', () => { expect(original.has(observable(key1))).toBe(false) }) - test('should not allow mutation & not trigger autorun', () => { + test('should not allow mutation & not trigger effect', () => { const set = immutable(new Collection()) const key = {} let dummy - autorun(() => { + effect(() => { dummy = set.has(key) }) expect(dummy).toBe(false) @@ -303,11 +303,11 @@ describe('observer/immutable', () => { expect(warn).toHaveBeenCalledTimes(1) }) - test('should allow mutation & trigger autorun when unlocked', () => { + test('should allow mutation & trigger effect when unlocked', () => { const set = immutable(new Collection()) const key = {} let dummy - autorun(() => { + effect(() => { dummy = set.has(key) }) expect(dummy).toBe(false) diff --git a/packages/observer/src/autorun.ts b/packages/observer/src/autorun.ts deleted file mode 100644 index f47eb591..00000000 --- a/packages/observer/src/autorun.ts +++ /dev/null @@ -1,180 +0,0 @@ -import { OperationTypes } from './operations' -import { Dep, KeyToDepMap, targetMap } from './state' - -export interface Autorun { - (): any - isAutorun: true - active: boolean - raw: Function - deps: Array - computed?: boolean - scheduler?: Scheduler - onTrack?: Debugger - onTrigger?: Debugger -} - -export interface AutorunOptions { - lazy?: boolean - scheduler?: Scheduler - onTrack?: Debugger - onTrigger?: Debugger -} - -export type Scheduler = (run: () => any) => void - -export type DebuggerEvent = { - runner: Autorun - target: any - type: OperationTypes - key: string | symbol | undefined -} - -export type Debugger = (event: DebuggerEvent) => void - -export const activeAutorunStack: Autorun[] = [] - -export const ITERATE_KEY = Symbol('iterate') - -export function createAutorun(fn: Function, options: AutorunOptions): Autorun { - const runner = function runner(...args): any { - return run(runner as Autorun, fn, args) - } as Autorun - runner.isAutorun = true - runner.active = true - runner.raw = fn - runner.scheduler = options.scheduler - runner.onTrack = options.onTrack - runner.onTrigger = options.onTrigger - runner.deps = [] - return runner -} - -function run(runner: Autorun, fn: Function, args: any[]): any { - if (!runner.active) { - return fn(...args) - } - if (activeAutorunStack.indexOf(runner) === -1) { - cleanup(runner) - try { - activeAutorunStack.push(runner) - return fn(...args) - } finally { - activeAutorunStack.pop() - } - } -} - -export function cleanup(runner: Autorun) { - for (let i = 0; i < runner.deps.length; i++) { - runner.deps[i].delete(runner) - } - runner.deps = [] -} - -export function track( - target: any, - type: OperationTypes, - key?: string | symbol -) { - const runner = activeAutorunStack[activeAutorunStack.length - 1] - if (runner) { - if (type === OperationTypes.ITERATE) { - key = ITERATE_KEY - } - // keyMap must exist because only an observed target can call this function - const depsMap = targetMap.get(target) as KeyToDepMap - let dep = depsMap.get(key as string | symbol) - if (!dep) { - depsMap.set(key as string | symbol, (dep = new Set())) - } - if (!dep.has(runner)) { - dep.add(runner) - runner.deps.push(dep) - if (__DEV__ && runner.onTrack) { - runner.onTrack({ - runner, - target, - type, - key - }) - } - } - } -} - -export function trigger( - target: any, - type: OperationTypes, - key?: string | symbol, - extraInfo?: any -) { - const depsMap = targetMap.get(target) as KeyToDepMap - const runners = new Set() - const computedRunners = new Set() - if (type === OperationTypes.CLEAR) { - // collection being cleared, trigger all runners for target - depsMap.forEach(dep => { - addRunners(runners, computedRunners, dep) - }) - } else { - // schedule runs for SET | ADD | DELETE - if (key !== void 0) { - addRunners(runners, computedRunners, depsMap.get(key as string | symbol)) - } - // also run for iteration key on ADD | DELETE - if (type === OperationTypes.ADD || type === OperationTypes.DELETE) { - const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY - addRunners(runners, computedRunners, depsMap.get(iterationKey)) - } - } - const run = (runner: Autorun) => { - scheduleRun(runner, target, type, key, extraInfo) - } - // Important: computed runners must be run first so that computed getters - // can be invalidated before any normal runners that depend on them are run. - computedRunners.forEach(run) - runners.forEach(run) -} - -function addRunners( - runners: Set, - computedRunners: Set, - runnersToAdd: Set | undefined -) { - if (runnersToAdd !== void 0) { - runnersToAdd.forEach(runner => { - if (runner.computed) { - computedRunners.add(runner) - } else { - runners.add(runner) - } - }) - } -} - -function scheduleRun( - runner: Autorun, - target: any, - type: OperationTypes, - key: string | symbol | undefined, - extraInfo: any -) { - if (__DEV__ && runner.onTrigger) { - runner.onTrigger( - Object.assign( - { - runner, - target, - key, - type - }, - extraInfo - ) - ) - } - if (runner.scheduler !== void 0) { - runner.scheduler(runner) - } else { - runner() - } -} diff --git a/packages/observer/src/baseHandlers.ts b/packages/observer/src/baseHandlers.ts index 0be06e3a..62515469 100644 --- a/packages/observer/src/baseHandlers.ts +++ b/packages/observer/src/baseHandlers.ts @@ -1,6 +1,6 @@ import { observable, immutable, unwrap } from './index' import { OperationTypes } from './operations' -import { track, trigger } from './autorun' +import { track, trigger } from './effect' import { LOCKED } from './lock' import { isObject } from '@vue/shared' diff --git a/packages/observer/src/collectionHandlers.ts b/packages/observer/src/collectionHandlers.ts index 8fd40a94..75e32c7b 100644 --- a/packages/observer/src/collectionHandlers.ts +++ b/packages/observer/src/collectionHandlers.ts @@ -1,5 +1,5 @@ import { unwrap, observable, immutable } from './index' -import { track, trigger } from './autorun' +import { track, trigger } from './effect' import { OperationTypes } from './operations' import { LOCKED } from './lock' import { isObject } from '@vue/shared' diff --git a/packages/observer/src/computed.ts b/packages/observer/src/computed.ts index 31033bbb..b74f8ca5 100644 --- a/packages/observer/src/computed.ts +++ b/packages/observer/src/computed.ts @@ -1,9 +1,9 @@ -import { autorun } from './index' -import { Autorun, activeAutorunStack } from './autorun' +import { effect } from './index' +import { ReactiveEffect, activeReactiveEffectStack } from './effect' export interface ComputedGetter { (): T - runner: Autorun + effect: ReactiveEffect } export function computed( @@ -12,7 +12,7 @@ export function computed( ): ComputedGetter { let dirty: boolean = true let value: any = undefined - const runner = autorun(() => getter.call(context, context), { + const runner = effect(() => getter.call(context, context), { lazy: true, scheduler: () => { dirty = true @@ -23,21 +23,22 @@ export function computed( value = runner() dirty = false } - // When computed autoruns are accessed in a parent autorun, the parent + // When computed effects are accessed in a parent effect, the parent // should track all the dependencies the computed property has tracked. // This should also apply for chained computed properties. trackChildRun(runner) return value }) as ComputedGetter - // expose runner so computed can be stopped - computedGetter.runner = runner - // mark runner as computed so that it gets priority during trigger + // expose effect so computed can be stopped + computedGetter.effect = runner + // mark effect as computed so that it gets priority during trigger runner.computed = true return computedGetter } -function trackChildRun(childRunner: Autorun) { - const parentRunner = activeAutorunStack[activeAutorunStack.length - 1] +function trackChildRun(childRunner: ReactiveEffect) { + const parentRunner = + activeReactiveEffectStack[activeReactiveEffectStack.length - 1] if (parentRunner) { for (let i = 0; i < childRunner.deps.length; i++) { const dep = childRunner.deps[i] diff --git a/packages/observer/src/effect.ts b/packages/observer/src/effect.ts new file mode 100644 index 00000000..e4ae3d92 --- /dev/null +++ b/packages/observer/src/effect.ts @@ -0,0 +1,183 @@ +import { OperationTypes } from './operations' +import { Dep, KeyToDepMap, targetMap } from './state' + +export interface ReactiveEffect { + (): any + isEffect: true + active: boolean + raw: Function + deps: Array + computed?: boolean + scheduler?: Scheduler + onTrack?: Debugger + onTrigger?: Debugger +} + +export interface ReactiveEffectOptions { + lazy?: boolean + scheduler?: Scheduler + onTrack?: Debugger + onTrigger?: Debugger +} + +export type Scheduler = (run: () => any) => void + +export type DebuggerEvent = { + effect: ReactiveEffect + target: any + type: OperationTypes + key: string | symbol | undefined +} + +export type Debugger = (event: DebuggerEvent) => void + +export const activeReactiveEffectStack: ReactiveEffect[] = [] + +export const ITERATE_KEY = Symbol('iterate') + +export function createReactiveEffect( + fn: Function, + options: ReactiveEffectOptions +): ReactiveEffect { + const effect = function effect(...args): any { + return run(effect as ReactiveEffect, fn, args) + } as ReactiveEffect + effect.isEffect = true + effect.active = true + effect.raw = fn + effect.scheduler = options.scheduler + effect.onTrack = options.onTrack + effect.onTrigger = options.onTrigger + effect.deps = [] + return effect +} + +function run(effect: ReactiveEffect, fn: Function, args: any[]): any { + if (!effect.active) { + return fn(...args) + } + if (activeReactiveEffectStack.indexOf(effect) === -1) { + cleanup(effect) + try { + activeReactiveEffectStack.push(effect) + return fn(...args) + } finally { + activeReactiveEffectStack.pop() + } + } +} + +export function cleanup(effect: ReactiveEffect) { + for (let i = 0; i < effect.deps.length; i++) { + effect.deps[i].delete(effect) + } + effect.deps = [] +} + +export function track( + target: any, + type: OperationTypes, + key?: string | symbol +) { + const effect = activeReactiveEffectStack[activeReactiveEffectStack.length - 1] + if (effect) { + if (type === OperationTypes.ITERATE) { + key = ITERATE_KEY + } + // keyMap must exist because only an observed target can call this function + const depsMap = targetMap.get(target) as KeyToDepMap + let dep = depsMap.get(key as string | symbol) + if (!dep) { + depsMap.set(key as string | symbol, (dep = new Set())) + } + if (!dep.has(effect)) { + dep.add(effect) + effect.deps.push(dep) + if (__DEV__ && effect.onTrack) { + effect.onTrack({ + effect, + target, + type, + key + }) + } + } + } +} + +export function trigger( + target: any, + type: OperationTypes, + key?: string | symbol, + extraInfo?: any +) { + const depsMap = targetMap.get(target) as KeyToDepMap + const effects = new Set() + const computedRunners = new Set() + if (type === OperationTypes.CLEAR) { + // collection being cleared, trigger all effects for target + depsMap.forEach(dep => { + addRunners(effects, computedRunners, dep) + }) + } else { + // schedule runs for SET | ADD | DELETE + if (key !== void 0) { + addRunners(effects, computedRunners, depsMap.get(key as string | symbol)) + } + // also run for iteration key on ADD | DELETE + if (type === OperationTypes.ADD || type === OperationTypes.DELETE) { + const iterationKey = Array.isArray(target) ? 'length' : ITERATE_KEY + addRunners(effects, computedRunners, depsMap.get(iterationKey)) + } + } + const run = (effect: ReactiveEffect) => { + scheduleRun(effect, target, type, key, extraInfo) + } + // Important: computed effects must be run first so that computed getters + // can be invalidated before any normal effects that depend on them are run. + computedRunners.forEach(run) + effects.forEach(run) +} + +function addRunners( + effects: Set, + computedRunners: Set, + effectsToAdd: Set | undefined +) { + if (effectsToAdd !== void 0) { + effectsToAdd.forEach(effect => { + if (effect.computed) { + computedRunners.add(effect) + } else { + effects.add(effect) + } + }) + } +} + +function scheduleRun( + effect: ReactiveEffect, + target: any, + type: OperationTypes, + key: string | symbol | undefined, + extraInfo: any +) { + if (__DEV__ && effect.onTrigger) { + effect.onTrigger( + Object.assign( + { + effect, + target, + key, + type + }, + extraInfo + ) + ) + } + if (effect.scheduler !== void 0) { + effect.scheduler(effect) + } else { + effect() + } +} diff --git a/packages/observer/src/index.ts b/packages/observer/src/index.ts index 005b82d3..a2cdf806 100644 --- a/packages/observer/src/index.ts +++ b/packages/observer/src/index.ts @@ -17,14 +17,14 @@ import { } from './state' import { - createAutorun, + createReactiveEffect, cleanup, - Autorun, - AutorunOptions, + ReactiveEffect, + ReactiveEffectOptions, DebuggerEvent -} from './autorun' +} from './effect' -export { Autorun, AutorunOptions, DebuggerEvent } +export { ReactiveEffect, ReactiveEffectOptions, DebuggerEvent } export { OperationTypes } from './operations' export { computed, ComputedGetter } from './computed' export { lock, unlock } from './lock' @@ -114,24 +114,24 @@ function createObservable( return observed } -export function autorun( +export function effect( fn: Function, - options: AutorunOptions = EMPTY_OBJ -): Autorun { - if ((fn as Autorun).isAutorun) { - fn = (fn as Autorun).raw + options: ReactiveEffectOptions = EMPTY_OBJ +): ReactiveEffect { + if ((fn as ReactiveEffect).isEffect) { + fn = (fn as ReactiveEffect).raw } - const runner = createAutorun(fn, options) + const effect = createReactiveEffect(fn, options) if (!options.lazy) { - runner() + effect() } - return runner + return effect } -export function stop(runner: Autorun) { - if (runner.active) { - cleanup(runner) - runner.active = false +export function stop(effect: ReactiveEffect) { + if (effect.active) { + cleanup(effect) + effect.active = false } } diff --git a/packages/observer/src/state.ts b/packages/observer/src/state.ts index 9efbe373..eee65d22 100644 --- a/packages/observer/src/state.ts +++ b/packages/observer/src/state.ts @@ -1,10 +1,10 @@ -import { Autorun } from './autorun' +import { ReactiveEffect } from './effect' // The main WeakMap that stores {target -> key -> dep} connections. // Conceptually, it's easier to think of a dependency as a Dep class // which maintains a Set of subscribers, but we simply store them as // raw Sets to reduce memory overhead. -export type Dep = Set +export type Dep = Set export type KeyToDepMap = Map export const targetMap: WeakMap = new WeakMap() diff --git a/packages/runtime-core/__tests__/fragment.spec.ts b/packages/runtime-core/__tests__/fragment.spec.ts index 0096cc77..52157472 100644 --- a/packages/runtime-core/__tests__/fragment.spec.ts +++ b/packages/runtime-core/__tests__/fragment.spec.ts @@ -12,7 +12,9 @@ import { nextTick, resetOps, dumpOps, - NodeOpTypes + NodeOpTypes, + createFragment, + createTextVNode } from '@vue/runtime-test' describe('Fragments', () => { @@ -57,9 +59,12 @@ describe('Fragments', () => { class App extends Component { render() { return state.ok - ? h.f([h('div', 'one'), h.t('two')], ChildrenFlags.NONE_KEYED_VNODES) - : h.f( - [h('div', 'foo'), h.t('bar'), h.t('baz')], + ? createFragment( + [h('div', 'one'), createTextVNode('two')], + ChildrenFlags.NONE_KEYED_VNODES + ) + : createFragment( + [h('div', 'foo'), createTextVNode('bar'), createTextVNode('baz')], ChildrenFlags.NONE_KEYED_VNODES ) } diff --git a/packages/runtime-core/src/component.ts b/packages/runtime-core/src/component.ts index 6de11b64..72d5d873 100644 --- a/packages/runtime-core/src/component.ts +++ b/packages/runtime-core/src/component.ts @@ -8,7 +8,7 @@ import { WatchOptions } from './componentOptions' import { setupWatcher } from './componentWatch' -import { Autorun, DebuggerEvent, ComputedGetter } from '@vue/observer' +import { ReactiveEffect, DebuggerEvent, ComputedGetter } from '@vue/observer' import { nextTick } from '@vue/scheduler' import { ErrorTypes } from './errorHandling' import { initializeComponentInstance } from './componentUtils' @@ -101,7 +101,7 @@ export interface ComponentInstance

$children: ComponentInstance[] $options: ComponentOptions - _updateHandle: Autorun + _update: ReactiveEffect _queueJob: ((fn: () => void) => void) _self: ComponentInstance // on proxies only } @@ -128,11 +128,11 @@ class InternalComponent implements PublicInstanceMethods { _rawData: Data | null = null _computedGetters: Record | null = null - _watchHandles: Set | null = null + _watchHandles: Set | null = null _mounted: boolean = false _unmounted: boolean = false _events: { [event: string]: Function[] | null } | null = null - _updateHandle: Autorun | null = null + _update: ReactiveEffect | null = null _queueJob: ((fn: () => void) => void) | null = null _isVue: boolean = true _inactiveRoot: boolean = false diff --git a/packages/runtime-core/src/componentComputed.ts b/packages/runtime-core/src/componentComputed.ts index 3ab80a67..0cc7796e 100644 --- a/packages/runtime-core/src/componentComputed.ts +++ b/packages/runtime-core/src/componentComputed.ts @@ -25,7 +25,7 @@ export function teardownComputed(instance: ComponentInstance) { const handles = instance._computedGetters if (handles !== null) { for (const key in handles) { - stop(handles[key].runner) + stop(handles[key].effect) } } } diff --git a/packages/runtime-core/src/componentUtils.ts b/packages/runtime-core/src/componentUtils.ts index 48e457a6..3582b57f 100644 --- a/packages/runtime-core/src/componentUtils.ts +++ b/packages/runtime-core/src/componentUtils.ts @@ -158,7 +158,7 @@ export function teardownComponentInstance(instance: ComponentInstance) { 1 ) } - stop(instance._updateHandle) + stop(instance._update) teardownComputed(instance) teardownWatch(instance) } diff --git a/packages/runtime-core/src/componentWatch.ts b/packages/runtime-core/src/componentWatch.ts index 183c230b..ad607e53 100644 --- a/packages/runtime-core/src/componentWatch.ts +++ b/packages/runtime-core/src/componentWatch.ts @@ -8,7 +8,7 @@ import { } from '@vue/shared' import { ComponentInstance } from './component' import { ComponentWatchOptions, WatchOptions } from './componentOptions' -import { autorun, stop } from '@vue/observer' +import { effect, stop } from '@vue/observer' import { queueJob } from '@vue/scheduler' import { handleError, ErrorTypes } from './errorHandling' import { warn } from './warning' @@ -70,7 +70,7 @@ export function setupWatcher( } } - const runner = autorun(getter, { + const runner = effect(getter, { lazy: true, scheduler: options.sync ? applyCb diff --git a/packages/runtime-core/src/createRenderer.ts b/packages/runtime-core/src/createRenderer.ts index 27a008e9..08b87060 100644 --- a/packages/runtime-core/src/createRenderer.ts +++ b/packages/runtime-core/src/createRenderer.ts @@ -1,15 +1,15 @@ import { - autorun, - stop, - Autorun, + effect as createReactiveEffect, + stop as stopReactiveEffect, + ReactiveEffect, immutable, - AutorunOptions + ReactiveEffectOptions } from '@vue/observer' import { queueJob, handleSchedulerError, nextTick, - queueEffect, + queuePostEffect, flushEffects, queueNodeOp } from '@vue/scheduler' @@ -78,7 +78,7 @@ export interface RendererOptions { export interface FunctionalHandle { prev: VNode next: VNode - update: Autorun + update: ReactiveEffect container: RenderNode | null } @@ -206,12 +206,12 @@ export function createRenderer(options: RendererOptions) { queueInsertOrAppend(container, el, endNode) } if (ref) { - queueEffect(() => { + queuePostEffect(() => { ref(el) }) } if (data != null && data.vnodeMounted) { - queueEffect(() => { + queuePostEffect(() => { data.vnodeMounted(vnode) }) } @@ -272,7 +272,7 @@ export function createRenderer(options: RendererOptions) { }) const doMount = () => { - handle.update = autorun( + handle.update = createReactiveEffect( () => { if (!handle.next) { // initial mount @@ -280,7 +280,7 @@ export function createRenderer(options: RendererOptions) { pushWarningContext(vnode) } const subTree = (vnode.children = renderFunctionalRoot(vnode)) - queueEffect(() => { + queuePostEffect(() => { vnode.el = subTree.el as RenderNode }) mount(subTree, container, vnode as MountedVNode, isSVG, endNode) @@ -306,7 +306,7 @@ export function createRenderer(options: RendererOptions) { doMount() // cleanup if mount is invalidated before committed return () => { - stop(handle.update) + stopReactiveEffect(handle.update) } }) } @@ -319,7 +319,7 @@ export function createRenderer(options: RendererOptions) { } const prevTree = prev.children as MountedVNode const nextTree = (next.children = renderFunctionalRoot(next)) - queueEffect(() => { + queuePostEffect(() => { next.el = nextTree.el }) patch( @@ -355,7 +355,7 @@ export function createRenderer(options: RendererOptions) { const { children, childFlags } = vnode switch (childFlags) { case ChildrenFlags.SINGLE_VNODE: - queueEffect(() => { + queuePostEffect(() => { vnode.el = (children as MountedVNode).el }) mount(children as VNode, container, contextVNode, isSVG, endNode) @@ -366,7 +366,7 @@ export function createRenderer(options: RendererOptions) { vnode.el = placeholder.el break default: - queueEffect(() => { + queuePostEffect(() => { vnode.el = (children as MountedVNode[])[0].el }) mountArrayChildren( @@ -403,7 +403,7 @@ export function createRenderer(options: RendererOptions) { ) } if (ref) { - queueEffect(() => { + queuePostEffect(() => { ref(target) }) } @@ -638,7 +638,7 @@ export function createRenderer(options: RendererOptions) { // then retrieve its next sibling to use as the end node for patchChildren. const endNode = platformNextSibling(getVNodeLastEl(prevVNode)) const { childFlags, children } = nextVNode - queueEffect(() => { + queuePostEffect(() => { switch (childFlags) { case ChildrenFlags.SINGLE_VNODE: nextVNode.el = (children as MountedVNode).el @@ -1181,7 +1181,7 @@ export function createRenderer(options: RendererOptions) { } } else { // functional - stop((handle as FunctionalHandle).update) + stopReactiveEffect((handle as FunctionalHandle).update) unmount(children as MountedVNode) } } else if (flags & VNodeFlags.PORTAL) { @@ -1293,16 +1293,16 @@ export function createRenderer(options: RendererOptions) { } = instance instance.$forceUpdate = () => { - queueJob(instance._updateHandle) + queueJob(instance._update) } - const autorunOptions: AutorunOptions = { + const effectOptions: ReactiveEffectOptions = { scheduler: queueJob } if (__DEV__) { if (renderTracked) { - autorunOptions.onTrack = event => { + effectOptions.onTrack = event => { callLifecycleHookWithHandler( renderTracked, $proxy, @@ -1312,7 +1312,7 @@ export function createRenderer(options: RendererOptions) { } } if (renderTriggered) { - autorunOptions.onTrigger = event => { + effectOptions.onTrigger = event => { callLifecycleHookWithHandler( renderTriggered, $proxy, @@ -1323,7 +1323,7 @@ export function createRenderer(options: RendererOptions) { } } - instance._updateHandle = autorun(() => { + instance._update = createReactiveEffect(() => { if (instance._unmounted) { return } @@ -1340,7 +1340,7 @@ export function createRenderer(options: RendererOptions) { instance.$vnode = renderInstanceRoot(instance) as MountedVNode - queueEffect(() => { + queuePostEffect(() => { vnode.el = instance.$vnode.el if (__COMPAT__) { // expose __vue__ for devtools @@ -1360,7 +1360,7 @@ export function createRenderer(options: RendererOptions) { mount(instance.$vnode, container, vnode as MountedVNode, isSVG, endNode) } - }, autorunOptions) + }, effectOptions) if (__DEV__) { popWarningContext() @@ -1397,7 +1397,7 @@ export function createRenderer(options: RendererOptions) { const nextVNode = renderInstanceRoot(instance) as MountedVNode - queueEffect(() => { + queuePostEffect(() => { instance.$vnode = nextVNode const el = nextVNode.el as RenderNode if (__COMPAT__) { @@ -1486,7 +1486,7 @@ export function createRenderer(options: RendererOptions) { if (__DEV__) { popWarningContext() } - queueEffect(() => { + queuePostEffect(() => { callActivatedHook(instance, true) }) } diff --git a/packages/scheduler/__tests__/scheduler.spec.ts b/packages/scheduler/__tests__/scheduler.spec.ts index e18881e6..d196ce9a 100644 --- a/packages/scheduler/__tests__/scheduler.spec.ts +++ b/packages/scheduler/__tests__/scheduler.spec.ts @@ -1,4 +1,4 @@ -import { queueJob, queueEffect, nextTick } from '../src/index' +import { queueJob, queuePostEffect, nextTick } from '../src/index' describe('scheduler', () => { it('queueJob', async () => { @@ -36,11 +36,11 @@ describe('scheduler', () => { const calls: any = [] const job1 = () => { calls.push('job1') - queueEffect(cb1) + queuePostEffect(cb1) } const job2 = () => { calls.push('job2') - queueEffect(cb2) + queuePostEffect(cb2) } const cb1 = () => { calls.push('cb1') @@ -59,13 +59,13 @@ describe('scheduler', () => { const calls: any = [] const job1 = () => { calls.push('job1') - queueEffect(cb1) + queuePostEffect(cb1) // job1 queues job2 queueJob(job2) } const job2 = () => { calls.push('job2') - queueEffect(cb2) + queuePostEffect(cb2) } const cb1 = () => { calls.push('cb1') @@ -96,11 +96,11 @@ describe('scheduler', () => { expect(calls).toEqual(['job1', 'job2']) }) - it('queueJob inside postCommitCb', async () => { + it('queueJob inside postEffect', async () => { const calls: any = [] const job1 = () => { calls.push('job1') - queueEffect(cb1) + queuePostEffect(cb1) } const cb1 = () => { // queue another job in postFlushCb @@ -109,7 +109,7 @@ describe('scheduler', () => { } const job2 = () => { calls.push('job2') - queueEffect(cb2) + queuePostEffect(cb2) } const cb2 = () => { calls.push('cb2') diff --git a/packages/scheduler/src/index.ts b/packages/scheduler/src/index.ts index 79e9a564..89323efd 100644 --- a/packages/scheduler/src/index.ts +++ b/packages/scheduler/src/index.ts @@ -31,15 +31,15 @@ interface Job void> { ops: Op[] // Any post DOM mutation side-effects (updated / mounted hooks, refs) are // buffered inside the job's effects queue. - // Effects are queued by calling `queueEffect` inside the job function. - effects: Function[] + // Effects are queued by calling `queuePostEffect` inside the job function. + postEffects: Function[] // A job may queue other jobs (e.g. a parent component update triggers the // update of a child component). Jobs queued by another job is kept in the // parent's children array, so that in case the parent job is invalidated, // all its children can be invalidated as well (recursively). children: Job[] // Sometimes it's inevitable for a stage fn to produce some side effects - // (e.g. a component instance sets up an Autorun). In those cases the stage fn + // (e.g. a component instance sets up an ReactiveEffect). In those cases the stage fn // can return a cleanup function which will be called when the job is // invalidated. cleanup: T | null @@ -172,8 +172,8 @@ function flushAfterMacroTask() { // This is the main API of the scheduler. The raw job can actually be any // function, but since they are invalidated by identity, it is important that // a component's update job is a consistent function across its lifecycle - -// in the renderer, it's actually instance._updateHandle which is in turn -// an Autorun function. +// in the renderer, it's actually instance._update which is in turn +// an ReactiveEffect function. export function queueJob(rawJob: Function) { const job = rawJob as Job if (currentJob) { @@ -196,9 +196,9 @@ export function queueJob(rawJob: Function) { } } -export function queueEffect(fn: Function) { +export function queuePostEffect(fn: Function) { if (currentJob) { - currentJob.effects.push(fn) + currentJob.postEffects.push(fn) } else { postEffectsQueue.push(fn) } @@ -296,13 +296,13 @@ function flush(): void { function resetJob(job: Job) { job.ops.length = 0 - job.effects.length = 0 + job.postEffects.length = 0 job.children.length = 0 } function queueJobForStaging(job: Job) { job.ops = job.ops || [] - job.effects = job.effects || [] + job.postEffects = job.postEffects || [] job.children = job.children || [] resetJob(job) // inherit parent job's expiration deadline @@ -361,13 +361,13 @@ function stageJob(job: Job) { } function commitJob(job: Job) { - const { ops, effects } = job + const { ops, postEffects } = job for (let i = 0; i < ops.length; i++) { applyOp(ops[i]) } // queue post commit cbs - if (effects) { - postEffectsQueue.push(...effects) + if (postEffects) { + postEffectsQueue.push(...postEffects) } resetJob(job) job.status = JobStatus.IDLE