@viewfly/core 3.0.0-alpha.8 → 3.0.1
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.md +7 -7
- package/dist/base/component.d.ts +2 -1
- package/dist/index.esm.js +69 -35
- package/dist/index.js +69 -34
- package/dist/reactive/computed.d.ts +4 -0
- package/dist/reactive/effect.d.ts +2 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @viewfly/core
|
|
2
2
|
|
|
3
|
-
Viewfly
|
|
3
|
+
Viewfly 的**内核包**:函数组件、`JSX`、响应式与 `signal`、`watch`、生命周期、`inject` 与 IoC 相关 API、以及 **`withMark`** 等与 UI 逻辑直接相关的入口均由此包提供。
|
|
4
4
|
|
|
5
5
|
在浏览器里挂载应用请使用 **`@viewfly/platform-browser`** 的 **`createApp`**(其内部使用本包提供的应用模型)。
|
|
6
6
|
|
|
@@ -9,8 +9,7 @@ Viewfly 的**内核包**:函数组件、JSX、响应式与信号、`watch`、
|
|
|
9
9
|
## 安装
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
|
|
13
|
-
# 或 npm / yarn
|
|
12
|
+
npm install @viewfly/core
|
|
14
13
|
```
|
|
15
14
|
|
|
16
15
|
本包依赖 **`reflect-metadata`**。正常从 **`@viewfly/core`** 的主入口导入时,会随模块加载一并初始化;若你的打包拆包方式导致依赖注入相关运行时异常,可在应用入口**最先**增加显式导入:
|
|
@@ -19,7 +18,7 @@ pnpm add @viewfly/core
|
|
|
19
18
|
import 'reflect-metadata'
|
|
20
19
|
```
|
|
21
20
|
|
|
22
|
-
|
|
21
|
+
更稳妥的做法以**当前安装版本**随附的类型定义与官方文档(<https://viewfly.org>)为准。
|
|
23
22
|
|
|
24
23
|
---
|
|
25
24
|
|
|
@@ -49,7 +48,7 @@ import 'reflect-metadata'
|
|
|
49
48
|
| 组件 | 函数组件:在 JSX 中当标签使用;配合生命周期钩子使用。 |
|
|
50
49
|
| 生命周期 | **`onMounted`**、**`onUpdated`**、**`onUnmounted`** 等(须在组件 setup 阶段调用)。 |
|
|
51
50
|
| 响应式 | **`reactive`**、**`shallowReactive`**、**`watch`** 等(`reactive` 模块)。 |
|
|
52
|
-
|
|
|
51
|
+
| `signal` / 派生 | **`createSignal`**、**`computed`** 等(见 `reactive` 导出;具体符号以类型为准)。 |
|
|
53
52
|
| 依赖注入 | 在组件内用 **`inject`** 解析 token;用 **`Injectable()`** 声明可注入类;用 **`withAnnotation`** 或 **`createContext`** / **`createContextProvider`** 挂载 **`Provider`**;根应用上通过 **`createApp(...).provide(...)`**(由 **`@viewfly/platform-browser`** 提供)注册全局提供者。 |
|
|
54
53
|
| 自定义 DOM 属性标记 | **`withMark(marks, setup)`**:为组件渲染出的元素附加与 `marks` 同名的属性(常用于 scoped CSS 的 `scopeId` 等场景)。 |
|
|
55
54
|
|
|
@@ -59,8 +58,9 @@ import 'reflect-metadata'
|
|
|
59
58
|
|
|
60
59
|
## 文档与示例
|
|
61
60
|
|
|
62
|
-
-
|
|
63
|
-
-
|
|
61
|
+
- **官方文档**:<https://viewfly.org>(重点见安装、组件、响应式、依赖注入)。
|
|
62
|
+
- **类型与注释**:发布包中的 **`.d.ts`** 与源码注释可用于补充 API 细节。
|
|
63
|
+
- **本仓库试跑**:克隆仓库后使用 **pnpm** 安装依赖,在仓库根目录执行 `pnpm dev` 打开 playground。
|
|
64
64
|
|
|
65
65
|
---
|
|
66
66
|
|
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
|
/**
|
|
@@ -55,6 +55,7 @@ export declare class Component {
|
|
|
55
55
|
rendered(): void;
|
|
56
56
|
private invokeMountHooks;
|
|
57
57
|
private invokeUpdatedHooks;
|
|
58
|
+
private getOrCreateRefEffects;
|
|
58
59
|
}
|
|
59
60
|
/**
|
|
60
61
|
* 获取当前组件实例
|
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) {
|
|
@@ -1469,7 +1478,7 @@ var Component = class {
|
|
|
1469
1478
|
this.props = props;
|
|
1470
1479
|
this.key = key;
|
|
1471
1480
|
this.instance = null;
|
|
1472
|
-
this.changedSubComponents =
|
|
1481
|
+
this.changedSubComponents = null;
|
|
1473
1482
|
this.viewMetadata = null;
|
|
1474
1483
|
this.unmountedCallbacks = null;
|
|
1475
1484
|
this.mountCallbacks = null;
|
|
@@ -1480,7 +1489,7 @@ var Component = class {
|
|
|
1480
1489
|
this.isFirstRendering = true;
|
|
1481
1490
|
this.rawProps = props;
|
|
1482
1491
|
this.props = createShallowReadonlyProxy({ ...props });
|
|
1483
|
-
this.refEffects =
|
|
1492
|
+
this.refEffects = null;
|
|
1484
1493
|
this.listener = new Dep(() => {
|
|
1485
1494
|
this.markAsDirtied();
|
|
1486
1495
|
}, "sync");
|
|
@@ -1490,7 +1499,10 @@ var Component = class {
|
|
|
1490
1499
|
this.markAsChanged();
|
|
1491
1500
|
}
|
|
1492
1501
|
markAsChanged(changedComponent) {
|
|
1493
|
-
if (changedComponent)
|
|
1502
|
+
if (changedComponent) {
|
|
1503
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
1504
|
+
this.changedSubComponents.add(changedComponent);
|
|
1505
|
+
}
|
|
1494
1506
|
if (this._changed) return;
|
|
1495
1507
|
this._changed = true;
|
|
1496
1508
|
if (this.parentComponent) this.parentComponent.markAsChanged(this);
|
|
@@ -1501,8 +1513,13 @@ var Component = class {
|
|
|
1501
1513
|
const isRenderFn = typeof render === "function";
|
|
1502
1514
|
this.instance = isRenderFn ? { render } : render;
|
|
1503
1515
|
onMounted(() => {
|
|
1504
|
-
|
|
1516
|
+
const refs = this.props.ref;
|
|
1517
|
+
if (refs) {
|
|
1518
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1519
|
+
applyRefs(refs, this.instance, refEffects);
|
|
1520
|
+
}
|
|
1505
1521
|
return () => {
|
|
1522
|
+
if (!this.refEffects) return;
|
|
1506
1523
|
this.refEffects.forEach((fn) => {
|
|
1507
1524
|
if (typeof fn === "function") fn();
|
|
1508
1525
|
});
|
|
@@ -1519,7 +1536,10 @@ var Component = class {
|
|
|
1519
1536
|
const oldProps = this.rawProps;
|
|
1520
1537
|
this.rawProps = newProps;
|
|
1521
1538
|
const newRefs = newProps.ref;
|
|
1522
|
-
|
|
1539
|
+
if (oldProps.ref || newRefs) {
|
|
1540
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1541
|
+
updateRefs(newRefs, this.instance, refEffects);
|
|
1542
|
+
}
|
|
1523
1543
|
comparePropsWithCallbacks(oldProps, newProps, (key) => {
|
|
1524
1544
|
internalWrite(() => {
|
|
1525
1545
|
Reflect.deleteProperty(oldProps, key);
|
|
@@ -1549,10 +1569,10 @@ var Component = class {
|
|
|
1549
1569
|
if (this.unmountedCallbacks) this.unmountedCallbacks.forEach((fn) => {
|
|
1550
1570
|
fn();
|
|
1551
1571
|
});
|
|
1552
|
-
this.parentComponent = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = null;
|
|
1572
|
+
this.parentComponent = this.changedSubComponents = this.refEffects = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = null;
|
|
1553
1573
|
}
|
|
1554
1574
|
rendered() {
|
|
1555
|
-
this.changedSubComponents
|
|
1575
|
+
this.changedSubComponents?.clear();
|
|
1556
1576
|
const is = this.isFirstRendering;
|
|
1557
1577
|
this.isFirstRendering = false;
|
|
1558
1578
|
this._dirty = this._changed = false;
|
|
@@ -1591,6 +1611,10 @@ var Component = class {
|
|
|
1591
1611
|
this.updatedDestroyCallbacks = updatedDestroyCallbacks.length ? updatedDestroyCallbacks : null;
|
|
1592
1612
|
}
|
|
1593
1613
|
}
|
|
1614
|
+
getOrCreateRefEffects() {
|
|
1615
|
+
if (!this.refEffects) this.refEffects = /* @__PURE__ */ new Map();
|
|
1616
|
+
return this.refEffects;
|
|
1617
|
+
}
|
|
1594
1618
|
};
|
|
1595
1619
|
/**
|
|
1596
1620
|
* 获取当前组件实例
|
|
@@ -1977,24 +2001,20 @@ function patchComponent(nativeRenderer, component, oldChildAtom, newAtom, contex
|
|
|
1977
2001
|
const newTemplate = component.rerender();
|
|
1978
2002
|
const portalContainer = getContainer();
|
|
1979
2003
|
const rawContext = context;
|
|
1980
|
-
const { computedContainer } = component.viewMetadata;
|
|
2004
|
+
const { computedContainer, contextContainer } = component.viewMetadata;
|
|
1981
2005
|
popContainer();
|
|
1982
|
-
if (portalContainer)
|
|
2006
|
+
if (portalContainer) {
|
|
1983
2007
|
if (portalContainer !== computedContainer) needMove = true;
|
|
1984
|
-
|
|
1985
|
-
if (portalContainer !== computedContainer) needMove = true;
|
|
1986
|
-
context = {
|
|
2008
|
+
if (portalContainer !== context.contextContainer) context = {
|
|
1987
2009
|
isParent: true,
|
|
1988
2010
|
anchorNode: portalContainer,
|
|
1989
|
-
contextContainer:
|
|
2011
|
+
contextContainer: context.contextContainer,
|
|
1990
2012
|
computedContainer: portalContainer
|
|
1991
2013
|
};
|
|
1992
|
-
}
|
|
1993
|
-
else if (computedContainer !== context.contextContainer) needMove = true;
|
|
2014
|
+
} else if (contextContainer !== context.contextContainer) needMove = true;
|
|
1994
2015
|
component.viewMetadata = {
|
|
1995
2016
|
atom: newAtom,
|
|
1996
|
-
...context
|
|
1997
|
-
contextContainer: rawContext.contextContainer
|
|
2017
|
+
...context
|
|
1998
2018
|
};
|
|
1999
2019
|
newAtom.child = createChildChain(newTemplate, nativeRenderer, newAtom.namespace);
|
|
2000
2020
|
diff(nativeRenderer, component, newAtom.child, oldChildAtom, context, needMove);
|
|
@@ -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
|
}
|
|
@@ -2122,15 +2141,17 @@ function updateComponent(nativeRenderer, context, offset, needMove, newAtom, old
|
|
|
2122
2141
|
view.anchorNode = updatedContext.anchorNode;
|
|
2123
2142
|
view.isParent = updatedContext.isParent;
|
|
2124
2143
|
}
|
|
2144
|
+
component.rendered();
|
|
2125
2145
|
} else {
|
|
2126
2146
|
component.viewMetadata = {
|
|
2127
2147
|
atom: newAtom,
|
|
2128
2148
|
...context
|
|
2129
2149
|
};
|
|
2130
2150
|
newAtom.child = oldAtom.child;
|
|
2131
|
-
|
|
2151
|
+
const skipSubComponentDiff = !component.changedSubComponents?.size;
|
|
2152
|
+
reuseComponentView(nativeRenderer, newAtom.child, context, needMove, skipSubComponentDiff);
|
|
2153
|
+
if (!skipSubComponentDiff) component.rendered();
|
|
2132
2154
|
}
|
|
2133
|
-
component.rendered();
|
|
2134
2155
|
}
|
|
2135
2156
|
function reuseComponentView(nativeRenderer, child, context, moveView, skipSubComponentDiff) {
|
|
2136
2157
|
const updateContext = (atom) => {
|
|
@@ -2456,7 +2477,10 @@ var RootComponent = class extends Component {
|
|
|
2456
2477
|
}
|
|
2457
2478
|
markAsChanged(changedComponent) {
|
|
2458
2479
|
this._changed = true;
|
|
2459
|
-
if (changedComponent)
|
|
2480
|
+
if (changedComponent) {
|
|
2481
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
2482
|
+
this.changedSubComponents.add(changedComponent);
|
|
2483
|
+
}
|
|
2460
2484
|
this.refresh();
|
|
2461
2485
|
}
|
|
2462
2486
|
};
|
|
@@ -2533,29 +2557,39 @@ function viewfly(config) {
|
|
|
2533
2557
|
/**
|
|
2534
2558
|
* 创建一个 computed,当依赖的值发生变化时,会重新计算值。
|
|
2535
2559
|
* computed 会返回一个对象,对象的 value 属性是计算的值。
|
|
2560
|
+
*
|
|
2561
|
+
* 若重新计算的结果与缓存值 `Object.is` 相等,不会对访问该 computed 的订阅者触发更新,
|
|
2562
|
+
* 避免无关依赖抖动导致的下游副作用。
|
|
2563
|
+
*
|
|
2536
2564
|
* @param getter 计算函数,用于计算值
|
|
2537
2565
|
* @returns 一个对象,对象的 value 属性是计算的值
|
|
2538
2566
|
*/
|
|
2539
2567
|
function computed(getter) {
|
|
2540
2568
|
let cacheValue;
|
|
2569
|
+
let hasCache = false;
|
|
2541
2570
|
let dirty = true;
|
|
2542
2571
|
const target = { get value() {
|
|
2543
|
-
if (dirty)
|
|
2544
|
-
dep.destroy();
|
|
2545
|
-
pushDepContext(dep);
|
|
2546
|
-
try {
|
|
2547
|
-
cacheValue = getter();
|
|
2548
|
-
dirty = false;
|
|
2549
|
-
} finally {
|
|
2550
|
-
popDepContext();
|
|
2551
|
-
}
|
|
2552
|
-
}
|
|
2572
|
+
if (dirty) flushComputed(false);
|
|
2553
2573
|
return cacheValue;
|
|
2554
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
|
+
}
|
|
2555
2589
|
const dep = new Dep(() => {
|
|
2556
2590
|
if (!dirty) {
|
|
2557
2591
|
dirty = true;
|
|
2558
|
-
|
|
2592
|
+
if (hasEffectSubscribers(target, TrackOpTypes.Get, "value")) flushComputed(true);
|
|
2559
2593
|
}
|
|
2560
2594
|
}, "sync");
|
|
2561
2595
|
registryComponentDestroyCallback(() => {
|
|
@@ -2642,4 +2676,4 @@ function createSignal(state) {
|
|
|
2642
2676
|
return signal;
|
|
2643
2677
|
}
|
|
2644
2678
|
//#endregion
|
|
2645
|
-
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 };
|
|
2679
|
+
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) {
|
|
@@ -1470,7 +1479,7 @@ var Component = class {
|
|
|
1470
1479
|
this.props = props;
|
|
1471
1480
|
this.key = key;
|
|
1472
1481
|
this.instance = null;
|
|
1473
|
-
this.changedSubComponents =
|
|
1482
|
+
this.changedSubComponents = null;
|
|
1474
1483
|
this.viewMetadata = null;
|
|
1475
1484
|
this.unmountedCallbacks = null;
|
|
1476
1485
|
this.mountCallbacks = null;
|
|
@@ -1481,7 +1490,7 @@ var Component = class {
|
|
|
1481
1490
|
this.isFirstRendering = true;
|
|
1482
1491
|
this.rawProps = props;
|
|
1483
1492
|
this.props = createShallowReadonlyProxy({ ...props });
|
|
1484
|
-
this.refEffects =
|
|
1493
|
+
this.refEffects = null;
|
|
1485
1494
|
this.listener = new Dep(() => {
|
|
1486
1495
|
this.markAsDirtied();
|
|
1487
1496
|
}, "sync");
|
|
@@ -1491,7 +1500,10 @@ var Component = class {
|
|
|
1491
1500
|
this.markAsChanged();
|
|
1492
1501
|
}
|
|
1493
1502
|
markAsChanged(changedComponent) {
|
|
1494
|
-
if (changedComponent)
|
|
1503
|
+
if (changedComponent) {
|
|
1504
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
1505
|
+
this.changedSubComponents.add(changedComponent);
|
|
1506
|
+
}
|
|
1495
1507
|
if (this._changed) return;
|
|
1496
1508
|
this._changed = true;
|
|
1497
1509
|
if (this.parentComponent) this.parentComponent.markAsChanged(this);
|
|
@@ -1502,8 +1514,13 @@ var Component = class {
|
|
|
1502
1514
|
const isRenderFn = typeof render === "function";
|
|
1503
1515
|
this.instance = isRenderFn ? { render } : render;
|
|
1504
1516
|
onMounted(() => {
|
|
1505
|
-
|
|
1517
|
+
const refs = this.props.ref;
|
|
1518
|
+
if (refs) {
|
|
1519
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1520
|
+
applyRefs(refs, this.instance, refEffects);
|
|
1521
|
+
}
|
|
1506
1522
|
return () => {
|
|
1523
|
+
if (!this.refEffects) return;
|
|
1507
1524
|
this.refEffects.forEach((fn) => {
|
|
1508
1525
|
if (typeof fn === "function") fn();
|
|
1509
1526
|
});
|
|
@@ -1520,7 +1537,10 @@ var Component = class {
|
|
|
1520
1537
|
const oldProps = this.rawProps;
|
|
1521
1538
|
this.rawProps = newProps;
|
|
1522
1539
|
const newRefs = newProps.ref;
|
|
1523
|
-
|
|
1540
|
+
if (oldProps.ref || newRefs) {
|
|
1541
|
+
const refEffects = this.getOrCreateRefEffects();
|
|
1542
|
+
updateRefs(newRefs, this.instance, refEffects);
|
|
1543
|
+
}
|
|
1524
1544
|
comparePropsWithCallbacks(oldProps, newProps, (key) => {
|
|
1525
1545
|
internalWrite(() => {
|
|
1526
1546
|
Reflect.deleteProperty(oldProps, key);
|
|
@@ -1550,10 +1570,10 @@ var Component = class {
|
|
|
1550
1570
|
if (this.unmountedCallbacks) this.unmountedCallbacks.forEach((fn) => {
|
|
1551
1571
|
fn();
|
|
1552
1572
|
});
|
|
1553
|
-
this.parentComponent = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = null;
|
|
1573
|
+
this.parentComponent = this.changedSubComponents = this.refEffects = this.updatedDestroyCallbacks = this.mountCallbacks = this.updatedCallbacks = this.unmountedCallbacks = null;
|
|
1554
1574
|
}
|
|
1555
1575
|
rendered() {
|
|
1556
|
-
this.changedSubComponents
|
|
1576
|
+
this.changedSubComponents?.clear();
|
|
1557
1577
|
const is = this.isFirstRendering;
|
|
1558
1578
|
this.isFirstRendering = false;
|
|
1559
1579
|
this._dirty = this._changed = false;
|
|
@@ -1592,6 +1612,10 @@ var Component = class {
|
|
|
1592
1612
|
this.updatedDestroyCallbacks = updatedDestroyCallbacks.length ? updatedDestroyCallbacks : null;
|
|
1593
1613
|
}
|
|
1594
1614
|
}
|
|
1615
|
+
getOrCreateRefEffects() {
|
|
1616
|
+
if (!this.refEffects) this.refEffects = /* @__PURE__ */ new Map();
|
|
1617
|
+
return this.refEffects;
|
|
1618
|
+
}
|
|
1595
1619
|
};
|
|
1596
1620
|
/**
|
|
1597
1621
|
* 获取当前组件实例
|
|
@@ -1978,24 +2002,20 @@ function patchComponent(nativeRenderer, component, oldChildAtom, newAtom, contex
|
|
|
1978
2002
|
const newTemplate = component.rerender();
|
|
1979
2003
|
const portalContainer = getContainer();
|
|
1980
2004
|
const rawContext = context;
|
|
1981
|
-
const { computedContainer } = component.viewMetadata;
|
|
2005
|
+
const { computedContainer, contextContainer } = component.viewMetadata;
|
|
1982
2006
|
popContainer();
|
|
1983
|
-
if (portalContainer)
|
|
2007
|
+
if (portalContainer) {
|
|
1984
2008
|
if (portalContainer !== computedContainer) needMove = true;
|
|
1985
|
-
|
|
1986
|
-
if (portalContainer !== computedContainer) needMove = true;
|
|
1987
|
-
context = {
|
|
2009
|
+
if (portalContainer !== context.contextContainer) context = {
|
|
1988
2010
|
isParent: true,
|
|
1989
2011
|
anchorNode: portalContainer,
|
|
1990
|
-
contextContainer:
|
|
2012
|
+
contextContainer: context.contextContainer,
|
|
1991
2013
|
computedContainer: portalContainer
|
|
1992
2014
|
};
|
|
1993
|
-
}
|
|
1994
|
-
else if (computedContainer !== context.contextContainer) needMove = true;
|
|
2015
|
+
} else if (contextContainer !== context.contextContainer) needMove = true;
|
|
1995
2016
|
component.viewMetadata = {
|
|
1996
2017
|
atom: newAtom,
|
|
1997
|
-
...context
|
|
1998
|
-
contextContainer: rawContext.contextContainer
|
|
2018
|
+
...context
|
|
1999
2019
|
};
|
|
2000
2020
|
newAtom.child = createChildChain(newTemplate, nativeRenderer, newAtom.namespace);
|
|
2001
2021
|
diff(nativeRenderer, component, newAtom.child, oldChildAtom, context, needMove);
|
|
@@ -2020,9 +2040,8 @@ function deepUpdateByComponentDirtyTree(nativeRenderer, component, needMove) {
|
|
|
2020
2040
|
}
|
|
2021
2041
|
component.rendered();
|
|
2022
2042
|
} else if (component.changed) {
|
|
2023
|
-
component.changedSubComponents
|
|
2024
|
-
|
|
2025
|
-
});
|
|
2043
|
+
const changedSubComponents = component.changedSubComponents;
|
|
2044
|
+
if (changedSubComponents) for (const child of changedSubComponents) deepUpdateByComponentDirtyTree(nativeRenderer, child, needMove);
|
|
2026
2045
|
component.rendered();
|
|
2027
2046
|
}
|
|
2028
2047
|
}
|
|
@@ -2123,15 +2142,17 @@ function updateComponent(nativeRenderer, context, offset, needMove, newAtom, old
|
|
|
2123
2142
|
view.anchorNode = updatedContext.anchorNode;
|
|
2124
2143
|
view.isParent = updatedContext.isParent;
|
|
2125
2144
|
}
|
|
2145
|
+
component.rendered();
|
|
2126
2146
|
} else {
|
|
2127
2147
|
component.viewMetadata = {
|
|
2128
2148
|
atom: newAtom,
|
|
2129
2149
|
...context
|
|
2130
2150
|
};
|
|
2131
2151
|
newAtom.child = oldAtom.child;
|
|
2132
|
-
|
|
2152
|
+
const skipSubComponentDiff = !component.changedSubComponents?.size;
|
|
2153
|
+
reuseComponentView(nativeRenderer, newAtom.child, context, needMove, skipSubComponentDiff);
|
|
2154
|
+
if (!skipSubComponentDiff) component.rendered();
|
|
2133
2155
|
}
|
|
2134
|
-
component.rendered();
|
|
2135
2156
|
}
|
|
2136
2157
|
function reuseComponentView(nativeRenderer, child, context, moveView, skipSubComponentDiff) {
|
|
2137
2158
|
const updateContext = (atom) => {
|
|
@@ -2457,7 +2478,10 @@ var RootComponent = class extends Component {
|
|
|
2457
2478
|
}
|
|
2458
2479
|
markAsChanged(changedComponent) {
|
|
2459
2480
|
this._changed = true;
|
|
2460
|
-
if (changedComponent)
|
|
2481
|
+
if (changedComponent) {
|
|
2482
|
+
if (!this.changedSubComponents) this.changedSubComponents = /* @__PURE__ */ new Set();
|
|
2483
|
+
this.changedSubComponents.add(changedComponent);
|
|
2484
|
+
}
|
|
2461
2485
|
this.refresh();
|
|
2462
2486
|
}
|
|
2463
2487
|
};
|
|
@@ -2534,29 +2558,39 @@ function viewfly(config) {
|
|
|
2534
2558
|
/**
|
|
2535
2559
|
* 创建一个 computed,当依赖的值发生变化时,会重新计算值。
|
|
2536
2560
|
* computed 会返回一个对象,对象的 value 属性是计算的值。
|
|
2561
|
+
*
|
|
2562
|
+
* 若重新计算的结果与缓存值 `Object.is` 相等,不会对访问该 computed 的订阅者触发更新,
|
|
2563
|
+
* 避免无关依赖抖动导致的下游副作用。
|
|
2564
|
+
*
|
|
2537
2565
|
* @param getter 计算函数,用于计算值
|
|
2538
2566
|
* @returns 一个对象,对象的 value 属性是计算的值
|
|
2539
2567
|
*/
|
|
2540
2568
|
function computed(getter) {
|
|
2541
2569
|
let cacheValue;
|
|
2570
|
+
let hasCache = false;
|
|
2542
2571
|
let dirty = true;
|
|
2543
2572
|
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
|
-
}
|
|
2573
|
+
if (dirty) flushComputed(false);
|
|
2554
2574
|
return cacheValue;
|
|
2555
2575
|
} };
|
|
2576
|
+
function flushComputed(notify) {
|
|
2577
|
+
dep.destroy();
|
|
2578
|
+
pushDepContext(dep);
|
|
2579
|
+
try {
|
|
2580
|
+
const newValue = getter();
|
|
2581
|
+
const changed = !hasCache || !Object.is(newValue, cacheValue);
|
|
2582
|
+
hasCache = true;
|
|
2583
|
+
cacheValue = newValue;
|
|
2584
|
+
dirty = false;
|
|
2585
|
+
if (notify && changed) trigger(target, TriggerOpTypes.Set, "value");
|
|
2586
|
+
} finally {
|
|
2587
|
+
popDepContext();
|
|
2588
|
+
}
|
|
2589
|
+
}
|
|
2556
2590
|
const dep = new Dep(() => {
|
|
2557
2591
|
if (!dirty) {
|
|
2558
2592
|
dirty = true;
|
|
2559
|
-
|
|
2593
|
+
if (hasEffectSubscribers(target, TrackOpTypes.Get, "value")) flushComputed(true);
|
|
2560
2594
|
}
|
|
2561
2595
|
}, "sync");
|
|
2562
2596
|
registryComponentDestroyCallback(() => {
|
|
@@ -2695,6 +2729,7 @@ exports.forwardRef = forwardRef;
|
|
|
2695
2729
|
exports.getCurrentInstance = getCurrentInstance;
|
|
2696
2730
|
exports.getDepContext = getDepContext;
|
|
2697
2731
|
exports.getSetupContext = getSetupContext;
|
|
2732
|
+
exports.hasEffectSubscribers = hasEffectSubscribers;
|
|
2698
2733
|
exports.inject = inject;
|
|
2699
2734
|
exports.internalWrite = internalWrite;
|
|
2700
2735
|
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