vite-plugin-taro-runtime 0.7.13 → 0.8.0-beta.0
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 +2 -0
- package/dist/plugin-framework-react/runtime.js +3 -3
- package/dist/plugin-html/html-elements.d.ts +4 -0
- package/dist/plugin-html/html-elements.js +94 -0
- package/dist/plugin-html/runtime.js +1 -12
- package/dist/react/index.d.ts +3 -1
- package/dist/react/react.esm.js +56 -48
- package/dist/react/renderer-host.d.ts +4 -0
- package/dist/runtime/dom/element.d.ts +21 -3
- package/dist/runtime/dom/element.js +58 -29
- package/dist/runtime/dom/event-source.js +5 -1
- package/dist/runtime/dom/event-target.d.ts +11 -1
- package/dist/runtime/dom/event-target.js +35 -7
- package/dist/runtime/dom/event.d.ts +8 -1
- package/dist/runtime/dom/event.js +45 -14
- package/dist/runtime/dom/nearest-ctx.d.ts +3 -1
- package/dist/runtime/dom/nearest-ctx.js +10 -1
- package/dist/runtime/dom/node.d.ts +15 -7
- package/dist/runtime/dom/node.js +88 -44
- package/dist/runtime/dom/style.d.ts +7 -2
- package/dist/runtime/dom/style.js +19 -5
- package/dist/runtime/dom/text.d.ts +3 -2
- package/dist/runtime/dom/text.js +13 -6
- package/dist/runtime/dom-external/mutation-observer/implements.d.ts +3 -1
- package/dist/runtime/dom-external/mutation-observer/implements.js +5 -1
- package/dist/runtime/dom-external/mutation-observer/index.d.ts +2 -0
- package/dist/runtime/dom-external/mutation-observer/index.js +5 -1
- package/dist/runtime/dom-external/node.js +56 -18
- package/dist/runtime/dsl/common.d.ts +1 -0
- package/dist/runtime/dsl/common.js +22 -74
- package/dist/runtime/dsl/instance.d.ts +2 -3
- package/dist/runtime/index.d.ts +1 -1
- package/dist/runtime/index.js +1 -1
- package/dist/runtime/utils/index.js +1 -1
- package/package.json +7 -1
- package/types/tt-document.d.ts +21 -2
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { hooks, isObject, warn, isArray } from '@tarojs/shared';
|
|
2
2
|
|
|
3
3
|
class TaroEventTarget {
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
// Each target retains one mutable table, initialized on first mutable access.
|
|
5
|
+
get __handlers() {
|
|
6
|
+
return this._handlers ??= {};
|
|
7
|
+
}
|
|
8
|
+
set __handlers(value) {
|
|
9
|
+
this._handlers = value;
|
|
10
|
+
}
|
|
11
|
+
getEventHandlers(type) {
|
|
12
|
+
return this._handlers?.[type];
|
|
6
13
|
}
|
|
7
14
|
addEventListener(type, handler, options) {
|
|
8
15
|
type = type.toLowerCase();
|
|
@@ -44,6 +51,11 @@ class TaroEventTarget {
|
|
|
44
51
|
else {
|
|
45
52
|
this.__handlers[type] = [handler];
|
|
46
53
|
}
|
|
54
|
+
// 用原有处理器数组判断首次订阅:第二个及后续处理器共用同一个原生入口。
|
|
55
|
+
// 通知发生在数组写入后,host 可直接读取已完成的逻辑状态。
|
|
56
|
+
if (this.__handlers[type].length === 1) {
|
|
57
|
+
this.delegateEventListener(type, true);
|
|
58
|
+
}
|
|
47
59
|
}
|
|
48
60
|
removeEventListener(type, handler) {
|
|
49
61
|
type = type.toLowerCase();
|
|
@@ -56,7 +68,7 @@ class TaroEventTarget {
|
|
|
56
68
|
if (!handler) {
|
|
57
69
|
return;
|
|
58
70
|
}
|
|
59
|
-
const handlers = this.
|
|
71
|
+
const handlers = this.getEventHandlers(type);
|
|
60
72
|
if (!isArray(handlers)) {
|
|
61
73
|
return;
|
|
62
74
|
}
|
|
@@ -65,16 +77,32 @@ class TaroEventTarget {
|
|
|
65
77
|
return true;
|
|
66
78
|
});
|
|
67
79
|
process.env.NODE_ENV !== 'production' && warn(index === -1, `事件: '${type}' 没有注册在 DOM 中,因此不会被移除。`);
|
|
80
|
+
if (index === -1) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
68
83
|
handlers.splice(index, 1);
|
|
84
|
+
// 最后一个处理器移除后再通知解绑;长度判断复用现有数组,为 O(1)。
|
|
85
|
+
if (handlers.length === 0) {
|
|
86
|
+
this.delegateEventListener(type, false);
|
|
87
|
+
}
|
|
69
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* 处理器数组从空变为非空时 subscribed=true,最后一个处理器移除后为 false。
|
|
91
|
+
* type 是 Taro 已规范化的小写逻辑事件名;regionchange 已展开为 begin/end,
|
|
92
|
+
* 平台原生事件名及其订阅关系由 host 对接。
|
|
93
|
+
* 模板通过既有模板绑定接收事件,因此默认实现为空;原生 host 在此控制订阅开关,
|
|
94
|
+
* 业务处理器仍由 __handlers 保存,once、包装函数和移除匹配继续由 Taro 负责。
|
|
95
|
+
* 原生回调可通过 eventHandler(event, logicalNode) 进入共享派发流程。
|
|
96
|
+
*/
|
|
97
|
+
delegateEventListener(_type, _subscribed) { }
|
|
70
98
|
isAnyEventBinded() {
|
|
71
|
-
const handlers = this.
|
|
72
|
-
const isAnyEventBinded = Object.keys(handlers).find(key => handlers[key].length);
|
|
99
|
+
const handlers = this._handlers;
|
|
100
|
+
const isAnyEventBinded = handlers && Object.keys(handlers).find(key => handlers[key].length);
|
|
73
101
|
return Boolean(isAnyEventBinded);
|
|
74
102
|
}
|
|
75
103
|
isOnlyClickBinded() {
|
|
76
|
-
const handlers = this.
|
|
77
|
-
const isOnlyClickBinded = handlers
|
|
104
|
+
const handlers = this._handlers;
|
|
105
|
+
const isOnlyClickBinded = handlers?.tap && Object.keys(handlers).length === 1;
|
|
78
106
|
return Boolean(isOnlyClickBinded);
|
|
79
107
|
}
|
|
80
108
|
}
|
|
@@ -17,11 +17,18 @@ declare class TaroEvent {
|
|
|
17
17
|
stopPropagation(): void;
|
|
18
18
|
stopImmediatePropagation(): void;
|
|
19
19
|
preventDefault(): void;
|
|
20
|
+
/** Template lookup policy; native hosts return null to use SDK target records. */
|
|
21
|
+
getTargetElement(target: MpEvent['target'] | undefined, useTargetDataset: boolean): TaroElement | null;
|
|
20
22
|
get target(): any;
|
|
21
23
|
get currentTarget(): any;
|
|
22
24
|
}
|
|
23
25
|
declare function createEvent(event: MpEvent | string, node?: TaroElement): TaroEvent;
|
|
24
|
-
|
|
26
|
+
/**
|
|
27
|
+
* 共用平台归一化、事件 hooks、批处理及表单处理的原生事件入口。
|
|
28
|
+
* currentNode 可传入已知 Taro 逻辑节点以跳过入口 ID 查询;省略时按原有事件索引查找。
|
|
29
|
+
* target/currentTarget 按 host 的 getTargetElement 策略读取模板索引或 SDK 数据。
|
|
30
|
+
*/
|
|
31
|
+
declare function eventHandler(event: MpEvent, currentNode?: TaroElement): any;
|
|
25
32
|
declare function eventHandlerTTDom(ele: any, listener: (event: MpEvent, element: any) => void, event: MpEvent): void;
|
|
26
33
|
|
|
27
34
|
export { TaroEvent, createEvent, eventHandler, eventHandlerTTDom };
|
|
@@ -28,15 +28,23 @@ class TaroEvent {
|
|
|
28
28
|
preventDefault() {
|
|
29
29
|
this.defaultPrevented = true;
|
|
30
30
|
}
|
|
31
|
+
/** Template events resolve logical datasets; native hosts use SDK target records. */
|
|
32
|
+
getTargetElement(target, useTargetDataset) {
|
|
33
|
+
const id = (useTargetDataset && target?.targetDataset?.sid) || target?.dataset?.sid || target?.id || null;
|
|
34
|
+
return env.document.getElementById(id);
|
|
35
|
+
}
|
|
31
36
|
get target() {
|
|
32
|
-
var _a,
|
|
37
|
+
var _a, _e;
|
|
33
38
|
const cacheTarget = this.cacheTarget;
|
|
34
39
|
if (!cacheTarget) {
|
|
35
40
|
const target = Object.create(((_a = this.mpEvent) === null || _a === void 0 ? void 0 : _a.target) || null);
|
|
36
|
-
const currentEle =
|
|
37
|
-
//
|
|
38
|
-
const element =
|
|
39
|
-
|
|
41
|
+
const currentEle = this.getTargetElement(target, false);
|
|
42
|
+
// Alipay's targetDataset identifies the bubbling target separately from its wrapper.
|
|
43
|
+
const element = this.getTargetElement(target, true);
|
|
44
|
+
// 索引命中时仍以逻辑 dataset 为准,保留对象值及支付宝两层数据的合并顺序。
|
|
45
|
+
// 索引未命中只表示无法取得逻辑节点,不能丢弃平台已提供的数据。
|
|
46
|
+
// 两个来源分别兜底,复用原有 Object.assign 和缓存;不补建索引或遍历节点树。
|
|
47
|
+
target.dataset = Object.assign(Object.assign({}, (currentEle !== null ? currentEle.dataset : target.dataset || EMPTY_OBJ)), (element !== null ? element.dataset : target.targetDataset || EMPTY_OBJ));
|
|
40
48
|
for (const key in (_e = this.mpEvent) === null || _e === void 0 ? void 0 : _e.detail) {
|
|
41
49
|
target[key] = this.mpEvent.detail[key];
|
|
42
50
|
}
|
|
@@ -48,18 +56,22 @@ class TaroEvent {
|
|
|
48
56
|
}
|
|
49
57
|
}
|
|
50
58
|
get currentTarget() {
|
|
51
|
-
var _a,
|
|
59
|
+
var _a, _h;
|
|
52
60
|
const cacheCurrentTarget = this.cacheCurrentTarget;
|
|
53
61
|
if (!cacheCurrentTarget) {
|
|
54
|
-
const doc = env.document;
|
|
55
62
|
const currentTarget = Object.create(((_a = this.mpEvent) === null || _a === void 0 ? void 0 : _a.currentTarget) || null);
|
|
56
|
-
const element =
|
|
57
|
-
const targetElement =
|
|
58
|
-
|
|
63
|
+
const element = this.getTargetElement(currentTarget, false);
|
|
64
|
+
const targetElement = this.getTargetElement(this.mpEvent?.target, false);
|
|
65
|
+
// 已知逻辑节点相同才沿用 target;未命中时,只有同一原始对象能证明身份相同。
|
|
66
|
+
// 两个 null 查询结果、空 ID 或相同的未解析 ID 都不能证明两个事件目标相同。
|
|
67
|
+
// 无平台事件时保留手动构造事件的共享目标;这里只比较已有引用,不新增查询或状态。
|
|
68
|
+
if (element !== null ? element === targetElement : !this.mpEvent || this.mpEvent.currentTarget === this.mpEvent.target) {
|
|
59
69
|
this.cacheCurrentTarget = this.target;
|
|
60
70
|
return this.target;
|
|
61
71
|
}
|
|
62
|
-
|
|
72
|
+
// 保留已知逻辑 dataset 的引用;未命中时使用当前接收目标自己的平台 dataset。
|
|
73
|
+
// detail 合并和惰性缓存继续复用下方流程,不把接收目标替换成触发目标。
|
|
74
|
+
currentTarget.dataset = element !== null ? element.dataset : currentTarget.dataset || EMPTY_OBJ;
|
|
63
75
|
for (const key in (_h = this.mpEvent) === null || _h === void 0 ? void 0 : _h.detail) {
|
|
64
76
|
currentTarget[key] = this.mpEvent.detail[key];
|
|
65
77
|
}
|
|
@@ -100,7 +112,27 @@ function getEventCBResult(event) {
|
|
|
100
112
|
return result;
|
|
101
113
|
}
|
|
102
114
|
// 小程序的事件代理回调函数
|
|
103
|
-
|
|
115
|
+
/**
|
|
116
|
+
* Taro 节点维护逻辑树、属性和业务监听器;TT 原生节点负责显示并接收平台事件。
|
|
117
|
+
*
|
|
118
|
+
* 模板中的多个原生节点共用此入口。eventHandler(event) 在平台事件归一化后,
|
|
119
|
+
* 根据事件中的 sid/id 从 Taro 已有索引取得接收节点,再进入统一的事件派发流程。
|
|
120
|
+
* 这里的索引查询不是整棵树的遍历。
|
|
121
|
+
*
|
|
122
|
+
* DOM host 为某个原生节点注册回调时,已经持有对应的 Taro 逻辑节点,可以直接传入:
|
|
123
|
+
* nativeNode.addEventListener(nativeEventName, event => eventHandler(event, taroNode));
|
|
124
|
+
* eventHandler(event, taroNode) 使用这个已知接收节点,省去入口的一次 ID 读取和索引查询。
|
|
125
|
+
* currentNode 是 Taro 节点,不是 TT 原生节点;该参数是可选的,原生事件携带正确的
|
|
126
|
+
* sid/id 时也可以使用单参数入口。它只是利用已知对象,并非 DOM 渲染的必要条件。
|
|
127
|
+
*
|
|
128
|
+
* 两种入口共用平台归一化、事件构造、hooks、冒泡批处理、回调返回值,以及 React
|
|
129
|
+
* 事件优先级和受控输入恢复。node.dispatchEvent() 只是其中靠后的环节,直接调用它
|
|
130
|
+
* 会绕过外围处理,因此 host 应从这里进入已有事件流程。
|
|
131
|
+
*
|
|
132
|
+
* 可选参数只改变接收节点的取得方式,不增加持久状态或循环,也不代表显著性能提升。
|
|
133
|
+
* target/currentTarget 通过 getTargetElement 选择模板索引或原生 SDK 数据。
|
|
134
|
+
*/
|
|
135
|
+
function eventHandler(event, currentNode) {
|
|
104
136
|
var _a, _b;
|
|
105
137
|
// Note: ohos 上事件没有设置 type、detail 类型 setter 方法,且部分事件(例如 load 等)缺失 target 导致事件错误
|
|
106
138
|
event.type === undefined && Object.defineProperty(event, 'type', {
|
|
@@ -112,8 +144,7 @@ function eventHandler(event) {
|
|
|
112
144
|
event.currentTarget = event.currentTarget || event.target || Object.assign({}, event);
|
|
113
145
|
hooks.call('modifyMpEventImpl', event);
|
|
114
146
|
const currentTarget = event.currentTarget;
|
|
115
|
-
const
|
|
116
|
-
const node = env.document.getElementById(id);
|
|
147
|
+
const node = currentNode || env.document.getElementById(((_a = currentTarget.dataset) === null || _a === void 0 ? void 0 : _a.sid /** sid */) || currentTarget.id /** uid */ || ((_b = event.detail) === null || _b === void 0 ? void 0 : _b.id) || '');
|
|
117
148
|
if (node) {
|
|
118
149
|
const dispatch = () => {
|
|
119
150
|
const e = createEvent(event, node);
|
|
@@ -4,6 +4,8 @@ import { TaroRootElement } from './root.js';
|
|
|
4
4
|
|
|
5
5
|
declare function isNearestCtxEnv(): boolean;
|
|
6
6
|
declare function getNearestCtx(node: TaroNode): MpInstance | null | undefined;
|
|
7
|
+
declare function getCachedNearestCtx(node: TaroNode): MpInstance | null | undefined;
|
|
8
|
+
declare function invalidateNearestCtx(node: TaroNode): void;
|
|
7
9
|
declare function bumpNearestCtxEpochForRoot(root: TaroRootElement | null | undefined): void;
|
|
8
10
|
|
|
9
|
-
export { bumpNearestCtxEpochForRoot, getNearestCtx, isNearestCtxEnv };
|
|
11
|
+
export { bumpNearestCtxEpochForRoot, getCachedNearestCtx, getNearestCtx, invalidateNearestCtx, isNearestCtxEnv };
|
|
@@ -1,8 +1,12 @@
|
|
|
1
|
+
// Template context reads reuse values until a root epoch change invalidates them.
|
|
1
2
|
const nearestCtxCache = new WeakMap();
|
|
2
3
|
function isNearestCtxEnv() {
|
|
3
4
|
return process.env.TARO_ENV === 'weapp' || process.env.TARO_ENV === 'jd';
|
|
4
5
|
}
|
|
5
6
|
function getNearestCtx(node) {
|
|
7
|
+
return node._scope;
|
|
8
|
+
}
|
|
9
|
+
function getCachedNearestCtx(node) {
|
|
6
10
|
if (!isNearestCtxEnv()) {
|
|
7
11
|
return undefined;
|
|
8
12
|
}
|
|
@@ -36,6 +40,11 @@ function resolveNearestCtxValue(node, root) {
|
|
|
36
40
|
}
|
|
37
41
|
return (_a = root.ctx) !== null && _a !== void 0 ? _a : null;
|
|
38
42
|
}
|
|
43
|
+
function invalidateNearestCtx(node) {
|
|
44
|
+
if (isNearestCtxEnv()) {
|
|
45
|
+
node.delegateContextInvalidation();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
39
48
|
function bumpNearestCtxEpochForRoot(root) {
|
|
40
49
|
if (!isNearestCtxEnv() || root == null) {
|
|
41
50
|
return;
|
|
@@ -45,5 +54,5 @@ function bumpNearestCtxEpochForRoot(root) {
|
|
|
45
54
|
}
|
|
46
55
|
}
|
|
47
56
|
|
|
48
|
-
export { bumpNearestCtxEpochForRoot, getNearestCtx, isNearestCtxEnv };
|
|
57
|
+
export { bumpNearestCtxEpochForRoot, getCachedNearestCtx, getNearestCtx, invalidateNearestCtx, isNearestCtxEnv };
|
|
49
58
|
//# sourceMappingURL=nearest-ctx.js.map
|
|
@@ -19,12 +19,17 @@ declare class TaroNode extends TaroEventTarget {
|
|
|
19
19
|
parentNode: TaroNode | null;
|
|
20
20
|
childNodes: TaroNode[];
|
|
21
21
|
constructor();
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
/**
|
|
23
|
+
* 子列表整体更新通知,包括 textContent 完成逐项变更后的最终通知。
|
|
24
|
+
* isClean=true 对应空列表,否则模板默认延迟序列化当前子节点;逻辑树已由调用方修改。
|
|
25
|
+
* 已通过逐项委托同步原生关系的 host 可将此作为完成通知。
|
|
26
|
+
*/
|
|
24
27
|
delegateChildReset(isClean?: boolean): void;
|
|
25
28
|
private updateSingleChild;
|
|
26
29
|
get _root(): TaroRootElement | null;
|
|
27
30
|
get _scope(): MpInstance | null | undefined;
|
|
31
|
+
/** Invalidate template context caches after presentation or a context lifecycle change. */
|
|
32
|
+
delegateContextInvalidation(): void;
|
|
28
33
|
protected findIndex(refChild: TaroNode): number;
|
|
29
34
|
get _path(): string;
|
|
30
35
|
get nextSibling(): TaroNode | null;
|
|
@@ -73,13 +78,16 @@ declare class TaroNode extends TaroEventTarget {
|
|
|
73
78
|
*/
|
|
74
79
|
removeChild<T extends TaroNode>(child: T, options?: RemoveChildOptions): T;
|
|
75
80
|
/**
|
|
76
|
-
*
|
|
77
|
-
*
|
|
81
|
+
* 逻辑插入完成后调用,包含未挂载的父节点;默认准备模板 payload。
|
|
82
|
+
* this 为新父节点,refChild 为空表示追加;index 仅在存在 refChild 时表示插入位置。
|
|
83
|
+
* isReplace 表示替换的插入阶段,旧 refChild 随后另行移除;host 此处只同步插入。
|
|
84
|
+
* previousIndex 为同父节点移动前的位置;移动仅通知插入,模板默认延迟更新完整子列表。
|
|
78
85
|
*/
|
|
79
|
-
delegateChildInsertion(newChild: TaroNode, refChild: TaroNode | null | undefined, index: number, isReplace: boolean | undefined): void;
|
|
86
|
+
delegateChildInsertion(newChild: TaroNode, refChild: TaroNode | null | undefined, index: number, isReplace: boolean | undefined, previousIndex?: number): void;
|
|
80
87
|
/**
|
|
81
|
-
*
|
|
82
|
-
*
|
|
88
|
+
* 逻辑解绑及按 cleanRef 清理事件索引后调用;this 为原父节点,child.parentNode 已为空。
|
|
89
|
+
* 每次实际移除都会通知,options.doUpdate=false 仅抑制默认模板更新。
|
|
90
|
+
* host 使用 child 同步原生解绑,逻辑树和事件索引仍由 Taro 维护。
|
|
83
91
|
*/
|
|
84
92
|
delegateChildRemoval(child: TaroNode, options: RemoveChildOptions): void;
|
|
85
93
|
remove(options?: RemoveChildOptions): void;
|
package/dist/runtime/dom/node.js
CHANGED
|
@@ -6,7 +6,7 @@ import { hydrate } from '../hydrate.js';
|
|
|
6
6
|
import { incrementId, isComment, extend } from '../utils/index.js';
|
|
7
7
|
import { eventSource } from './event-source.js';
|
|
8
8
|
import { TaroEventTarget } from './event-target.js';
|
|
9
|
-
import {
|
|
9
|
+
import { getCachedNearestCtx, bumpNearestCtxEpochForRoot } from './nearest-ctx.js';
|
|
10
10
|
|
|
11
11
|
const CHILDNODES = "cn" /* Shortcuts.Childnodes */;
|
|
12
12
|
const nodeId = incrementId();
|
|
@@ -15,12 +15,18 @@ class TaroNode extends TaroEventTarget {
|
|
|
15
15
|
super();
|
|
16
16
|
this.parentNode = null;
|
|
17
17
|
this.childNodes = [];
|
|
18
|
-
this.hydrate = (node) => () => hydrate(node);
|
|
19
18
|
this.uid = '_' + nodeId(); // dom 节点 id,开发者可修改
|
|
20
19
|
this.sid = this.uid; // dom 节点全局唯一 id,不可被修改
|
|
21
20
|
eventSource.set(this.sid, this);
|
|
22
21
|
}
|
|
23
|
-
/**
|
|
22
|
+
/**
|
|
23
|
+
* 通知整个子列表的最终呈现状态;包括 textContent 完成逐项删除/插入后的整表通知。
|
|
24
|
+
* isClean 为 true 时,模板 payload 的值为空列表;否则在 flush 时序列化当前子节点。
|
|
25
|
+
* 逻辑树的修改已经由调用方完成,此方法只准备呈现更新,不负责清空或重建逻辑树。
|
|
26
|
+
* 默认实现保留过滤注释节点、延迟 hydrate 和 cn 路径更新。
|
|
27
|
+
* 已通过逐项插入/移除委托同步原生树的 host,可将此处作为完成通知,
|
|
28
|
+
* 避免把同一逻辑变更再次执行为整树遍历或原生节点重建。
|
|
29
|
+
*/
|
|
24
30
|
delegateChildReset(isClean) {
|
|
25
31
|
const cleanChildNodes = () => [];
|
|
26
32
|
const rerenderChildNodes = () => {
|
|
@@ -40,7 +46,7 @@ class TaroNode extends TaroEventTarget {
|
|
|
40
46
|
return;
|
|
41
47
|
this.enqueueUpdate({
|
|
42
48
|
path: child._path,
|
|
43
|
-
value:
|
|
49
|
+
value: () => hydrate(child)
|
|
44
50
|
});
|
|
45
51
|
});
|
|
46
52
|
}
|
|
@@ -49,7 +55,11 @@ class TaroNode extends TaroEventTarget {
|
|
|
49
55
|
return ((_a = this.parentNode) === null || _a === void 0 ? void 0 : _a._root) || null;
|
|
50
56
|
}
|
|
51
57
|
get _scope() {
|
|
52
|
-
return
|
|
58
|
+
return getCachedNearestCtx(this);
|
|
59
|
+
}
|
|
60
|
+
/** Called after child presentation and at Page/component context lifecycle boundaries. */
|
|
61
|
+
delegateContextInvalidation() {
|
|
62
|
+
bumpNearestCtxEpochForRoot(this._root);
|
|
53
63
|
}
|
|
54
64
|
findIndex(refChild) {
|
|
55
65
|
const index = this.childNodes.indexOf(refChild);
|
|
@@ -95,8 +105,13 @@ class TaroNode extends TaroEventTarget {
|
|
|
95
105
|
*/
|
|
96
106
|
// eslint-disable-next-line accessor-pairs
|
|
97
107
|
set textContent(text) {
|
|
98
|
-
|
|
99
|
-
const
|
|
108
|
+
// Capture old children only for registered observers; added nodes fill this operation-local record.
|
|
109
|
+
const record = MutationObserver.hasObservers ? {
|
|
110
|
+
type: "childList" /* MutationRecordType.CHILD_LIST */,
|
|
111
|
+
target: this,
|
|
112
|
+
removedNodes: this.childNodes.slice(),
|
|
113
|
+
addedNodes: []
|
|
114
|
+
} : null;
|
|
100
115
|
// Handle old children' data structure & ref
|
|
101
116
|
while (this.firstChild) {
|
|
102
117
|
this.removeChild(this.firstChild, { doUpdate: false });
|
|
@@ -105,18 +120,17 @@ class TaroNode extends TaroEventTarget {
|
|
|
105
120
|
this.delegateChildReset(true);
|
|
106
121
|
}
|
|
107
122
|
else {
|
|
108
|
-
const newText =
|
|
109
|
-
|
|
123
|
+
const newText = this.ownerDocument.createTextNode(text);
|
|
124
|
+
if (record) {
|
|
125
|
+
record.addedNodes.push(newText);
|
|
126
|
+
}
|
|
110
127
|
this.appendChild(newText);
|
|
111
128
|
this.delegateChildReset();
|
|
112
129
|
}
|
|
113
130
|
// @Todo: appendChild 会多触发一次
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
removedNodes,
|
|
118
|
-
addedNodes
|
|
119
|
-
});
|
|
131
|
+
if (record) {
|
|
132
|
+
MutationObserver.record(record);
|
|
133
|
+
}
|
|
120
134
|
}
|
|
121
135
|
/**
|
|
122
136
|
* @doc https://developer.mozilla.org/zh-CN/docs/Web/API/Node/insertBefore
|
|
@@ -127,6 +141,10 @@ class TaroNode extends TaroEventTarget {
|
|
|
127
141
|
* 3. insert D before C, D has the different parent of C
|
|
128
142
|
*/
|
|
129
143
|
insertBefore(newChild, refChild, isReplace) {
|
|
144
|
+
if (newChild === refChild) {
|
|
145
|
+
this.findIndex(refChild);
|
|
146
|
+
return newChild;
|
|
147
|
+
}
|
|
130
148
|
if (newChild.nodeName === DOCUMENT_FRAGMENT) {
|
|
131
149
|
newChild.childNodes.reduceRight((previousValue, currentValue) => {
|
|
132
150
|
this.insertBefore(currentValue, previousValue);
|
|
@@ -134,10 +152,15 @@ class TaroNode extends TaroEventTarget {
|
|
|
134
152
|
}, refChild);
|
|
135
153
|
return newChild;
|
|
136
154
|
}
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
155
|
+
const previousIndex = newChild.parentNode === this ? this.findIndex(newChild) : undefined;
|
|
156
|
+
if (previousIndex !== undefined) {
|
|
157
|
+
// Reorder the sibling array in place, retaining parent and event identity for the native move.
|
|
158
|
+
this.childNodes.splice(previousIndex, 1);
|
|
159
|
+
}
|
|
160
|
+
else {
|
|
161
|
+
newChild.remove({ cleanRef: false });
|
|
162
|
+
}
|
|
163
|
+
// The insertion index is assigned after removal so forward moves use the anchor's current position.
|
|
141
164
|
let index = 0;
|
|
142
165
|
// Data structure
|
|
143
166
|
newChild.parentNode = this;
|
|
@@ -152,22 +175,22 @@ class TaroNode extends TaroEventTarget {
|
|
|
152
175
|
}
|
|
153
176
|
// Delegation runs after the logical insertion, including on detached parents.
|
|
154
177
|
// Its default implementation prepares template payloads; native hosts can replace it before any serialization work.
|
|
155
|
-
this.delegateChildInsertion(newChild, refChild, index, isReplace);
|
|
156
|
-
|
|
157
|
-
|
|
178
|
+
this.delegateChildInsertion(newChild, refChild, index, isReplace, previousIndex);
|
|
179
|
+
this.delegateContextInvalidation();
|
|
180
|
+
if (MutationObserver.hasObservers) {
|
|
181
|
+
MutationObserver.record({
|
|
182
|
+
type: "childList" /* MutationRecordType.CHILD_LIST */,
|
|
183
|
+
target: this,
|
|
184
|
+
addedNodes: [newChild],
|
|
185
|
+
removedNodes: isReplace
|
|
186
|
+
? [refChild] /** replaceChild */
|
|
187
|
+
: [],
|
|
188
|
+
nextSibling: isReplace
|
|
189
|
+
? refChild.nextSibling /** replaceChild */
|
|
190
|
+
: (refChild || null), /** insertBefore & appendChild */
|
|
191
|
+
previousSibling: newChild.previousSibling
|
|
192
|
+
});
|
|
158
193
|
}
|
|
159
|
-
MutationObserver.record({
|
|
160
|
-
type: "childList" /* MutationRecordType.CHILD_LIST */,
|
|
161
|
-
target: this,
|
|
162
|
-
addedNodes: [newChild],
|
|
163
|
-
removedNodes: isReplace
|
|
164
|
-
? [refChild] /** replaceChild */
|
|
165
|
-
: [],
|
|
166
|
-
nextSibling: isReplace
|
|
167
|
-
? refChild.nextSibling /** replaceChild */
|
|
168
|
-
: (refChild || null), /** insertBefore & appendChild */
|
|
169
|
-
previousSibling: newChild.previousSibling
|
|
170
|
-
});
|
|
171
194
|
return newChild;
|
|
172
195
|
}
|
|
173
196
|
/**
|
|
@@ -192,6 +215,9 @@ class TaroNode extends TaroEventTarget {
|
|
|
192
215
|
replaceChild(newChild, oldChild) {
|
|
193
216
|
if (oldChild.parentNode !== this)
|
|
194
217
|
return;
|
|
218
|
+
if (newChild === oldChild) {
|
|
219
|
+
return oldChild;
|
|
220
|
+
}
|
|
195
221
|
// Insert the newChild
|
|
196
222
|
this.insertBefore(newChild, oldChild, true);
|
|
197
223
|
// Destroy the oldChild
|
|
@@ -209,7 +235,7 @@ class TaroNode extends TaroEventTarget {
|
|
|
209
235
|
*/
|
|
210
236
|
removeChild(child, options = {}) {
|
|
211
237
|
const { cleanRef, doUpdate } = options;
|
|
212
|
-
if (cleanRef !== false && doUpdate !== false) {
|
|
238
|
+
if (cleanRef !== false && doUpdate !== false && MutationObserver.hasObservers) {
|
|
213
239
|
// appendChild/replaceChild/insertBefore 不应该触发
|
|
214
240
|
// @Todo: 但其实如果 newChild 的父节点是另一颗子树的节点,应该是要触发的
|
|
215
241
|
MutationObserver.record({
|
|
@@ -230,17 +256,28 @@ class TaroNode extends TaroEventTarget {
|
|
|
230
256
|
}
|
|
231
257
|
// Forward every removal. doUpdate controls template batching, not whether the logical child was removed.
|
|
232
258
|
this.delegateChildRemoval(child, options);
|
|
233
|
-
|
|
234
|
-
bumpNearestCtxEpochForRoot(this._root);
|
|
235
|
-
}
|
|
259
|
+
this.delegateContextInvalidation();
|
|
236
260
|
return child;
|
|
237
261
|
}
|
|
238
|
-
/**
|
|
239
|
-
|
|
262
|
+
/**
|
|
263
|
+
* 在 Taro 更新 parentNode/childNodes 后委托插入,父节点尚未挂载时也会调用。
|
|
264
|
+
* this 是新父节点;newChild 是已插入的逻辑节点;refChild 为空表示追加。
|
|
265
|
+
* index 仅在有 refChild 时表示逻辑插入位置,追加分支中的值不表示末尾索引。
|
|
266
|
+
* isReplace 表示 replaceChild 的插入阶段:refChild 此时仍在树中,随后由原方法
|
|
267
|
+
* 单独移除。原生 host 此处执行插入,旧节点的移除由移除委托接收。
|
|
268
|
+
* previousIndex 是同父节点移动前的位置;移动仅通知插入,保留 parentNode 和事件身份。
|
|
269
|
+
* 默认实现对移动延迟更新完整子列表,其余插入保留局部更新/整表更新选择。
|
|
270
|
+
* 原有上下文缓存失效和插入 MutationObserver 记录仍由调用方在委托之后完成。
|
|
271
|
+
*/
|
|
272
|
+
delegateChildInsertion(newChild, refChild, index, isReplace, previousIndex) {
|
|
240
273
|
const childNodesLength = this.childNodes.length;
|
|
241
274
|
// Serialization
|
|
242
275
|
if (this._root) {
|
|
243
|
-
if (
|
|
276
|
+
if (previousIndex !== undefined) {
|
|
277
|
+
// A move changes existing slots, including append and replacement. Hydrate the final list at flush.
|
|
278
|
+
this.delegateChildReset();
|
|
279
|
+
}
|
|
280
|
+
else if (!refChild) {
|
|
244
281
|
// appendChild
|
|
245
282
|
const isOnlyChild = childNodesLength === 1;
|
|
246
283
|
if (isOnlyChild) {
|
|
@@ -249,7 +286,7 @@ class TaroNode extends TaroEventTarget {
|
|
|
249
286
|
else {
|
|
250
287
|
this.enqueueUpdate({
|
|
251
288
|
path: newChild._path,
|
|
252
|
-
value:
|
|
289
|
+
value: () => hydrate(newChild)
|
|
253
290
|
});
|
|
254
291
|
}
|
|
255
292
|
}
|
|
@@ -257,7 +294,7 @@ class TaroNode extends TaroEventTarget {
|
|
|
257
294
|
// replaceChild
|
|
258
295
|
this.enqueueUpdate({
|
|
259
296
|
path: newChild._path,
|
|
260
|
-
value:
|
|
297
|
+
value: () => hydrate(newChild)
|
|
261
298
|
});
|
|
262
299
|
}
|
|
263
300
|
else {
|
|
@@ -284,7 +321,14 @@ class TaroNode extends TaroEventTarget {
|
|
|
284
321
|
}
|
|
285
322
|
}
|
|
286
323
|
}
|
|
287
|
-
/**
|
|
324
|
+
/**
|
|
325
|
+
* 在逻辑解绑及按 cleanRef 执行事件索引清理后委托移除。
|
|
326
|
+
* this 仍是原父节点,child.parentNode 已为 null;_child 供原生 host 定位被移除的节点,
|
|
327
|
+
* 模板默认实现只需要读取父节点当前的子列表,因此不使用该参数。
|
|
328
|
+
* options 是调用方原有选项;doUpdate: false 仅抑制默认模板整表更新,
|
|
329
|
+
* 常用于移动、替换和 textContent 的批量操作。每次实际解绑仍会通知 host。
|
|
330
|
+
* cleanRef 的处理属于 Taro,host 只同步原生关系;上下文缓存失效由调用方随后完成。
|
|
331
|
+
*/
|
|
288
332
|
delegateChildRemoval(_child, options) {
|
|
289
333
|
if (this._root && options.doUpdate !== false) {
|
|
290
334
|
this.delegateChildReset();
|
|
@@ -9,8 +9,13 @@ declare class Style {
|
|
|
9
9
|
private setCssVariables;
|
|
10
10
|
get cssText(): string;
|
|
11
11
|
set cssText(str: string);
|
|
12
|
-
/**
|
|
13
|
-
|
|
12
|
+
/**
|
|
13
|
+
* 单项变更传入属性名与赋值;null/undefined/空字符串表示清除。
|
|
14
|
+
* 普通属性名为 camelCase,CSS 变量及连字符前缀保留原名。
|
|
15
|
+
* cssText 的同步批量赋值完成后不传参数调用一次,表示整体替换。
|
|
16
|
+
* this 为 Style,可读取 _element 和完整 cssText;默认准备模板 st 更新。
|
|
17
|
+
*/
|
|
18
|
+
delegateStyleUpdate(styleKey?: string, value?: string | number | null): void;
|
|
14
19
|
setProperty(propertyName: string, value?: string | null): void;
|
|
15
20
|
removeProperty(propertyName: string): string;
|
|
16
21
|
getPropertyValue(propertyName: string): any;
|
|
@@ -4,6 +4,9 @@ import { MutationObserver } from '../dom-external/mutation-observer/index.js';
|
|
|
4
4
|
import { styleProperties } from './style_properties.js';
|
|
5
5
|
|
|
6
6
|
function recordCss(obj) {
|
|
7
|
+
if (!MutationObserver.hasObservers) {
|
|
8
|
+
return;
|
|
9
|
+
}
|
|
7
10
|
MutationObserver.record({
|
|
8
11
|
type: "attributes" /* MutationRecordType.ATTRIBUTES */,
|
|
9
12
|
target: obj._element,
|
|
@@ -25,7 +28,7 @@ function setStyle(newVal, styleKey) {
|
|
|
25
28
|
this._usedStyleProp.add(styleKey);
|
|
26
29
|
this._value[styleKey] = newVal;
|
|
27
30
|
}
|
|
28
|
-
!this._pending && this.delegateStyleUpdate();
|
|
31
|
+
!this._pending && this.delegateStyleUpdate(styleKey, newVal);
|
|
29
32
|
}
|
|
30
33
|
function initStyle(ctor, styleProperties) {
|
|
31
34
|
const properties = {};
|
|
@@ -110,8 +113,17 @@ class Style {
|
|
|
110
113
|
this._pending = false;
|
|
111
114
|
this.delegateStyleUpdate();
|
|
112
115
|
}
|
|
113
|
-
/**
|
|
114
|
-
|
|
116
|
+
/**
|
|
117
|
+
* 单项样式写入在逻辑值和 mutation 记录更新后调用;相同值沿用原有提前返回。
|
|
118
|
+
* styleKey 为 setStyle 收到的属性名(普通属性为 camelCase,CSS 变量及连字符前缀保留原名),
|
|
119
|
+
* value 为该次赋值;null/undefined/空字符串表示清除。原生 host 可直接转发这一个属性。
|
|
120
|
+
* cssText 赋值使用 Taro 已有的 _pending 同步批处理,完成后不传参数调用一次,表示整体替换;
|
|
121
|
+
* 清空 cssText 也走此入口。setProperty/removeProperty/CSS 变量复用同一套样式存储。
|
|
122
|
+
* this 是 Style,this._element 是所属 Taro 元素;整体替换可读取已规范化的 this.cssText。
|
|
123
|
+
* 默认模板实现统一准备完整 st payload,因此不使用单项参数。
|
|
124
|
+
* 单项委托本身无需遍历样式;Taro 的 mutation 旧值记录及具体 SDK 的序列化成本仍独立存在。
|
|
125
|
+
*/
|
|
126
|
+
delegateStyleUpdate(_styleKey, _value) {
|
|
115
127
|
const element = this._element;
|
|
116
128
|
if (element._root) {
|
|
117
129
|
element.enqueueUpdate({
|
|
@@ -136,7 +148,8 @@ class Style {
|
|
|
136
148
|
}
|
|
137
149
|
}
|
|
138
150
|
removeProperty(propertyName) {
|
|
139
|
-
|
|
151
|
+
// Prefer stored keys while preserving dashed access to registered camelCase vendor setters.
|
|
152
|
+
propertyName = isCssVariable(propertyName) || this._usedStyleProp.has(propertyName) ? propertyName : toCamelCase(propertyName);
|
|
140
153
|
if (!this._usedStyleProp.has(propertyName)) {
|
|
141
154
|
return '';
|
|
142
155
|
}
|
|
@@ -145,7 +158,8 @@ class Style {
|
|
|
145
158
|
return value;
|
|
146
159
|
}
|
|
147
160
|
getPropertyValue(propertyName) {
|
|
148
|
-
|
|
161
|
+
// CSS variables retain their spelling; other names resolve through a stored key or camelCase.
|
|
162
|
+
propertyName = isCssVariable(propertyName) || this._usedStyleProp.has(propertyName) ? propertyName : toCamelCase(propertyName);
|
|
149
163
|
const value = this[propertyName];
|
|
150
164
|
if (!value) {
|
|
151
165
|
return '';
|
|
@@ -13,8 +13,9 @@ declare class TaroText extends TaroNode {
|
|
|
13
13
|
set data(text: string);
|
|
14
14
|
get data(): string;
|
|
15
15
|
/**
|
|
16
|
-
*
|
|
17
|
-
*
|
|
16
|
+
* characterData 记录及逻辑值写入后调用,text 为最新文本。
|
|
17
|
+
* textContent、nodeValue、data setter 共用此入口;初值由节点创建路径处理。
|
|
18
|
+
* 默认准备模板 v 更新,host 可改为原生文本更新。
|
|
18
19
|
*/
|
|
19
20
|
delegateTextUpdate(text: string): void;
|
|
20
21
|
}
|
package/dist/runtime/dom/text.js
CHANGED
|
@@ -9,11 +9,13 @@ class TaroText extends TaroNode {
|
|
|
9
9
|
this._value = value;
|
|
10
10
|
}
|
|
11
11
|
set textContent(text) {
|
|
12
|
-
MutationObserver.
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
12
|
+
if (MutationObserver.hasObservers) {
|
|
13
|
+
MutationObserver.record({
|
|
14
|
+
target: this,
|
|
15
|
+
type: "characterData" /* MutationRecordType.CHARACTER_DATA */,
|
|
16
|
+
oldValue: this._value
|
|
17
|
+
});
|
|
18
|
+
}
|
|
17
19
|
this._value = text;
|
|
18
20
|
this.delegateTextUpdate(text);
|
|
19
21
|
}
|
|
@@ -32,7 +34,12 @@ class TaroText extends TaroNode {
|
|
|
32
34
|
get data() {
|
|
33
35
|
return this._value;
|
|
34
36
|
}
|
|
35
|
-
/**
|
|
37
|
+
/**
|
|
38
|
+
* Taro 已记录 characterData mutation 并写入 _value,text 是更新后的逻辑文本。
|
|
39
|
+
* textContent、nodeValue 和 data setter 共用此入口;构造时的初值由节点创建路径处理。
|
|
40
|
+
* 默认实现准备文本 v 路径的模板 payload;原生 host 可直接更新对应原生文本节点,
|
|
41
|
+
* 保持逻辑文本值、节点身份和 mutation 记录仍由 Taro 管理。
|
|
42
|
+
*/
|
|
36
43
|
delegateTextUpdate(text) {
|
|
37
44
|
this.enqueueUpdate({
|
|
38
45
|
path: `${this._path}.${"v" /* Shortcuts.Text */}`,
|