@cloudbase/lowcode-builder 1.3.0 → 1.3.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.
@@ -4,104 +4,91 @@ import { getDeep, generateDataContext } from './util';
4
4
  import { compLowcodes } from './weapp-component';
5
5
  import EventEmitter from './event-emitter';
6
6
  import lodashSet from 'lodash.set';
7
- import { REPEATER } from './constant'
8
-
9
- function basePullAt(array, indexes) {
10
- let length = array ? indexes.length : 0;
11
- let lastIndex = length - 1;
12
-
13
- // eslint-disable-next-line no-plusplus
14
- while (length--) {
15
- let index = indexes[length];
16
- let previous;
17
- if (length == lastIndex || index !== previous) {
18
- previous = index;
19
- Array.prototype.splice.call(array, index, 1);
20
- }
21
- }
22
- return array;
23
- }
24
-
25
- function remove(array, predicate) {
26
- const result = [];
27
- if (!array?.length) {
28
- return result;
29
- }
30
- let index = -1;
31
- const indexes = [];
32
- const { length } = array;
33
-
34
- while (++index < length) {
35
- const value = array[index];
36
- if (predicate(value, index, array)) {
37
- result.push(value);
38
- indexes.push(index);
39
- }
40
- }
41
- basePullAt(array, indexes);
42
- return result;
43
- }
7
+ import { REPEATER } from './constant';
44
8
 
45
9
  /**
46
10
  * convert widget prop to data for wxml
47
11
  * @param {*} props
48
12
  */
13
+ const EXTRA_PROPS_MAP = [
14
+ /**
15
+ * create widgets
16
+ */
17
+ 'id',
18
+ 'widgetType',
19
+ 'parent',
20
+ 'children',
21
+ '_scope',
22
+ '_disposers',
23
+ '_eventListeners',
24
+ /**
25
+ * mount widget api
26
+ */
27
+ 'findWidgets',
28
+ 'getWidgetsByType',
29
+ 'getOwnerWidget',
30
+ 'getDom',
31
+ 'on',
32
+ 'off',
33
+ 'getConfig',
34
+ '_getInstanceRef',
35
+ '_methods',
36
+ '_userWidget',
37
+ /**
38
+ * 其他挂载
39
+ */
40
+ '_descendants',
41
+ '_forContext', // for root 挂载
42
+ /**
43
+ * widgetProps 附带值
44
+ */
45
+ '_id',
46
+ '_order',
47
+ 'classList',
48
+ ].reduce((map, key) => {
49
+ map[key] = true;
50
+ return map;
51
+ }, {});
52
+
49
53
  function resolveWidgetProp(props) {
50
- let { classList = [] } = props;
51
- const data = {};
52
- Object.keys(props).forEach((key) => {
53
- if (props[key] instanceof Function || props[key] === undefined) {
54
- return;
55
- }
56
- data[key] = props[key];
57
- });
58
- data.style = styleToCss(props.style);
59
- data.className = classList.join ? classList.join(' ') : classList;
60
- const extraProps = [
61
- 'classList',
62
- '_forContext',
63
- '_disposers',
64
- 'children',
65
- 'parent',
66
- '_parentId',
67
- '_ancestorId',
68
- '_descendants',
69
- 'id',
70
- '_order',
71
- '_scope',
72
- '_userWidget',
73
- 'widgetType',
74
- ];
75
- extraProps.map((prop) => {
76
- delete data[prop];
77
- });
78
- return data;
54
+ let { classList = [], ...restProps } = props;
55
+ const data = {};
56
+ Object.keys(restProps).forEach((key) => {
57
+ if (EXTRA_PROPS_MAP[key]) {
58
+ return;
59
+ }
60
+ if (restProps[key] instanceof Function || restProps[key] === undefined) {
61
+ return;
62
+ }
63
+ data[key] = restProps[key];
64
+ });
65
+ data.style = styleToCss(restProps.style);
66
+ data.className = classList.join ? classList.join(' ') : classList;
67
+ return data;
79
68
  }
80
69
 
81
70
  // widget prop -> wxml data
82
71
  export function resolveWidgetData(props) {
83
- if (!Array.isArray(props)) {
84
- return resolveWidgetProp(props);
85
- }
86
- return props.map(resolveWidgetData);
72
+ if (!Array.isArray(props)) {
73
+ return resolveWidgetProp(props);
74
+ }
75
+ return props.map(resolveWidgetData);
87
76
  }
88
77
 
89
- export function createWidgets(widgetProps, dataBinds, widgetHolder, ownerMpInst) {
90
- const rootNode = createWidgetDataTree(widgetProps, dataBinds);
91
- const failedBinds = [];
92
- const result = createSubWidgetTree(
93
- rootNode,
94
- widgetProps,
95
- dataBinds,
96
- ownerMpInst,
97
- widgetHolder,
98
- {},
99
- null,
100
- failedBinds,
101
- undefined,
102
- );
103
- retryFailedBinds(failedBinds, true);
104
- return result;
78
+ export function createWidgets(widgetProps, dataBinds, ownerMpInst) {
79
+ const rootNode = createWidgetDataTree(widgetProps, dataBinds);
80
+ const failedBinds = [];
81
+ const result = createSubWidgetTree(
82
+ { ownerForWidgetHolder: {} },
83
+ rootNode,
84
+ dataBinds,
85
+ ownerMpInst,
86
+ {},
87
+ failedBinds,
88
+ undefined,
89
+ );
90
+ retryFailedBinds(failedBinds, true);
91
+ return result;
105
92
  }
106
93
 
107
94
  /**
@@ -111,57 +98,81 @@ export function createWidgets(widgetProps, dataBinds, widgetHolder, ownerMpInst)
111
98
  * @returns {widgets: {id1:[], id2}, rootWidget: {children: [], _disposers: [], ...otherProps}}
112
99
  */
113
100
  function createSubWidgetTree(
114
- curForNode,
115
- widgetProps,
116
- dataBinds,
117
- ownerMpInst,
118
- widgetHolder = {},
119
- forContext = {},
120
- ownerForWidgetHolder = null,
121
- failedBinds = [],
122
- defaultParent = { children: observable([]), _disposers: [] },
101
+ ctx,
102
+ curForNode,
103
+ dataBinds,
104
+ ownerMpInst,
105
+ forContext = {},
106
+ failedBinds = [],
107
+ parentWidget = { children: observable([]), _disposers: [] },
123
108
  ) {
124
- const indexPostfix = (forContext.lists || [])
125
- .slice()
126
- .reverse()
127
- .map(({ currentIndex }) => ID_SEPARATOR + currentIndex)
128
- .join('');
129
-
130
- // traverse down the tree to set up all widgets
131
- dfsTree(curForNode, (node, parentNode, cache) => {
132
- const parentForWidgetArr = ownerForWidgetHolder?.[node.id] || [];
109
+ const { ownerForWidgetHolder = {}, existingWidgetMap = {} } = ctx;
110
+ /**
111
+ * 不能类似web实现记录额外的 _disposers
112
+ * const widgetHolder = { _disposers: [] }
113
+ * 因为 merger-render.initMergeRenderer 里面没有特殊处理
114
+ */
115
+ const widgetHolder = {};
116
+ const { lists: forLists = [] } = forContext;
117
+ const currentIndex = forLists[0]?.currentIndex;
118
+ const indexPostfix = (forContext.lists || [])
119
+ .slice()
120
+ .reverse()
121
+ .map(({ currentIndex }) => ID_SEPARATOR + currentIndex)
122
+ .join('');
123
+
124
+ // traverse down the tree to set up all widgets
125
+ dfsTree(
126
+ curForNode,
127
+ (node, parentNode, cache) => {
128
+ const parentForWidgetArr = ownerForWidgetHolder[node.id] || [];
133
129
  const { _waForKey } = node.value;
134
130
  const key = forContext.forItems?.[node.id]?.[_waForKey];
135
- const index = cache[parentNode?.id]
136
- ? cache[parentNode.id].index
137
- : parentForWidgetArr.findIndex((widget) => key && widget._key === key);
138
- const existedWidget = index !== -1 ? parentForWidgetArr[index] : null; // try to reuse previous node when rerun for
139
-
140
- if (existedWidget) {
141
- cache[node.id] = {
142
- index,
143
- };
131
+ let forExistingWidgetMap = {};
132
+ let forExsitWidget;
133
+
134
+ /**
135
+ * for 起始节点,根据 existingWidgetMap 判断复用
136
+ */
137
+ if (node.id === curForNode.id) {
138
+ if (existingWidgetMap[key] && existingWidgetMap[key].index === currentIndex) {
139
+ forExistingWidgetMap = existingWidgetMap[key].widgets || {};
140
+ forExsitWidget = forExistingWidgetMap[node.id];
141
+ if (forExsitWidget) {
142
+ cache[node.id] = {
143
+ widgets: forExistingWidgetMap,
144
+ };
145
+ }
146
+ }
147
+ } else if (cache[parentNode?.id]) {
148
+ forExsitWidget = cache[parentNode?.id].widgets[node.id] || null;
149
+ if (forExsitWidget) {
150
+ cache[node.id] = cache[parentNode?.id];
151
+ }
144
152
  }
153
+ const existedWidget = forExsitWidget || null;
145
154
 
146
155
  if (node.forCount === curForNode.forCount) {
147
- // Leaf node
156
+ /**
157
+ * 同一层循环作用域内,当前节点与 curForNode(循环根节点)在同一级循环作用域中
158
+ * 即没有再开辟新级别的 for 循环
159
+ * Leaf node
160
+ */
148
161
  let w = existedWidget;
149
162
  if (!existedWidget) {
150
163
  const parentNode = node.parent;
151
- let parentWidget = null;
152
- if (parentNode) {
153
- parentWidget = widgetHolder[parentNode.id] || ownerForWidgetHolder[parentNode.id];
154
- }
155
- w = createWidget(widgetProps[node.id], node.id, indexPostfix, parentWidget, ownerMpInst, forContext.forItems?.[node.id]);
164
+ let parent = parentNode ? widgetHolder[parentNode.id] || ownerForWidgetHolder[parentNode.id] : null;
165
+ w = createWidget(node.value, node.id, indexPostfix, parent, ownerMpInst, forContext.forItems?.[node.id]);
156
166
  w._key = key;
157
167
 
158
- if (!parentWidget) {
159
- defaultParent.children.push(w);
168
+ if (!parent) {
169
+ parentWidget.children.push(w);
160
170
  }
161
- parentForWidgetArr.push(w);
162
171
  } else {
172
+ w.id = `${node.id}${indexPostfix}`;
163
173
  disposeWidget(existedWidget, true);
164
174
  }
175
+ parentForWidgetArr.push?.(w);
165
176
  setUpWidgetDataBinds(w, dataBinds[node.id], forContext, failedBinds, ownerMpInst._getInstance());
166
177
  widgetHolder[node.id] = w;
167
178
  if (widgetHolder?.[node._ancestorId]) {
@@ -170,37 +181,47 @@ function createSubWidgetTree(
170
181
  widgetHolder[node._ancestorId]._descendants = widgetHolder[node._ancestorId]._descendants || {};
171
182
  widgetHolder[node._ancestorId]._descendants[node.id] = widgetHolder[node.id];
172
183
  }
173
- } else if (!existedWidget) {
174
- const len = parentForWidgetArr.push(observable([]));
175
- widgetHolder[node.id] = parentForWidgetArr[len - 1];
176
184
  } else {
177
- // Reuse existed for widget array
178
- widgetHolder[node.id] = existedWidget;
185
+ if (!existedWidget) {
186
+ widgetHolder[node.id] = observable([]);
187
+ } else {
188
+ // Reuse existed for widget array
189
+ widgetHolder[node.id] = existedWidget;
190
+ }
191
+ if (parentForWidgetArr) {
192
+ parentForWidgetArr.push(widgetHolder[node.id]);
193
+ }
179
194
  }
180
- });
195
+ },
196
+ undefined,
197
+ );
181
198
 
182
- // run for of next level
183
- dfsTree(curForNode, (node) => {
199
+ // run for of next level
200
+ dfsTree(
201
+ curForNode,
202
+ (node) => {
184
203
  if (node.forCount === curForNode.forCount + 1 && dataBinds[node.id] && dataBinds[node.id]._waFor) {
185
204
  // find the node bound with next level for
186
- const parent = node.parent ? widgetHolder[node.parent.id] : defaultParent;
187
- const dispose = runFor(node, widgetProps, dataBinds, ownerMpInst, forContext, widgetHolder, failedBinds, parent);
205
+ const parent = getNodeParentWidget(node, widgetHolder, parentWidget);
206
+ const dispose = runFor(node, dataBinds, ownerMpInst, forContext, widgetHolder, failedBinds, parent);
188
207
  parent._disposers.push(dispose); // Add the for bind dispose to the parent node of forNode
189
208
  }
190
- });
209
+ },
210
+ undefined,
211
+ );
191
212
 
192
- retryFailedBinds(failedBinds);
213
+ retryFailedBinds(failedBinds);
193
214
 
194
- return { widgets: widgetHolder, rootWidget: widgetHolder[curForNode.id] || defaultParent };
215
+ return { widgets: widgetHolder, rootWidget: widgetHolder[curForNode.id] || parentWidget };
195
216
  }
196
217
 
197
218
  // Retry failed databinds
198
219
  function retryFailedBinds(failedBinds, finalTry) {
199
- const len = failedBinds.length;
200
- for (let i = 0; i < len; i++) {
201
- const setUpDataBind = failedBinds.shift();
202
- setUpDataBind(finalTry);
203
- }
220
+ const len = failedBinds.length;
221
+ for (let i = 0; i < len; i++) {
222
+ const setUpDataBind = failedBinds.shift();
223
+ setUpDataBind(finalTry);
224
+ }
204
225
  }
205
226
 
206
227
  /**
@@ -212,540 +233,579 @@ function retryFailedBinds(failedBinds, finalTry) {
212
233
  * @returns top level widgets or for dispose
213
234
  */
214
235
  const _FOR_ERROR_CACHE_MAP = {};
215
- function runFor(
216
- curForNode,
217
- widgetProps,
218
- dataBinds,
219
- ownerMpInst,
220
- forContext,
221
- ownerForWidgetHolder,
222
- failedBinds,
223
- defaultParent,
224
- ) {
225
- const nodeId = curForNode.id;
226
- const { _waForKey } = curForNode.value;
227
-
228
- const dispose = autorun(() => {
229
- let forList = [];
230
- const currentForParentWidget = ownerForWidgetHolder[curForNode.parent?.id];
231
- try {
232
- clearTimeout(_FOR_ERROR_CACHE_MAP[nodeId]);
233
-
234
- const $instance = ownerMpInst._getInstance();
235
- const dataContext = untracked(() => generateDataContext(defaultParent));
236
- const $w = untracked(() => generateWidgetAPIContext($instance?.__internal__?.$w, currentForParentWidget, forContext));
237
-
238
- forList = dataBinds[nodeId]._waFor.call(
239
- $instance,
240
- $instance,
241
- forContext.lists,
242
- forContext.forItems,
243
- undefined,
244
- dataContext,
245
- $w,
246
- );
247
- if (!Array.isArray(forList)) {
248
- forList = [];
249
- }
250
- } catch (e) {
236
+ function runFor(curForNode, dataBinds, ownerMpInst, forContext, ownerForWidgetHolder, failedBinds, parentWidget) {
237
+ const nodeId = curForNode.id;
238
+ const { _waForKey } = curForNode.value;
239
+
240
+ const dispose = autorun(() => {
241
+ let forList = [];
242
+ try {
243
+ clearTimeout(_FOR_ERROR_CACHE_MAP[nodeId]);
244
+
245
+ const $instance = ownerMpInst._getInstance();
246
+ const dataContext = untracked(() => generateDataContext(parentWidget));
247
+ const $w = untracked(() => generateWidgetAPIContext($instance?.__internal__?.$w, parentWidget, forContext));
248
+
249
+ forList = dataBinds[nodeId]._waFor.call(
250
+ $instance,
251
+ $instance,
252
+ forContext.lists,
253
+ forContext.forItems,
254
+ undefined,
255
+ dataContext,
256
+ $w,
257
+ );
258
+ if (!Array.isArray(forList)) {
251
259
  forList = [];
252
- _FOR_ERROR_CACHE_MAP[nodeId] = setTimeout(() => {
253
- console.warn('For binding error', nodeId, e);
254
- }, 1000);
255
260
  }
261
+ } catch (e) {
262
+ forList = [];
263
+ _FOR_ERROR_CACHE_MAP[nodeId] = setTimeout(() => {
264
+ console.warn('For binding error', nodeId, e);
265
+ }, 1000);
266
+ }
256
267
 
257
- // Track list change (e.g. push)
258
- forList.forEach((e) => {});
268
+ // Track list change (e.g. push)
269
+ forList.forEach((e) => {});
259
270
 
260
- untracked(() => {
261
- // dispose widgets before reused instead
262
- // disposeWidgets(parentForWidgets[curForNode.id])
263
- const exsitMap = forList.reduce((map, item) => {
264
- if (item[_waForKey]) {
265
- map[item[_waForKey]] = true;
266
- }
267
- return map;
268
- }, {});
269
- const forWidgets = ownerForWidgetHolder[nodeId];
270
- const existedWidgetIndex = [];
271
- const extraWidgetsIndex = [];
272
- const extraWidgets = [];
273
- forWidgets.forEach((widget, index) => {
274
- if (exsitMap[widget._key]) {
275
- existedWidgetIndex.push(index);
276
- // need to use uqique key
277
- exsitMap[widget._key] = undefined;
278
- } else {
279
- extraWidgetsIndex.push(index);
280
- }
281
- });
282
- const extraWidgetsIndexMap = extraWidgetsIndex.reduce((map, item) => {
283
- map[item] = true;
284
- extraWidgets.push(forWidgets[item]);
285
- return map;
286
- }, {});
287
-
288
- // clean extra widgets of previous for run
289
- dfsTree(curForNode, (node) => {
290
- const arr = ownerForWidgetHolder[node.id];
291
- remove(arr, (_, index) => {
292
- return extraWidgetsIndexMap[index];
293
- });
294
- });
295
-
296
- // 清理根 for 的 autorun, 并递归清理子节点
297
- extraWidgets.map((w) => {
298
- disposeWidget(w);
299
- const { children } = w.parent || defaultParent;
300
- children.remove(w);
301
- // w.parent = null
302
- });
271
+ untracked(() => {
272
+ // dispose widgets before reused instead
273
+ // disposeWidgets(parentForWidgets[curForNode.id])
274
+ const exsitMap = forList.reduce((map, item, index) => {
275
+ const cache = item?.[_waForKey];
276
+ if (map[cache] === undefined) {
277
+ map[cache] = index;
278
+ }
279
+ return map;
280
+ }, {});
281
+ const forWidgets = ownerForWidgetHolder[nodeId];
282
+ const existingWidgetMap = {};
283
+ const existingWidgetIndexMap = {};
284
+ const extraWidgetsIndexMap = {};
285
+ forWidgets.forEach((widget, index) => {
286
+ if (exsitMap[widget._key] !== undefined) {
287
+ const nodeId = widget.id?.split(ID_SEPARATOR)[0];
288
+ existingWidgetMap[widget._key] = { index: exsitMap[widget._key], widgets: { [nodeId]: widget } };
289
+ /**
290
+ * 此处依赖了 existingWidgetMap[widget._key].widgets 的引用,
291
+ * 为了直接可以通过 index 访问到 existingWidgetMap 里的值进行编辑
292
+ * 但是依赖引用关系,存在维护风险
293
+ */
294
+ existingWidgetIndexMap[index] = existingWidgetMap[widget._key].widgets;
295
+ // need to use uqique key
296
+ exsitMap[widget._key] = undefined;
297
+ } else {
298
+ extraWidgetsIndexMap[index] = widget;
299
+ }
300
+ });
303
301
 
304
- const isInRepeaterChild =
305
- currentForParentWidget?.widgetType === `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_NAME}`;
306
- forList.forEach((item, index) => {
307
- let forContextListAlias;
308
- let { lists = [], forItems = {} } = forContext;
309
- const listMeta = { currentItem: item, currentIndex: index };
310
- if (isInRepeaterChild) {
311
- forContextListAlias = {
312
- [`${currentForParentWidget.forIndex}` || 'currentIndex']: listMeta.currentIndex,
313
- [`${currentForParentWidget.forItem}` || 'currentItem']: listMeta.currentItem,
314
- };
302
+ // clean extra widgets of previous for run
303
+ dfsTree(
304
+ curForNode,
305
+ (node) => {
306
+ const arr = ownerForWidgetHolder[node.id] || [];
307
+ /**
308
+ * clone 上次 for 已有的 widgets
309
+ */
310
+ if (node.id !== curForNode.id) {
311
+ arr.forEach((item, index) => {
312
+ if (existingWidgetIndexMap[index]) {
313
+ existingWidgetIndexMap[index][node.id] = item;
314
+ }
315
+ });
315
316
  }
316
- const _forContext = {
317
- lists: [{ ...listMeta, alias: forContextListAlias }, ...lists],
318
- forItems: { ...forItems, [nodeId]: item },
317
+ /**
318
+ * 重头开始生成
319
+ * 清空原有 arr 并保持引用不变
320
+ */
321
+ arr.splice(0, arr.length);
322
+ },
323
+ undefined,
324
+ );
325
+
326
+ /**
327
+ * 明确已经不会复用的节点,清除 mobx observer
328
+ * 并递归清理子节点
329
+ */
330
+ for (const index in extraWidgetsIndexMap) {
331
+ const w = extraWidgetsIndexMap[index];
332
+ disposeWidget(w);
333
+ const { children } = w.parent || parentWidget;
334
+ children.remove(w);
335
+ // w.parent = null
336
+ }
337
+
338
+ const isInRepeaterChild = parentWidget?.widgetType === `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_NAME}`;
339
+ forList.forEach((item, index) => {
340
+ let forContextListAlias;
341
+ let { lists = [], forItems = {} } = forContext;
342
+ const listMeta = { currentItem: item, currentIndex: index };
343
+ if (isInRepeaterChild) {
344
+ forContextListAlias = {
345
+ [`${parentWidget.forIndex}` || 'currentIndex']: listMeta.currentIndex,
346
+ [`${parentWidget.forItem}` || 'currentItem']: listMeta.currentItem,
319
347
  };
320
- const { rootWidget } = createSubWidgetTree(
321
- curForNode,
322
- widgetProps,
323
- dataBinds,
324
- ownerMpInst,
325
- {},
326
- _forContext,
327
- ownerForWidgetHolder,
328
- failedBinds,
329
- defaultParent,
330
- );
331
- rootWidget._forContext = _forContext;
332
- });
348
+ }
349
+ const _forContext = {
350
+ lists: [{ ...listMeta, alias: forContextListAlias }, ...lists],
351
+ forItems: { ...forItems, [nodeId]: item },
352
+ };
353
+ const { rootWidget } = createSubWidgetTree(
354
+ { ownerForWidgetHolder, existingWidgetMap },
355
+ curForNode,
356
+ dataBinds,
357
+ ownerMpInst,
358
+ _forContext,
359
+ failedBinds,
360
+ parentWidget,
361
+ );
362
+ rootWidget._forContext = _forContext;
333
363
  });
334
364
  });
365
+ });
335
366
 
336
- return dispose;
367
+ return dispose;
337
368
  }
338
369
 
339
370
  function createWidget(props, nodeId, indexPostfix, parent, ownerMpInst, forContext) {
340
- const { widgetType, _parentId, ...restProps } = props;
341
- const w = observable(restProps);
342
- const id = `${nodeId}${indexPostfix}`;
343
-
344
- // Builtin props
345
- Object.defineProperty(w, 'id', { value: id });
346
- Object.defineProperty(w, 'widgetType', { value: widgetType });
347
- Object.defineProperty(w, '_scope', {
348
- value: observable({
349
- id: nodeId,
350
- dataContext: {},
351
- forContext
352
- }),
353
- });
354
-
355
- // w._disposers = []
356
- // w.children = []
357
- Object.defineProperty(w, 'children', { value: observable([]) });
358
- Object.defineProperty(w, '_disposers', { value: observable([]) });
359
- Object.defineProperty(w, '_eventListeners', { value: new EventEmitter() });
360
- if (parent) {
361
- // w.parent = parent
362
- Object.defineProperty(w, 'parent', { value: parent });
363
- parent.children.push(w);
364
- }
371
+ const { widgetType, _parentId, ...restProps } = props;
372
+ const w = observable(restProps);
373
+ const id = `${nodeId}${indexPostfix}`;
374
+
375
+ // Builtin props
376
+ w.id = id; // 重用之后要修改id
377
+ // Object.defineProperty(w, 'id', { value: id });
378
+ Object.defineProperty(w, 'widgetType', { value: widgetType });
379
+ Object.defineProperty(w, '_scope', {
380
+ value: observable({
381
+ id: nodeId,
382
+ dataContext: {},
383
+ /**
384
+ * 当前节点的 for currentItem 对象
385
+ */
386
+ forContext,
387
+ }),
388
+ });
389
+
390
+ // w._disposers = []
391
+ // w.children = []
392
+ Object.defineProperty(w, 'children', { value: observable([]) });
393
+ Object.defineProperty(w, '_disposers', { value: observable([]) });
394
+ Object.defineProperty(w, '_eventListeners', { value: new EventEmitter() });
395
+ if (parent) {
396
+ // w.parent = parent
397
+ Object.defineProperty(w, 'parent', { value: parent });
398
+ parent.children.push(w);
399
+ }
365
400
 
366
- switch (widgetType) {
367
- case `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_NAME}`: {
368
- if (!w.items) {
369
- Object.defineProperty(w, 'items', {
370
- get() {
371
- return (w.children || []).map((item) => {
372
- const descendants = {};
373
- Object.keys(item?._descendants || {}).forEach((key) => {
374
- descendants[key] = item._descendants[key]._userWidget;
375
- });
376
- return descendants;
401
+ switch (widgetType) {
402
+ case `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_NAME}`: {
403
+ if (!w.items) {
404
+ Object.defineProperty(w, 'items', {
405
+ get() {
406
+ return (w.children || []).map((item) => {
407
+ const descendants = {};
408
+ Object.keys(item?._descendants || {}).forEach((key) => {
409
+ descendants[key] = item._descendants[key]._userWidget;
377
410
  });
378
- },
379
- });
380
- }
381
- break;
411
+ return descendants;
412
+ });
413
+ },
414
+ });
382
415
  }
416
+ break;
383
417
  }
418
+ }
384
419
 
385
- mountBuiltinWigetsAPI(w, ownerMpInst);
386
- return w;
420
+ mountBuiltinWigetsAPI(w, ownerMpInst);
421
+ return w;
387
422
  }
388
423
 
389
424
  function setUpWidgetDataBinds(w, dataBinds, forContext, failedBinds, ctx) {
390
- Object.keys(dataBinds || {})
391
- .sort((a, b) => {
392
- return a.length - b.length > 0 ? 1 : -1;
393
- })
394
- .map((prop) => {
395
- if (prop === '_waFor') {
396
- return;
397
- }
398
- let timer = null;
399
- const setUpDataBind = (isFinalTry) => {
400
- let ran = false;
401
- const dispose = autorun((reaction) => {
402
- try {
403
- clearTimeout(timer);
404
-
405
- const dataContext = untracked(() => generateDataContext(w));
406
- const $w = untracked(() => generateWidgetAPIContext(ctx?.__internal__?.$w, w, forContext));
407
-
408
- // Computed data bind in the next tick since data bind may read widgets data
409
- const value = dataBinds[prop].call(
410
- ctx,
411
- ctx,
412
- forContext.lists,
413
- forContext.forItems,
414
- undefined,
415
- dataContext,
416
- $w,
417
- );
418
- const paths = prop.split('.').filter((key) => !!key);
419
- if (paths.length > 1) {
420
- // 一定要 untracked 不然爆栈了
421
- untracked(() => lodashSet(w, prop, value));
422
- } else {
423
- // 普通 key 直接赋值
424
- w[prop] = value;
425
- }
426
- } catch (e) {
427
- if (prop === '_waIf') {
428
- w[prop] = false;
429
- }
425
+ Object.keys(dataBinds || {})
426
+ .sort((a, b) => {
427
+ return a.length - b.length > 0 ? 1 : -1;
428
+ })
429
+ .map((prop) => {
430
+ if (prop === '_waFor') {
431
+ return;
432
+ }
433
+ let timer = null;
434
+ const setUpDataBind = (isFinalTry) => {
435
+ let ran = false;
436
+ const dispose = autorun((reaction) => {
437
+ try {
438
+ clearTimeout(timer);
439
+
440
+ const dataContext = untracked(() => generateDataContext(w));
441
+ const $w = untracked(() => generateWidgetAPIContext(ctx?.__internal__?.$w, w, forContext));
442
+
443
+ // Computed data bind in the next tick since data bind may read widgets data
444
+ const value = dataBinds[prop].call(
445
+ ctx,
446
+ ctx,
447
+ forContext.lists,
448
+ forContext.forItems,
449
+ undefined,
450
+ dataContext,
451
+ $w,
452
+ );
453
+ const paths = prop.split('.').filter((key) => !!key);
454
+ if (paths.length > 1) {
455
+ // 一定要 untracked 不然爆栈了
456
+ untracked(() => lodashSet(w, prop, value));
457
+ } else {
458
+ // 普通 key 直接赋值
459
+ w[prop] = value;
460
+ }
461
+ } catch (e) {
462
+ if (prop === '_waIf') {
463
+ w[prop] = false;
464
+ }
430
465
 
431
- if (isFinalTry || ran) {
432
- timer = setTimeout(() => {
433
- console.warn(`Error computing data bind ${w.id}.${prop}`, e);
434
- }, 1000);
435
- } else {
466
+ if (isFinalTry || ran) {
467
+ timer = setTimeout(() => {
468
+ console.warn(`Error computing data bind ${w.id}.${prop}`, e);
469
+ }, 1000);
470
+ } else {
471
+ failedBinds.push((...args) => {
436
472
  reaction.dispose();
437
- failedBinds.push(setUpDataBind);
438
- }
439
-
440
- ran = true;
473
+ return setUpDataBind(...args);
474
+ });
441
475
  }
442
- });
443
- w._disposers.push(dispose);
444
- };
445
- setUpDataBind();
446
- });
476
+
477
+ ran = true;
478
+ }
479
+ });
480
+ w._disposers.push(dispose);
481
+ };
482
+ setUpDataBind();
483
+ });
447
484
  }
448
485
 
449
486
  export function generateForContextOfWidget(widget) {
450
- const forContext = widget._forContext;
451
- if (forContext) return forContext;
452
- if (widget.parent) return generateForContextOfWidget(widget.parent);
487
+ const forContext = widget._forContext;
488
+ if (forContext) return forContext;
489
+ if (widget.parent) return generateForContextOfWidget(widget.parent);
453
490
  }
454
491
 
455
492
  export const ID_SEPARATOR = '-';
456
493
  export function getWidget(widgets, id) {
457
- return getDeep(widgets, id, ID_SEPARATOR);
494
+ return getDeep(widgets, id, ID_SEPARATOR);
495
+ }
496
+
497
+ function getNodeParentWidget(node, widgets, defaultParent = { children: observable([]), _disposers: [] }) {
498
+ return (node?.parent?.id && widgets?.[node.parent.id]) || defaultParent;
458
499
  }
459
500
 
460
501
  /**
461
502
  * Add parent, children to widget
462
503
  */
463
504
  function createWidgetDataTree(widgets, dataBinds) {
464
- const virtualRoot = { children: [], forCount: 0 };
465
- const nodes = Object.keys(widgets).reduce((result, id) => {
466
- const w = widgets[id];
467
- result[id] = { id, value: w, _order: w._order, children: [], parent: null, forCount: 0 };
468
- return result;
469
- }, {});
470
-
471
- // Create widgets tree API
472
- Object.keys(nodes).map((id) => {
473
- const curNode = nodes[id];
474
- const parent = nodes[widgets[id]._parentId];
475
- // delete widgets[id]._parentId
476
- if (!parent) {
477
- virtualRoot.children.push(curNode);
478
- return;
505
+ const virtualRoot = { children: [], forCount: 0 };
506
+ const nodes = Object.keys(widgets).reduce((result, id) => {
507
+ const w = widgets[id];
508
+ result[id] = { id, value: w, _order: w._order, children: [], parent: null, forCount: 0 };
509
+ return result;
510
+ }, {});
511
+
512
+ // Create widgets tree API
513
+ Object.keys(nodes).map((id) => {
514
+ const curNode = nodes[id];
515
+ const parent = nodes[widgets[id]._parentId];
516
+ // delete widgets[id]._parentId
517
+ if (!parent) {
518
+ virtualRoot.children.push(curNode);
519
+ return;
520
+ }
521
+ curNode.parent = parent;
522
+ parent.children.push(curNode);
523
+ });
524
+
525
+ // Sort children
526
+ Object.keys(nodes).map((id) => {
527
+ nodes[id].children.sort((a, b) => a._order - b._order);
528
+ });
529
+
530
+ virtualRoot.children.map(addForCount);
531
+
532
+ // dfs, add forCount
533
+ function addForCount(node) {
534
+ if (node.parent) {
535
+ node.forCount = node.parent.forCount;
536
+ const { widgetType } = node.parent.value;
537
+ if (widgetType === `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_ITEM_NAME}`) {
538
+ node._ancestorId = node.parent.id;
479
539
  }
480
- curNode.parent = parent;
481
- parent.children.push(curNode);
482
- });
483
-
484
- // Sort children
485
- Object.keys(nodes).map((id) => {
486
- nodes[id].children.sort((a, b) => a._order - b._order);
487
- });
488
-
489
- virtualRoot.children.map(addForCount);
490
-
491
- // dfs, add forCount
492
- function addForCount(node) {
493
- if (node.parent) {
494
- node.forCount = node.parent.forCount;
495
- const { widgetType } = node.parent.value;
496
- if (widgetType === `${REPEATER.MODULE_NAME}:${REPEATER.REPEATER_ITEM_NAME}`) {
497
- node._ancestorId = node.parent.id;
498
- }
499
- if (node.parent?._ancestorId) {
500
- // Repeater 作用域内的所有子孙(排除里面的 Repeater 组件,它本身又产生深一层的作用域)继承父级的循环信息
501
- if (!node._ancestorId) {
502
- node._ancestorId = node.parent._ancestorId;
503
- }
540
+ if (node.parent?._ancestorId) {
541
+ // Repeater 作用域内的所有子孙(排除里面的 Repeater 组件,它本身又产生深一层的作用域)继承父级的循环信息
542
+ if (!node._ancestorId) {
543
+ node._ancestorId = node.parent._ancestorId;
504
544
  }
505
545
  }
506
- if (dataBinds[node.id]?._waFor) {
507
- node.forCount += 1;
508
- }
509
- node.children.map(addForCount);
510
546
  }
547
+ if (dataBinds[node.id]?._waFor) {
548
+ node.forCount += 1;
549
+ }
550
+ node.children.map(addForCount);
551
+ }
511
552
 
512
- return virtualRoot;
553
+ return virtualRoot;
513
554
  }
514
555
 
515
556
  function dfsTree(node, fn, parent, cache = {}) {
516
- node.value && fn(node, parent, cache);
517
- node.children.map((e) => dfsTree(e, fn, node.value ? node : null, cache));
557
+ node.value && fn(node, parent, cache);
558
+ node.children.map((e) => dfsTree(e, fn, node.value ? node : null, cache));
518
559
  }
519
560
 
520
561
  // dispose autorun, widget can be the virtual root widget
521
- export function disposeWidget(widget, noRecursive) {
522
- const disposers = widget._disposers;
523
- disposers.map((dispose) => dispose());
524
- disposers.splice(0, disposers.length);
525
- !noRecursive && widget.children.forEach((w) => disposeWidget(w));
562
+ export function disposeWidget(widget, noRecursive = false) {
563
+ const disposers = widget._disposers;
564
+ disposers.map((dispose) => dispose());
565
+ disposers.splice(0, disposers.length);
566
+ !noRecursive && widget.children.forEach((w) => disposeWidget(w));
526
567
  }
527
568
 
528
569
  export function createInitData(widgets, dataBinds, keyPrefix = '') {
529
- return Object.keys(widgets).reduce((result, id) => {
530
- if (!isWidgetInFor(id, widgets, dataBinds)) {
531
- result[keyPrefix + id] = resolveWidgetData(widgets[id]);
532
- } else {
533
- result[keyPrefix + id] = [];
534
- }
535
- return result;
536
- }, {});
570
+ return Object.keys(widgets).reduce((result, id) => {
571
+ if (!isWidgetInFor(id, widgets, dataBinds)) {
572
+ result[keyPrefix + id] = resolveWidgetData(widgets[id]);
573
+ } else {
574
+ result[keyPrefix + id] = [];
575
+ }
576
+ return result;
577
+ }, {});
537
578
  }
538
579
 
539
580
  function isWidgetInFor(id, widgets, dataBinds) {
540
- let curNode = widgets[id];
541
- let nodeId = id;
542
- while (curNode) {
543
- if (dataBinds[nodeId]?._waFor) {
544
- return true;
545
- }
546
- nodeId = curNode._parentId;
547
- curNode = widgets[nodeId];
581
+ let curNode = widgets[id];
582
+ let nodeId = id;
583
+ while (curNode) {
584
+ if (dataBinds[nodeId]?._waFor) {
585
+ return true;
548
586
  }
587
+ nodeId = curNode._parentId;
588
+ curNode = widgets[nodeId];
589
+ }
549
590
  }
550
591
 
551
592
  function mountBuiltinWigetsAPI(widget, owner) {
552
- // #1 builtin APIs
553
- widget.findWidgets = function (filter, includeInvisibleDescendants) {
554
- let { children = [] } = this;
555
- if (!includeInvisibleDescendants) {
556
- // include visible widgets only by default
557
- children = children.filter((e) => e._waIf !== false);
593
+ // #1 builtin APIs
594
+ widget.findWidgets = function (filter, includeInvisibleDescendants) {
595
+ let { children = [] } = this;
596
+ if (!includeInvisibleDescendants) {
597
+ // include visible widgets only by default
598
+ children = children.filter((e) => e._waIf !== false);
599
+ }
600
+ const matched = [];
601
+ children.forEach((w) => {
602
+ if (filter(w)) {
603
+ matched.push(w);
558
604
  }
559
- const matched = [];
560
- children.forEach((w) => {
561
- if (filter(w)) {
562
- matched.push(w);
563
- }
564
- matched.push(...w.findWidgets(filter, includeInvisibleDescendants));
565
- });
566
- return matched;
567
- };
568
-
569
- widget.getWidgetsByType = function (type, includeInvisibleDescendants) {
570
- return this.findWidgets((w) => w.widgetType === type, includeInvisibleDescendants);
571
- };
605
+ matched.push(...w.findWidgets(filter, includeInvisibleDescendants));
606
+ });
607
+ return matched;
608
+ };
572
609
 
573
- /**
574
- * Similar to selectOwnerComponent of WX MP: https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html
575
- */
576
- widget.getOwnerWidget = function () {
577
- return owner?._getInstance?.()?.node;
578
- };
610
+ widget.getWidgetsByType = function (type, includeInvisibleDescendants) {
611
+ return this.findWidgets((w) => w.widgetType === type, includeInvisibleDescendants);
612
+ };
579
613
 
580
- // Will be overwritten by composited component
581
- widget.getDom = function (fields) {
582
- return new Promise((resolve, reject) => {
583
- const query = (owner || wx).createSelectorQuery();
584
- query
585
- .select(`#${this.id}`)
586
- .fields(fields, (res) => {
587
- resolve(res);
588
- })
589
- .exec();
590
- });
591
- };
614
+ /**
615
+ * Similar to selectOwnerComponent of WX MP: https://developers.weixin.qq.com/miniprogram/dev/reference/api/Component.html
616
+ */
617
+ widget.getOwnerWidget = function () {
618
+ return owner?._getInstance?.()?.node;
619
+ };
620
+
621
+ // Will be overwritten by composited component
622
+ widget.getDom = function (fields) {
623
+ return new Promise((resolve, reject) => {
624
+ const query = (owner || wx).createSelectorQuery();
625
+ query
626
+ .select(`#${this.id}`)
627
+ .fields(fields, (res) => {
628
+ resolve(res);
629
+ })
630
+ .exec();
631
+ });
632
+ };
592
633
 
593
- widget.on = function (type, listener) {
594
- this._eventListeners.on(type, listener);
595
- };
634
+ widget.on = function (type, listener) {
635
+ this._eventListeners.on(type, listener);
636
+ };
596
637
 
597
- widget.off = function (type, listener) {
598
- this._eventListeners.off(type, listener);
599
- };
638
+ widget.off = function (type, listener) {
639
+ this._eventListeners.off(type, listener);
640
+ };
600
641
 
601
- widget.getConfig = () => ({});
642
+ widget.getConfig = () => ({});
602
643
 
603
- const lowcode = compLowcodes[widget.widgetType];
604
- if (lowcode) {
605
- const { config } = lowcode;
606
- widget.getConfig = () => config;
607
- }
644
+ const lowcode = compLowcodes[widget.widgetType];
645
+ if (lowcode) {
646
+ const { config } = lowcode;
647
+ widget.getConfig = () => config;
648
+ }
608
649
 
609
- widget._getInstanceRef = () => null;
650
+ widget._getInstanceRef = () => null;
610
651
 
611
- /**
612
- * @deprecated
613
- */
614
- widget._methods = {};
652
+ /**
653
+ * @deprecated
654
+ */
655
+ widget._methods = {};
615
656
 
616
- widget._userWidget = untracked(() => {
657
+ Object.defineProperty(widget, '_userWidget', {
658
+ value: untracked(() => {
617
659
  return new UserWidget(widget);
618
- });
660
+ }),
661
+ });
619
662
  }
620
663
 
621
664
  /**
622
665
  * 对外(用户)的 Widget
623
666
  */
624
667
  class UserWidget {
625
- _widget = null;
626
-
627
- get description() {
628
- const { id } = this._widget;
629
- return `
630
- 使用说明:
631
- 1. w 命名空间下为内置方法,可通过 $w.${id}.w.<成员> 或 $w.${id}.<成员> 访问。
632
- 访问 id 示例: $w.${id}.id 或 $w.${id}.w.id
633
- 2. custom 命名空间下为用户自定义对外暴露的成员,可通过 $w.${id}.custom.<成员> 或 $w.${id}.<成员> 访问。
634
- 访问自定义方法 hello 示例: $w.${id}.hello() 或 $w.${id}.custom.hello()
635
- 3. 通过 $w.${id}.<成员> 访问时,如果自定义成员与内置成员重名,则自定义成员覆盖内置成员。
636
- 4. [注意]: 请不要直接访问 _widget,它里面的成员为内部实现,随时可能进行调整
637
- `;
638
- }
668
+ _widget = null;
669
+
670
+ get description() {
671
+ const { id } = this._widget;
672
+ return `
673
+ 使用说明:
674
+ 1. w 命名空间下为内置方法,可通过 $w.${id}.w.<成员> 或 $w.${id}.<成员> 访问。
675
+ 访问 id 示例: $w.${id}.id 或 $w.${id}.w.id
676
+ 2. custom 命名空间下为用户自定义对外暴露的成员,可通过 $w.${id}.custom.<成员> 或 $w.${id}.<成员> 访问。
677
+ 访问自定义方法 hello 示例: $w.${id}.hello() 或 $w.${id}.custom.hello()
678
+ 3. 通过 $w.${id}.<成员> 访问时,如果自定义成员与内置成员重名,则自定义成员覆盖内置成员。
679
+ 4. [注意]: 请不要直接访问 _widget,它里面的成员为内部实现,随时可能进行调整
680
+ `;
681
+ }
639
682
 
640
- constructor(widget) {
641
- this._widget = widget;
642
- return new Proxy(this, {
643
- get(target, prop) {
644
- if (prop in target) {
645
- return target[prop];
646
- }
683
+ constructor(widget) {
684
+ this._widget = widget;
685
+ return new Proxy(this, {
686
+ get(target, prop) {
687
+ if (prop in target) {
688
+ return target[prop];
689
+ }
647
690
 
648
- // 优先 custom
649
- if (target.custom[prop]) {
650
- return target.custom[prop];
651
- }
691
+ // 优先 custom
692
+ if (target.custom[prop]) {
693
+ return target.custom[prop];
694
+ }
652
695
 
653
- // 再尝试内置的方法和属性
654
- const buildinMember = target.sys[prop];
655
- if (buildinMember) return buildinMember;
696
+ // 再尝试内置的方法和属性
697
+ const buildinMember = target.sys[prop];
698
+ if (buildinMember) return buildinMember;
656
699
 
657
- // 兼容原来的 widget。最后直接代理到 widget 上,主要是访问组件的属性
658
- return target._widget[prop];
659
- },
660
- });
661
- }
700
+ // 兼容原来的 widget。最后直接代理到 widget 上,主要是访问组件的属性
701
+ return target._widget[prop];
702
+ },
703
+ });
704
+ }
662
705
 
663
- // 内置属性和方法命名空间
664
- get sys() {
665
- const widget = this._widget;
666
- return {
667
- /**
668
- * 内置属性
669
- */
706
+ // 内置属性和方法命名空间
707
+ get sys() {
708
+ const widget = this._widget;
709
+ const [module, component] = widget.widgetType?.split?.(':') || [];
710
+ return {
711
+ /**
712
+ * 内置属性
713
+ */
714
+
715
+ get id() {
716
+ return widget.id;
717
+ },
670
718
 
671
- get id() {
672
- return widget.id;
673
- },
719
+ get module() {
720
+ return module;
721
+ },
674
722
 
675
- get name() {
676
- return widget.widgetType;
677
- },
723
+ get component() {
724
+ return component;
725
+ },
678
726
 
679
- get parent() {
680
- return widget.parent?._userWidget;
681
- },
727
+ get name() {
728
+ return widget.widgetType;
729
+ },
682
730
 
683
- get children() {
684
- return widget.children?.map((item) => item._userWidget) || [];
685
- },
731
+ get parent() {
732
+ return widget.parent?._userWidget;
733
+ },
686
734
 
687
- /**
688
- * 内置方法
689
- */
735
+ get children() {
736
+ return widget.children?.map((item) => item._userWidget) || [];
737
+ },
690
738
 
691
- closest(filter) {
692
- let { parent } = this;
693
- if (!filter) return parent;
739
+ /**
740
+ * 内置方法
741
+ */
694
742
 
695
- while (parent) {
696
- const matched = filter(parent);
697
- if (matched) return parent;
743
+ closest(filter) {
744
+ let { parent } = this;
745
+ if (!filter) return parent;
698
746
 
699
- parent = parent.parent;
700
- }
747
+ while (parent) {
748
+ const matched = filter(parent);
749
+ if (matched) return parent;
701
750
 
702
- return null;
703
- },
704
- };
705
- }
751
+ parent = parent.parent;
752
+ }
706
753
 
707
- get custom() {
708
- const { methods = {}, ...restInstance } = this._widget._getInstanceRef?.()?.current || {};
754
+ return null;
755
+ },
709
756
 
710
- const userCustomMember = {
711
- ...this._widget._methods,
712
- ...restInstance,
713
- ...methods,
714
- };
757
+ /**
758
+ * 为了兼容 2.17+ button 组件,使用 $node.parent.getConfig
759
+ * 挂载代理
760
+ */
761
+ getConfig(...args) {
762
+ return widget.getConfig?.(...args);
763
+ },
764
+ };
765
+ }
715
766
 
716
- return userCustomMember;
717
- }
767
+ get custom() {
768
+ const { methods = {}, ...restInstance } = this._widget._getInstanceRef?.()?.current || {};
769
+
770
+ const userCustomMember = {
771
+ ...this._widget._methods,
772
+ ...restInstance,
773
+ ...methods,
774
+ };
775
+
776
+ return userCustomMember;
777
+ }
718
778
  }
719
779
 
720
780
  export function generateWidgetAPIContext($w = {}, widget, forContext) {
721
- return new Proxy($w, {
722
- get(target, prop) {
723
- // debugger;
724
-
725
- /**
726
- * for context 变量优先
727
- */
728
- const { lists = [] } = forContext;
729
- for (const meta of lists) {
730
- const map = meta.alias || {};
731
- if (prop in map) {
732
- return map[prop];
733
- }
781
+ return new Proxy($w, {
782
+ get(target, prop) {
783
+ // debugger;
784
+
785
+ /**
786
+ * for context 变量优先
787
+ */
788
+ const { lists = [] } = forContext;
789
+ for (const meta of lists) {
790
+ const map = meta.alias || {};
791
+ if (prop in map) {
792
+ return map[prop];
734
793
  }
794
+ }
735
795
 
736
- // 尝试代理同级 widget
737
- if (widget) {
738
- let { parent } = widget;
739
- while (parent) {
740
- if (parent._descendants?.[prop]) {
741
- return parent._descendants[prop]._userWidget;
742
- }
743
- parent = parent.parent;
796
+ // 尝试代理同级 widget
797
+ if (widget) {
798
+ let { parent } = widget;
799
+ while (parent) {
800
+ if (parent._descendants?.[prop]) {
801
+ return parent._descendants[prop]._userWidget;
744
802
  }
803
+ parent = parent.parent;
745
804
  }
805
+ }
746
806
 
747
- // 尝试代理到全局的 $w
748
- return target[prop];
749
- },
750
- });
807
+ // 尝试代理到全局的 $w
808
+ return target[prop];
809
+ },
810
+ });
751
811
  }