@viewfly/core 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.en.md +71 -0
- package/README.md +2 -0
- package/dist/base/component.d.ts +3 -3
- package/dist/index.esm.js +94 -62
- package/dist/index.js +94 -61
- package/dist/reactive/computed.d.ts +4 -0
- package/dist/reactive/effect.d.ts +2 -0
- package/package.json +1 -1
package/README.en.md
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# @viewfly/core
|
|
2
|
+
|
|
3
|
+
**Languages:** [简体中文](./README.md)
|
|
4
|
+
|
|
5
|
+
The **core** Viewfly package: function components, JSX, reactivity and `signal`, `watch`, lifecycle, `inject` / IoC APIs, and **`withMark`** for UI-facing utilities.
|
|
6
|
+
|
|
7
|
+
To mount in the browser use **`createApp`** from **`@viewfly/platform-browser`** (built on this package’s application model).
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
npm install @viewfly/core
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This package depends on **`reflect-metadata`**. Importing from the **`@viewfly/core`** main entry initializes it; if DI breaks after aggressive splitting, add at the **top** of your entry:
|
|
18
|
+
|
|
19
|
+
```ts
|
|
20
|
+
import 'reflect-metadata'
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Prefer the **`.d.ts`** shipped with your installed version and the official docs (<https://viewfly.org>) as the source of truth.
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## JSX / TSX setup
|
|
28
|
+
|
|
29
|
+
Enable automatic JSX runtime and point it at this package:
|
|
30
|
+
|
|
31
|
+
```json
|
|
32
|
+
{
|
|
33
|
+
"compilerOptions": {
|
|
34
|
+
"jsx": "react-jsx",
|
|
35
|
+
"jsxImportSource": "@viewfly/core"
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
With Babel, align `@babel/preset-react` (`runtime: "automatic"`, `importSource: "@viewfly/core"`).
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Capabilities (user-facing)
|
|
45
|
+
|
|
46
|
+
Common directions below — **exact types and parameters come from `.d.ts` and the docs**.
|
|
47
|
+
|
|
48
|
+
| Area | Public API hints |
|
|
49
|
+
|------|------------------|
|
|
50
|
+
| Components | Function components as JSX tags; pair with lifecycle hooks. |
|
|
51
|
+
| Lifecycle | **`onMounted`**, **`onUpdated`**, **`onUnmounted`**, etc. (call during component setup). |
|
|
52
|
+
| Reactivity | **`reactive`**, **`shallowReactive`**, **`watch`**, … (`reactive` module). |
|
|
53
|
+
| `signal` / derived | **`createSignal`**, **`computed`**, … (see `reactive` exports). |
|
|
54
|
+
| DI | **`inject`** in components; **`Injectable()`** on classes; **`withAnnotation`** or **`createContext`** / **`createContextProvider`** for providers; root **`createApp(...).provide(...)`** from **`@viewfly/platform-browser`**. |
|
|
55
|
+
| DOM marks | **`withMark(marks, setup)`** — attach attributes (e.g. scoped CSS `scopeId`). |
|
|
56
|
+
|
|
57
|
+
Symbols marked **`@internal`** or undocumented on the site are for framework/experimental use — **avoid relying on them** in apps; they may change without semver guarantees.
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Docs & examples
|
|
62
|
+
|
|
63
|
+
- **Official docs:** <https://viewfly.org> (install, components, reactivity, DI).
|
|
64
|
+
- **Types & comments:** published **`.d.ts`** and source comments.
|
|
65
|
+
- **This repo:** `pnpm install` at root, then `pnpm dev` for the playground.
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## License
|
|
70
|
+
|
|
71
|
+
MIT
|
package/README.md
CHANGED
package/dist/base/component.d.ts
CHANGED
|
@@ -28,7 +28,7 @@ export declare class Component {
|
|
|
28
28
|
props: Record<string, any>;
|
|
29
29
|
readonly key?: Key;
|
|
30
30
|
instance: ComponentInstance;
|
|
31
|
-
changedSubComponents: Set<Component
|
|
31
|
+
changedSubComponents: Set<Component> | null;
|
|
32
32
|
get dirty(): boolean;
|
|
33
33
|
get changed(): boolean;
|
|
34
34
|
/**
|
|
@@ -48,13 +48,13 @@ export declare class Component {
|
|
|
48
48
|
constructor(parentComponent: Component | null, type: ComponentSetup, props: Record<string, any>, key?: Key);
|
|
49
49
|
markAsDirtied(): void;
|
|
50
50
|
markAsChanged(changedComponent?: Component): void;
|
|
51
|
-
render(
|
|
51
|
+
render(): JSXNode;
|
|
52
52
|
updateProps(newProps: Record<string, any>): void;
|
|
53
|
-
rerender(): JSXNode;
|
|
54
53
|
destroy(): void;
|
|
55
54
|
rendered(): void;
|
|
56
55
|
private invokeMountHooks;
|
|
57
56
|
private invokeUpdatedHooks;
|
|
57
|
+
private getOrCreateRefEffects;
|
|
58
58
|
}
|
|
59
59
|
/**
|
|
60
60
|
* 获取当前组件实例
|
package/dist/index.esm.js
CHANGED
|
@@ -615,6 +615,15 @@ function cleanupEmptyEffects(target, type, key, effects, record, subscriber) {
|
|
|
615
615
|
subscriber.delete(type);
|
|
616
616
|
if (subscriber.size === 0) subscribers.delete(target);
|
|
617
617
|
}
|
|
618
|
+
/** @internal 是否有订阅者监听 target 上某一追踪键(用于 computed 等按需刷新) */
|
|
619
|
+
function hasEffectSubscribers(target, type, key = unKnownKey) {
|
|
620
|
+
const subscriber = subscribers.get(target);
|
|
621
|
+
if (!subscriber) return false;
|
|
622
|
+
const record = subscriber.get(type);
|
|
623
|
+
if (!record) return false;
|
|
624
|
+
const effects = record.get(key);
|
|
625
|
+
return !!effects && effects.size > 0;
|
|
626
|
+
}
|
|
618
627
|
function track(target, type, key = unKnownKey) {
|
|
619
628
|
const dep = getDepContext();
|
|
620
629
|
if (dep) {
|
|
@@ -1466,10 +1475,9 @@ var Component = class {
|
|
|
1466
1475
|
constructor(parentComponent, type, props, key) {
|
|
1467
1476
|
this.parentComponent = parentComponent;
|
|
1468
1477
|
this.type = type;
|
|
1469
|
-
this.props = props;
|
|
1470
1478
|
this.key = key;
|
|
1471
1479
|
this.instance = null;
|
|
1472
|
-
this.changedSubComponents =
|
|
1480
|
+
this.changedSubComponents = null;
|
|
1473
1481
|
this.viewMetadata = null;
|
|
1474
1482
|
this.unmountedCallbacks = null;
|
|
1475
1483
|
this.mountCallbacks = null;
|
|
@@ -1480,7 +1488,7 @@ var Component = class {
|
|
|
1480
1488
|
this.isFirstRendering = true;
|
|
1481
1489
|
this.rawProps = props;
|
|
1482
1490
|
this.props = createShallowReadonlyProxy({ ...props });
|
|
1483
|
-
this.refEffects =
|
|
1491
|
+
this.refEffects = null;
|
|
1484
1492
|
this.listener = new Dep(() => {
|
|
1485
1493
|
this.markAsDirtied();
|
|
1486
1494
|
}, "sync");
|
|
@@ -1490,19 +1498,34 @@ var Component = class {
|
|
|
1490
1498
|
this.markAsChanged();
|
|
1491
1499
|
}
|
|
1492
1500
|
markAsChanged(changedComponent) {
|
|
1493
|
-
if (changedComponent)
|
|
1501
|
+
if (changedComponent) {
|
|
1502
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
1503
|
+
this.changedSubComponents.add(changedComponent);
|
|
1504
|
+
}
|
|
1494
1505
|
if (this._changed) return;
|
|
1495
1506
|
this._changed = true;
|
|
1496
1507
|
if (this.parentComponent) this.parentComponent.markAsChanged(this);
|
|
1497
1508
|
}
|
|
1498
|
-
render(
|
|
1509
|
+
render() {
|
|
1510
|
+
if (this.instance) {
|
|
1511
|
+
this.listener.destroy();
|
|
1512
|
+
pushDepContext(this.listener);
|
|
1513
|
+
const template = this.instance.render();
|
|
1514
|
+
popDepContext();
|
|
1515
|
+
return template;
|
|
1516
|
+
}
|
|
1499
1517
|
componentSetupStack.push(this);
|
|
1500
1518
|
const render = this.type(this.props);
|
|
1501
1519
|
const isRenderFn = typeof render === "function";
|
|
1502
1520
|
this.instance = isRenderFn ? { render } : render;
|
|
1503
1521
|
onMounted(() => {
|
|
1504
|
-
|
|
1522
|
+
const refs = this.props.ref;
|
|
1523
|
+
if (refs) {
|
|
1524
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1525
|
+
applyRefs(refs, this.instance, refEffects);
|
|
1526
|
+
}
|
|
1505
1527
|
return () => {
|
|
1528
|
+
if (!this.refEffects) return;
|
|
1506
1529
|
this.refEffects.forEach((fn) => {
|
|
1507
1530
|
if (typeof fn === "function") fn();
|
|
1508
1531
|
});
|
|
@@ -1512,14 +1535,16 @@ var Component = class {
|
|
|
1512
1535
|
pushDepContext(this.listener);
|
|
1513
1536
|
const template = this.instance.render();
|
|
1514
1537
|
popDepContext();
|
|
1515
|
-
|
|
1516
|
-
this.rendered();
|
|
1538
|
+
return template;
|
|
1517
1539
|
}
|
|
1518
1540
|
updateProps(newProps) {
|
|
1519
1541
|
const oldProps = this.rawProps;
|
|
1520
1542
|
this.rawProps = newProps;
|
|
1521
1543
|
const newRefs = newProps.ref;
|
|
1522
|
-
|
|
1544
|
+
if (oldProps.ref || newRefs) {
|
|
1545
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1546
|
+
updateRefs(newRefs, this.instance, refEffects);
|
|
1547
|
+
}
|
|
1523
1548
|
comparePropsWithCallbacks(oldProps, newProps, (key) => {
|
|
1524
1549
|
internalWrite(() => {
|
|
1525
1550
|
Reflect.deleteProperty(oldProps, key);
|
|
@@ -1534,13 +1559,6 @@ var Component = class {
|
|
|
1534
1559
|
});
|
|
1535
1560
|
});
|
|
1536
1561
|
}
|
|
1537
|
-
rerender() {
|
|
1538
|
-
this.listener.destroy();
|
|
1539
|
-
pushDepContext(this.listener);
|
|
1540
|
-
const template = this.instance.render();
|
|
1541
|
-
popDepContext();
|
|
1542
|
-
return template;
|
|
1543
|
-
}
|
|
1544
1562
|
destroy() {
|
|
1545
1563
|
this.listener.destroy();
|
|
1546
1564
|
if (this.updatedDestroyCallbacks) this.updatedDestroyCallbacks.forEach((fn) => {
|
|
@@ -1549,10 +1567,11 @@ var Component = class {
|
|
|
1549
1567
|
if (this.unmountedCallbacks) this.unmountedCallbacks.forEach((fn) => {
|
|
1550
1568
|
fn();
|
|
1551
1569
|
});
|
|
1552
|
-
this.
|
|
1570
|
+
this.isFirstRendering = true;
|
|
1571
|
+
this.changedSubComponents = this.refEffects = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = this.instance = null;
|
|
1553
1572
|
}
|
|
1554
1573
|
rendered() {
|
|
1555
|
-
this.changedSubComponents
|
|
1574
|
+
this.changedSubComponents?.clear();
|
|
1556
1575
|
const is = this.isFirstRendering;
|
|
1557
1576
|
this.isFirstRendering = false;
|
|
1558
1577
|
this._dirty = this._changed = false;
|
|
@@ -1591,6 +1610,10 @@ var Component = class {
|
|
|
1591
1610
|
this.updatedDestroyCallbacks = updatedDestroyCallbacks.length ? updatedDestroyCallbacks : null;
|
|
1592
1611
|
}
|
|
1593
1612
|
}
|
|
1613
|
+
getOrCreateRefEffects() {
|
|
1614
|
+
if (!this.refEffects) this.refEffects = /* @__PURE__ */ new Map();
|
|
1615
|
+
return this.refEffects;
|
|
1616
|
+
}
|
|
1594
1617
|
};
|
|
1595
1618
|
/**
|
|
1596
1619
|
* 获取当前组件实例
|
|
@@ -1974,23 +1997,20 @@ function buildElementChildren(atom, nativeRenderer, parentComponent, context) {
|
|
|
1974
1997
|
}
|
|
1975
1998
|
}
|
|
1976
1999
|
function patchComponent(nativeRenderer, component, oldChildAtom, newAtom, context, needMove) {
|
|
1977
|
-
const newTemplate = component.
|
|
2000
|
+
const newTemplate = component.render();
|
|
1978
2001
|
const portalContainer = getContainer();
|
|
1979
2002
|
const rawContext = context;
|
|
1980
2003
|
const { computedContainer, contextContainer } = component.viewMetadata;
|
|
1981
2004
|
popContainer();
|
|
1982
|
-
if (portalContainer)
|
|
2005
|
+
if (portalContainer) {
|
|
1983
2006
|
if (portalContainer !== computedContainer) needMove = true;
|
|
1984
|
-
|
|
1985
|
-
if (portalContainer !== computedContainer) needMove = true;
|
|
1986
|
-
context = {
|
|
2007
|
+
if (portalContainer !== context.contextContainer) context = {
|
|
1987
2008
|
isParent: true,
|
|
1988
2009
|
anchorNode: portalContainer,
|
|
1989
2010
|
contextContainer: context.contextContainer,
|
|
1990
2011
|
computedContainer: portalContainer
|
|
1991
2012
|
};
|
|
1992
|
-
}
|
|
1993
|
-
else if (contextContainer !== context.contextContainer) needMove = true;
|
|
2013
|
+
} else if (contextContainer !== context.contextContainer) needMove = true;
|
|
1994
2014
|
component.viewMetadata = {
|
|
1995
2015
|
atom: newAtom,
|
|
1996
2016
|
...context
|
|
@@ -2018,9 +2038,8 @@ function deepUpdateByComponentDirtyTree(nativeRenderer, component, needMove) {
|
|
|
2018
2038
|
}
|
|
2019
2039
|
component.rendered();
|
|
2020
2040
|
} else if (component.changed) {
|
|
2021
|
-
component.changedSubComponents
|
|
2022
|
-
|
|
2023
|
-
});
|
|
2041
|
+
const changedSubComponents = component.changedSubComponents;
|
|
2042
|
+
if (changedSubComponents) for (const child of changedSubComponents) deepUpdateByComponentDirtyTree(nativeRenderer, child, needMove);
|
|
2024
2043
|
component.rendered();
|
|
2025
2044
|
}
|
|
2026
2045
|
}
|
|
@@ -2128,7 +2147,7 @@ function updateComponent(nativeRenderer, context, offset, needMove, newAtom, old
|
|
|
2128
2147
|
...context
|
|
2129
2148
|
};
|
|
2130
2149
|
newAtom.child = oldAtom.child;
|
|
2131
|
-
const skipSubComponentDiff = !component.changedSubComponents
|
|
2150
|
+
const skipSubComponentDiff = !component.changedSubComponents?.size;
|
|
2132
2151
|
reuseComponentView(nativeRenderer, newAtom.child, context, needMove, skipSubComponentDiff);
|
|
2133
2152
|
if (!skipSubComponentDiff) component.rendered();
|
|
2134
2153
|
}
|
|
@@ -2198,26 +2217,26 @@ function cleanChildren(atom, nativeRenderer, needClean) {
|
|
|
2198
2217
|
}
|
|
2199
2218
|
}
|
|
2200
2219
|
function componentRender(nativeRenderer, component, from, context) {
|
|
2201
|
-
component.render(
|
|
2202
|
-
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2220
|
+
const template = component.render();
|
|
2221
|
+
const portalContainer = getContainer();
|
|
2222
|
+
popContainer();
|
|
2223
|
+
from.child = createChildChain(template, nativeRenderer, from.namespace);
|
|
2224
|
+
if (portalContainer && portalContainer !== context.contextContainer) context = {
|
|
2225
|
+
isParent: true,
|
|
2226
|
+
anchorNode: portalContainer,
|
|
2227
|
+
contextContainer: context.contextContainer,
|
|
2228
|
+
computedContainer: portalContainer
|
|
2229
|
+
};
|
|
2230
|
+
component.viewMetadata = {
|
|
2231
|
+
atom: from,
|
|
2232
|
+
...context
|
|
2233
|
+
};
|
|
2234
|
+
let child = from.child;
|
|
2235
|
+
while (child) {
|
|
2236
|
+
buildView(nativeRenderer, component, child, context);
|
|
2237
|
+
child = child.sibling;
|
|
2238
|
+
}
|
|
2239
|
+
component.rendered();
|
|
2221
2240
|
}
|
|
2222
2241
|
function createChainByJSXNode(type, jsxNode, nodeType, prevAtom, namespace, key) {
|
|
2223
2242
|
const atom = {
|
|
@@ -2457,7 +2476,10 @@ var RootComponent = class extends Component {
|
|
|
2457
2476
|
}
|
|
2458
2477
|
markAsChanged(changedComponent) {
|
|
2459
2478
|
this._changed = true;
|
|
2460
|
-
if (changedComponent)
|
|
2479
|
+
if (changedComponent) {
|
|
2480
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
2481
|
+
this.changedSubComponents.add(changedComponent);
|
|
2482
|
+
}
|
|
2461
2483
|
this.refresh();
|
|
2462
2484
|
}
|
|
2463
2485
|
};
|
|
@@ -2534,29 +2556,39 @@ function viewfly(config) {
|
|
|
2534
2556
|
/**
|
|
2535
2557
|
* 创建一个 computed,当依赖的值发生变化时,会重新计算值。
|
|
2536
2558
|
* computed 会返回一个对象,对象的 value 属性是计算的值。
|
|
2559
|
+
*
|
|
2560
|
+
* 若重新计算的结果与缓存值 `Object.is` 相等,不会对访问该 computed 的订阅者触发更新,
|
|
2561
|
+
* 避免无关依赖抖动导致的下游副作用。
|
|
2562
|
+
*
|
|
2537
2563
|
* @param getter 计算函数,用于计算值
|
|
2538
2564
|
* @returns 一个对象,对象的 value 属性是计算的值
|
|
2539
2565
|
*/
|
|
2540
2566
|
function computed(getter) {
|
|
2541
2567
|
let cacheValue;
|
|
2568
|
+
let hasCache = false;
|
|
2542
2569
|
let dirty = true;
|
|
2543
2570
|
const target = { get value() {
|
|
2544
|
-
if (dirty)
|
|
2545
|
-
dep.destroy();
|
|
2546
|
-
pushDepContext(dep);
|
|
2547
|
-
try {
|
|
2548
|
-
cacheValue = getter();
|
|
2549
|
-
dirty = false;
|
|
2550
|
-
} finally {
|
|
2551
|
-
popDepContext();
|
|
2552
|
-
}
|
|
2553
|
-
}
|
|
2571
|
+
if (dirty) flushComputed(false);
|
|
2554
2572
|
return cacheValue;
|
|
2555
2573
|
} };
|
|
2574
|
+
function flushComputed(notify) {
|
|
2575
|
+
dep.destroy();
|
|
2576
|
+
pushDepContext(dep);
|
|
2577
|
+
try {
|
|
2578
|
+
const newValue = getter();
|
|
2579
|
+
const changed = !hasCache || !Object.is(newValue, cacheValue);
|
|
2580
|
+
hasCache = true;
|
|
2581
|
+
cacheValue = newValue;
|
|
2582
|
+
dirty = false;
|
|
2583
|
+
if (notify && changed) trigger(target, TriggerOpTypes.Set, "value");
|
|
2584
|
+
} finally {
|
|
2585
|
+
popDepContext();
|
|
2586
|
+
}
|
|
2587
|
+
}
|
|
2556
2588
|
const dep = new Dep(() => {
|
|
2557
2589
|
if (!dirty) {
|
|
2558
2590
|
dirty = true;
|
|
2559
|
-
|
|
2591
|
+
if (hasEffectSubscribers(target, TrackOpTypes.Get, "value")) flushComputed(true);
|
|
2560
2592
|
}
|
|
2561
2593
|
}, "sync");
|
|
2562
2594
|
registryComponentDestroyCallback(() => {
|
|
@@ -2643,4 +2675,4 @@ function createSignal(state) {
|
|
|
2643
2675
|
return signal;
|
|
2644
2676
|
}
|
|
2645
2677
|
//#endregion
|
|
2646
|
-
export { ArrayReactiveHandler, Component, Dep, ForwardRef, Fragment, Inject, InjectFlags, Injectable, InjectionToken, Injector, JSXNodeFactory, MapReactiveHandler, NativeRenderer, NullInjector, ObjectReactiveHandler, Optional, Portal, Prop, ReflectiveInjector, RootComponent, Scope, Self, SetReactiveHandler, SkipSelf, THROW_IF_NOT_FOUND, TrackOpTypes, TriggerOpTypes, Type, applyMark, applyRefs, comparePropsWithCallbacks, computed, createContext, createContextProvider, createDynamicRef, createRef, createRenderer, createShallowReadonlyProxy, createSignal, defaultArrayReactiveHandler, defaultMapReactiveHandler, defaultObjectReactiveHandler, defaultSetReactiveHandler, defaultShallowArrayReactiveHandler, defaultShallowMapReactiveHandler, defaultShallowObjectReactiveHandler, defaultShallowSetReactiveHandler, flushReactiveEffectsSync, forwardRef, getCurrentInstance, getDepContext, getSetupContext, inject, internalWrite, isReactive, jsx, jsxs, makeError, nextTick, normalizeProvider, onMounted, onUnmounted, onUpdated, popDepContext, proxyToRawCache, pushDepContext, rawToProxyCache, reactive, readonlyProxyHandler, registryComponentDestroyCallback, shallowProxyToRawCache, shallowRawToProxyCache, shallowReactive, toRaw, track, trigger, updateRefs, viewfly, watch, watchEffect, withAnnotation, withMark };
|
|
2678
|
+
export { ArrayReactiveHandler, Component, Dep, ForwardRef, Fragment, Inject, InjectFlags, Injectable, InjectionToken, Injector, JSXNodeFactory, MapReactiveHandler, NativeRenderer, NullInjector, ObjectReactiveHandler, Optional, Portal, Prop, ReflectiveInjector, RootComponent, Scope, Self, SetReactiveHandler, SkipSelf, THROW_IF_NOT_FOUND, TrackOpTypes, TriggerOpTypes, Type, applyMark, applyRefs, comparePropsWithCallbacks, computed, createContext, createContextProvider, createDynamicRef, createRef, createRenderer, createShallowReadonlyProxy, createSignal, defaultArrayReactiveHandler, defaultMapReactiveHandler, defaultObjectReactiveHandler, defaultSetReactiveHandler, defaultShallowArrayReactiveHandler, defaultShallowMapReactiveHandler, defaultShallowObjectReactiveHandler, defaultShallowSetReactiveHandler, flushReactiveEffectsSync, forwardRef, getCurrentInstance, getDepContext, getSetupContext, hasEffectSubscribers, inject, internalWrite, isReactive, jsx, jsxs, makeError, nextTick, normalizeProvider, onMounted, onUnmounted, onUpdated, popDepContext, proxyToRawCache, pushDepContext, rawToProxyCache, reactive, readonlyProxyHandler, registryComponentDestroyCallback, shallowProxyToRawCache, shallowRawToProxyCache, shallowReactive, toRaw, track, trigger, updateRefs, viewfly, watch, watchEffect, withAnnotation, withMark };
|
package/dist/index.js
CHANGED
|
@@ -616,6 +616,15 @@ function cleanupEmptyEffects(target, type, key, effects, record, subscriber) {
|
|
|
616
616
|
subscriber.delete(type);
|
|
617
617
|
if (subscriber.size === 0) subscribers.delete(target);
|
|
618
618
|
}
|
|
619
|
+
/** @internal 是否有订阅者监听 target 上某一追踪键(用于 computed 等按需刷新) */
|
|
620
|
+
function hasEffectSubscribers(target, type, key = unKnownKey) {
|
|
621
|
+
const subscriber = subscribers.get(target);
|
|
622
|
+
if (!subscriber) return false;
|
|
623
|
+
const record = subscriber.get(type);
|
|
624
|
+
if (!record) return false;
|
|
625
|
+
const effects = record.get(key);
|
|
626
|
+
return !!effects && effects.size > 0;
|
|
627
|
+
}
|
|
619
628
|
function track(target, type, key = unKnownKey) {
|
|
620
629
|
const dep = getDepContext();
|
|
621
630
|
if (dep) {
|
|
@@ -1467,10 +1476,9 @@ var Component = class {
|
|
|
1467
1476
|
constructor(parentComponent, type, props, key) {
|
|
1468
1477
|
this.parentComponent = parentComponent;
|
|
1469
1478
|
this.type = type;
|
|
1470
|
-
this.props = props;
|
|
1471
1479
|
this.key = key;
|
|
1472
1480
|
this.instance = null;
|
|
1473
|
-
this.changedSubComponents =
|
|
1481
|
+
this.changedSubComponents = null;
|
|
1474
1482
|
this.viewMetadata = null;
|
|
1475
1483
|
this.unmountedCallbacks = null;
|
|
1476
1484
|
this.mountCallbacks = null;
|
|
@@ -1481,7 +1489,7 @@ var Component = class {
|
|
|
1481
1489
|
this.isFirstRendering = true;
|
|
1482
1490
|
this.rawProps = props;
|
|
1483
1491
|
this.props = createShallowReadonlyProxy({ ...props });
|
|
1484
|
-
this.refEffects =
|
|
1492
|
+
this.refEffects = null;
|
|
1485
1493
|
this.listener = new Dep(() => {
|
|
1486
1494
|
this.markAsDirtied();
|
|
1487
1495
|
}, "sync");
|
|
@@ -1491,19 +1499,34 @@ var Component = class {
|
|
|
1491
1499
|
this.markAsChanged();
|
|
1492
1500
|
}
|
|
1493
1501
|
markAsChanged(changedComponent) {
|
|
1494
|
-
if (changedComponent)
|
|
1502
|
+
if (changedComponent) {
|
|
1503
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
1504
|
+
this.changedSubComponents.add(changedComponent);
|
|
1505
|
+
}
|
|
1495
1506
|
if (this._changed) return;
|
|
1496
1507
|
this._changed = true;
|
|
1497
1508
|
if (this.parentComponent) this.parentComponent.markAsChanged(this);
|
|
1498
1509
|
}
|
|
1499
|
-
render(
|
|
1510
|
+
render() {
|
|
1511
|
+
if (this.instance) {
|
|
1512
|
+
this.listener.destroy();
|
|
1513
|
+
pushDepContext(this.listener);
|
|
1514
|
+
const template = this.instance.render();
|
|
1515
|
+
popDepContext();
|
|
1516
|
+
return template;
|
|
1517
|
+
}
|
|
1500
1518
|
componentSetupStack.push(this);
|
|
1501
1519
|
const render = this.type(this.props);
|
|
1502
1520
|
const isRenderFn = typeof render === "function";
|
|
1503
1521
|
this.instance = isRenderFn ? { render } : render;
|
|
1504
1522
|
onMounted(() => {
|
|
1505
|
-
|
|
1523
|
+
const refs = this.props.ref;
|
|
1524
|
+
if (refs) {
|
|
1525
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1526
|
+
applyRefs(refs, this.instance, refEffects);
|
|
1527
|
+
}
|
|
1506
1528
|
return () => {
|
|
1529
|
+
if (!this.refEffects) return;
|
|
1507
1530
|
this.refEffects.forEach((fn) => {
|
|
1508
1531
|
if (typeof fn === "function") fn();
|
|
1509
1532
|
});
|
|
@@ -1513,14 +1536,16 @@ var Component = class {
|
|
|
1513
1536
|
pushDepContext(this.listener);
|
|
1514
1537
|
const template = this.instance.render();
|
|
1515
1538
|
popDepContext();
|
|
1516
|
-
|
|
1517
|
-
this.rendered();
|
|
1539
|
+
return template;
|
|
1518
1540
|
}
|
|
1519
1541
|
updateProps(newProps) {
|
|
1520
1542
|
const oldProps = this.rawProps;
|
|
1521
1543
|
this.rawProps = newProps;
|
|
1522
1544
|
const newRefs = newProps.ref;
|
|
1523
|
-
|
|
1545
|
+
if (oldProps.ref || newRefs) {
|
|
1546
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1547
|
+
updateRefs(newRefs, this.instance, refEffects);
|
|
1548
|
+
}
|
|
1524
1549
|
comparePropsWithCallbacks(oldProps, newProps, (key) => {
|
|
1525
1550
|
internalWrite(() => {
|
|
1526
1551
|
Reflect.deleteProperty(oldProps, key);
|
|
@@ -1535,13 +1560,6 @@ var Component = class {
|
|
|
1535
1560
|
});
|
|
1536
1561
|
});
|
|
1537
1562
|
}
|
|
1538
|
-
rerender() {
|
|
1539
|
-
this.listener.destroy();
|
|
1540
|
-
pushDepContext(this.listener);
|
|
1541
|
-
const template = this.instance.render();
|
|
1542
|
-
popDepContext();
|
|
1543
|
-
return template;
|
|
1544
|
-
}
|
|
1545
1563
|
destroy() {
|
|
1546
1564
|
this.listener.destroy();
|
|
1547
1565
|
if (this.updatedDestroyCallbacks) this.updatedDestroyCallbacks.forEach((fn) => {
|
|
@@ -1550,10 +1568,11 @@ var Component = class {
|
|
|
1550
1568
|
if (this.unmountedCallbacks) this.unmountedCallbacks.forEach((fn) => {
|
|
1551
1569
|
fn();
|
|
1552
1570
|
});
|
|
1553
|
-
this.
|
|
1571
|
+
this.isFirstRendering = true;
|
|
1572
|
+
this.changedSubComponents = this.refEffects = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = this.instance = null;
|
|
1554
1573
|
}
|
|
1555
1574
|
rendered() {
|
|
1556
|
-
this.changedSubComponents
|
|
1575
|
+
this.changedSubComponents?.clear();
|
|
1557
1576
|
const is = this.isFirstRendering;
|
|
1558
1577
|
this.isFirstRendering = false;
|
|
1559
1578
|
this._dirty = this._changed = false;
|
|
@@ -1592,6 +1611,10 @@ var Component = class {
|
|
|
1592
1611
|
this.updatedDestroyCallbacks = updatedDestroyCallbacks.length ? updatedDestroyCallbacks : null;
|
|
1593
1612
|
}
|
|
1594
1613
|
}
|
|
1614
|
+
getOrCreateRefEffects() {
|
|
1615
|
+
if (!this.refEffects) this.refEffects = /* @__PURE__ */ new Map();
|
|
1616
|
+
return this.refEffects;
|
|
1617
|
+
}
|
|
1595
1618
|
};
|
|
1596
1619
|
/**
|
|
1597
1620
|
* 获取当前组件实例
|
|
@@ -1975,23 +1998,20 @@ function buildElementChildren(atom, nativeRenderer, parentComponent, context) {
|
|
|
1975
1998
|
}
|
|
1976
1999
|
}
|
|
1977
2000
|
function patchComponent(nativeRenderer, component, oldChildAtom, newAtom, context, needMove) {
|
|
1978
|
-
const newTemplate = component.
|
|
2001
|
+
const newTemplate = component.render();
|
|
1979
2002
|
const portalContainer = getContainer();
|
|
1980
2003
|
const rawContext = context;
|
|
1981
2004
|
const { computedContainer, contextContainer } = component.viewMetadata;
|
|
1982
2005
|
popContainer();
|
|
1983
|
-
if (portalContainer)
|
|
2006
|
+
if (portalContainer) {
|
|
1984
2007
|
if (portalContainer !== computedContainer) needMove = true;
|
|
1985
|
-
|
|
1986
|
-
if (portalContainer !== computedContainer) needMove = true;
|
|
1987
|
-
context = {
|
|
2008
|
+
if (portalContainer !== context.contextContainer) context = {
|
|
1988
2009
|
isParent: true,
|
|
1989
2010
|
anchorNode: portalContainer,
|
|
1990
2011
|
contextContainer: context.contextContainer,
|
|
1991
2012
|
computedContainer: portalContainer
|
|
1992
2013
|
};
|
|
1993
|
-
}
|
|
1994
|
-
else if (contextContainer !== context.contextContainer) needMove = true;
|
|
2014
|
+
} else if (contextContainer !== context.contextContainer) needMove = true;
|
|
1995
2015
|
component.viewMetadata = {
|
|
1996
2016
|
atom: newAtom,
|
|
1997
2017
|
...context
|
|
@@ -2019,9 +2039,8 @@ function deepUpdateByComponentDirtyTree(nativeRenderer, component, needMove) {
|
|
|
2019
2039
|
}
|
|
2020
2040
|
component.rendered();
|
|
2021
2041
|
} else if (component.changed) {
|
|
2022
|
-
component.changedSubComponents
|
|
2023
|
-
|
|
2024
|
-
});
|
|
2042
|
+
const changedSubComponents = component.changedSubComponents;
|
|
2043
|
+
if (changedSubComponents) for (const child of changedSubComponents) deepUpdateByComponentDirtyTree(nativeRenderer, child, needMove);
|
|
2025
2044
|
component.rendered();
|
|
2026
2045
|
}
|
|
2027
2046
|
}
|
|
@@ -2129,7 +2148,7 @@ function updateComponent(nativeRenderer, context, offset, needMove, newAtom, old
|
|
|
2129
2148
|
...context
|
|
2130
2149
|
};
|
|
2131
2150
|
newAtom.child = oldAtom.child;
|
|
2132
|
-
const skipSubComponentDiff = !component.changedSubComponents
|
|
2151
|
+
const skipSubComponentDiff = !component.changedSubComponents?.size;
|
|
2133
2152
|
reuseComponentView(nativeRenderer, newAtom.child, context, needMove, skipSubComponentDiff);
|
|
2134
2153
|
if (!skipSubComponentDiff) component.rendered();
|
|
2135
2154
|
}
|
|
@@ -2199,26 +2218,26 @@ function cleanChildren(atom, nativeRenderer, needClean) {
|
|
|
2199
2218
|
}
|
|
2200
2219
|
}
|
|
2201
2220
|
function componentRender(nativeRenderer, component, from, context) {
|
|
2202
|
-
component.render(
|
|
2203
|
-
|
|
2204
|
-
|
|
2205
|
-
|
|
2206
|
-
|
|
2207
|
-
|
|
2208
|
-
|
|
2209
|
-
|
|
2210
|
-
|
|
2211
|
-
|
|
2212
|
-
|
|
2213
|
-
|
|
2214
|
-
|
|
2215
|
-
|
|
2216
|
-
|
|
2217
|
-
|
|
2218
|
-
|
|
2219
|
-
|
|
2220
|
-
|
|
2221
|
-
|
|
2221
|
+
const template = component.render();
|
|
2222
|
+
const portalContainer = getContainer();
|
|
2223
|
+
popContainer();
|
|
2224
|
+
from.child = createChildChain(template, nativeRenderer, from.namespace);
|
|
2225
|
+
if (portalContainer && portalContainer !== context.contextContainer) context = {
|
|
2226
|
+
isParent: true,
|
|
2227
|
+
anchorNode: portalContainer,
|
|
2228
|
+
contextContainer: context.contextContainer,
|
|
2229
|
+
computedContainer: portalContainer
|
|
2230
|
+
};
|
|
2231
|
+
component.viewMetadata = {
|
|
2232
|
+
atom: from,
|
|
2233
|
+
...context
|
|
2234
|
+
};
|
|
2235
|
+
let child = from.child;
|
|
2236
|
+
while (child) {
|
|
2237
|
+
buildView(nativeRenderer, component, child, context);
|
|
2238
|
+
child = child.sibling;
|
|
2239
|
+
}
|
|
2240
|
+
component.rendered();
|
|
2222
2241
|
}
|
|
2223
2242
|
function createChainByJSXNode(type, jsxNode, nodeType, prevAtom, namespace, key) {
|
|
2224
2243
|
const atom = {
|
|
@@ -2458,7 +2477,10 @@ var RootComponent = class extends Component {
|
|
|
2458
2477
|
}
|
|
2459
2478
|
markAsChanged(changedComponent) {
|
|
2460
2479
|
this._changed = true;
|
|
2461
|
-
if (changedComponent)
|
|
2480
|
+
if (changedComponent) {
|
|
2481
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
2482
|
+
this.changedSubComponents.add(changedComponent);
|
|
2483
|
+
}
|
|
2462
2484
|
this.refresh();
|
|
2463
2485
|
}
|
|
2464
2486
|
};
|
|
@@ -2535,29 +2557,39 @@ function viewfly(config) {
|
|
|
2535
2557
|
/**
|
|
2536
2558
|
* 创建一个 computed,当依赖的值发生变化时,会重新计算值。
|
|
2537
2559
|
* computed 会返回一个对象,对象的 value 属性是计算的值。
|
|
2560
|
+
*
|
|
2561
|
+
* 若重新计算的结果与缓存值 `Object.is` 相等,不会对访问该 computed 的订阅者触发更新,
|
|
2562
|
+
* 避免无关依赖抖动导致的下游副作用。
|
|
2563
|
+
*
|
|
2538
2564
|
* @param getter 计算函数,用于计算值
|
|
2539
2565
|
* @returns 一个对象,对象的 value 属性是计算的值
|
|
2540
2566
|
*/
|
|
2541
2567
|
function computed(getter) {
|
|
2542
2568
|
let cacheValue;
|
|
2569
|
+
let hasCache = false;
|
|
2543
2570
|
let dirty = true;
|
|
2544
2571
|
const target = { get value() {
|
|
2545
|
-
if (dirty)
|
|
2546
|
-
dep.destroy();
|
|
2547
|
-
pushDepContext(dep);
|
|
2548
|
-
try {
|
|
2549
|
-
cacheValue = getter();
|
|
2550
|
-
dirty = false;
|
|
2551
|
-
} finally {
|
|
2552
|
-
popDepContext();
|
|
2553
|
-
}
|
|
2554
|
-
}
|
|
2572
|
+
if (dirty) flushComputed(false);
|
|
2555
2573
|
return cacheValue;
|
|
2556
2574
|
} };
|
|
2575
|
+
function flushComputed(notify) {
|
|
2576
|
+
dep.destroy();
|
|
2577
|
+
pushDepContext(dep);
|
|
2578
|
+
try {
|
|
2579
|
+
const newValue = getter();
|
|
2580
|
+
const changed = !hasCache || !Object.is(newValue, cacheValue);
|
|
2581
|
+
hasCache = true;
|
|
2582
|
+
cacheValue = newValue;
|
|
2583
|
+
dirty = false;
|
|
2584
|
+
if (notify && changed) trigger(target, TriggerOpTypes.Set, "value");
|
|
2585
|
+
} finally {
|
|
2586
|
+
popDepContext();
|
|
2587
|
+
}
|
|
2588
|
+
}
|
|
2557
2589
|
const dep = new Dep(() => {
|
|
2558
2590
|
if (!dirty) {
|
|
2559
2591
|
dirty = true;
|
|
2560
|
-
|
|
2592
|
+
if (hasEffectSubscribers(target, TrackOpTypes.Get, "value")) flushComputed(true);
|
|
2561
2593
|
}
|
|
2562
2594
|
}, "sync");
|
|
2563
2595
|
registryComponentDestroyCallback(() => {
|
|
@@ -2696,6 +2728,7 @@ exports.forwardRef = forwardRef;
|
|
|
2696
2728
|
exports.getCurrentInstance = getCurrentInstance;
|
|
2697
2729
|
exports.getDepContext = getDepContext;
|
|
2698
2730
|
exports.getSetupContext = getSetupContext;
|
|
2731
|
+
exports.hasEffectSubscribers = hasEffectSubscribers;
|
|
2699
2732
|
exports.inject = inject;
|
|
2700
2733
|
exports.internalWrite = internalWrite;
|
|
2701
2734
|
exports.isReactive = isReactive;
|
|
@@ -4,6 +4,10 @@ export interface Computed<T> {
|
|
|
4
4
|
/**
|
|
5
5
|
* 创建一个 computed,当依赖的值发生变化时,会重新计算值。
|
|
6
6
|
* computed 会返回一个对象,对象的 value 属性是计算的值。
|
|
7
|
+
*
|
|
8
|
+
* 若重新计算的结果与缓存值 `Object.is` 相等,不会对访问该 computed 的订阅者触发更新,
|
|
9
|
+
* 避免无关依赖抖动导致的下游副作用。
|
|
10
|
+
*
|
|
7
11
|
* @param getter 计算函数,用于计算值
|
|
8
12
|
* @returns 一个对象,对象的 value 属性是计算的值
|
|
9
13
|
*/
|
|
@@ -16,5 +16,7 @@ export declare enum TriggerOpTypes {
|
|
|
16
16
|
Clear = "Clear",
|
|
17
17
|
Iterate = "Iterate"
|
|
18
18
|
}
|
|
19
|
+
/** @internal 是否有订阅者监听 target 上某一追踪键(用于 computed 等按需刷新) */
|
|
20
|
+
export declare function hasEffectSubscribers(target: object, type: TrackOpTypes, key?: unknown): boolean;
|
|
19
21
|
export declare function track(target: object, type: TrackOpTypes, key?: unknown): void;
|
|
20
22
|
export declare function trigger(target: object, type: TriggerOpTypes, key?: unknown): void;
|
package/package.json
CHANGED