airx 0.4.0 → 0.7.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.
Files changed (150) hide show
  1. package/README.md +247 -63
  2. package/output/app/app.d.ts +41 -0
  3. package/output/app/app.d.ts.map +1 -0
  4. package/output/app/app.js +51 -0
  5. package/output/app/app.js.map +1 -0
  6. package/output/app/index.d.ts +2 -0
  7. package/output/app/index.d.ts.map +1 -0
  8. package/output/app/index.js +2 -0
  9. package/output/app/index.js.map +1 -0
  10. package/output/element/element.d.ts +108 -0
  11. package/output/element/element.d.ts.map +1 -0
  12. package/output/element/element.js +106 -0
  13. package/output/element/element.js.map +1 -0
  14. package/output/element/index.d.ts +2 -0
  15. package/output/element/index.d.ts.map +1 -0
  16. package/output/element/index.js +2 -0
  17. package/output/element/index.js.map +1 -0
  18. package/output/index.d.ts +40 -0
  19. package/output/index.d.ts.map +1 -0
  20. package/output/index.js +36 -0
  21. package/output/index.js.map +1 -0
  22. package/output/jsx-dev-runtime.d.ts +4 -0
  23. package/output/jsx-dev-runtime.d.ts.map +1 -0
  24. package/output/jsx-dev-runtime.js +9 -0
  25. package/output/jsx-dev-runtime.js.map +1 -0
  26. package/output/jsx-runtime.d.ts +23 -0
  27. package/output/jsx-runtime.d.ts.map +1 -0
  28. package/output/jsx-runtime.js +24 -0
  29. package/output/jsx-runtime.js.map +1 -0
  30. package/output/logger/index.d.ts +2 -0
  31. package/output/logger/index.d.ts.map +1 -0
  32. package/output/logger/index.js +2 -0
  33. package/output/logger/index.js.map +1 -0
  34. package/output/{esm → logger}/logger.d.ts +1 -0
  35. package/output/logger/logger.d.ts.map +1 -0
  36. package/output/logger/logger.js +17 -0
  37. package/output/logger/logger.js.map +1 -0
  38. package/output/{esm/render/common/index.d.ts → render/basic/common.d.ts} +4 -3
  39. package/output/render/basic/common.d.ts.map +1 -0
  40. package/output/render/basic/common.js +436 -0
  41. package/output/render/basic/common.js.map +1 -0
  42. package/output/render/basic/hooks/hooks.d.ts +61 -0
  43. package/output/render/basic/hooks/hooks.d.ts.map +1 -0
  44. package/output/render/basic/hooks/hooks.js +72 -0
  45. package/output/render/basic/hooks/hooks.js.map +1 -0
  46. package/output/render/basic/hooks/index.d.ts +2 -0
  47. package/output/render/basic/hooks/index.d.ts.map +1 -0
  48. package/output/render/basic/hooks/index.js +2 -0
  49. package/output/render/basic/hooks/index.js.map +1 -0
  50. package/output/render/basic/index.d.ts +2 -0
  51. package/output/render/basic/index.d.ts.map +1 -0
  52. package/output/render/basic/index.js +2 -0
  53. package/output/render/basic/index.js.map +1 -0
  54. package/output/{umd/render/common/plugins → render/basic/plugins/context}/context.d.ts +2 -1
  55. package/output/render/basic/plugins/context/context.d.ts.map +1 -0
  56. package/output/render/basic/plugins/context/context.js +12 -0
  57. package/output/render/basic/plugins/context/context.js.map +1 -0
  58. package/output/render/basic/plugins/context/index.d.ts +2 -0
  59. package/output/render/basic/plugins/context/index.d.ts.map +1 -0
  60. package/output/render/basic/plugins/context/index.js +2 -0
  61. package/output/render/basic/plugins/context/index.js.map +1 -0
  62. package/output/render/basic/plugins/index.d.ts +3 -0
  63. package/output/render/basic/plugins/index.d.ts.map +1 -0
  64. package/output/render/basic/plugins/index.js +2 -0
  65. package/output/render/basic/plugins/index.js.map +1 -0
  66. package/output/{umd/render/common/plugins/internal → render/basic/plugins/internal/basic}/basic.d.ts +4 -3
  67. package/output/render/basic/plugins/internal/basic/basic.d.ts.map +1 -0
  68. package/output/render/basic/plugins/internal/basic/basic.js +158 -0
  69. package/output/render/basic/plugins/internal/basic/basic.js.map +1 -0
  70. package/output/render/basic/plugins/internal/basic/index.d.ts +2 -0
  71. package/output/render/basic/plugins/internal/basic/index.d.ts.map +1 -0
  72. package/output/render/basic/plugins/internal/basic/index.js +2 -0
  73. package/output/render/basic/plugins/internal/basic/index.js.map +1 -0
  74. package/output/render/basic/plugins/internal/inject/index.d.ts +2 -0
  75. package/output/render/basic/plugins/internal/inject/index.d.ts.map +1 -0
  76. package/output/render/basic/plugins/internal/inject/index.js +2 -0
  77. package/output/render/basic/plugins/internal/inject/index.js.map +1 -0
  78. package/output/{umd/render/common/plugins/internal → render/basic/plugins/internal/inject}/inject.d.ts +3 -2
  79. package/output/render/basic/plugins/internal/inject/inject.d.ts.map +1 -0
  80. package/output/render/basic/plugins/internal/inject/inject.js +25 -0
  81. package/output/render/basic/plugins/internal/inject/inject.js.map +1 -0
  82. package/output/{umd/render/common/plugins/index.d.ts → render/basic/plugins/plugin.d.ts} +3 -3
  83. package/output/render/basic/plugins/plugin.d.ts.map +1 -0
  84. package/output/render/basic/plugins/plugin.js +2 -0
  85. package/output/render/basic/plugins/plugin.js.map +1 -0
  86. package/output/{esm/render → render/browser}/browser.d.ts +4 -3
  87. package/output/render/browser/browser.d.ts.map +1 -0
  88. package/output/render/browser/browser.js +218 -0
  89. package/output/render/browser/browser.js.map +1 -0
  90. package/output/render/browser/index.d.ts +2 -0
  91. package/output/render/browser/index.d.ts.map +1 -0
  92. package/output/render/browser/index.js +2 -0
  93. package/output/render/browser/index.js.map +1 -0
  94. package/output/render/index.d.ts +5 -0
  95. package/output/render/index.d.ts.map +1 -0
  96. package/output/render/index.js +5 -0
  97. package/output/render/index.js.map +1 -0
  98. package/output/render/server/index.d.ts +2 -0
  99. package/output/render/server/index.d.ts.map +1 -0
  100. package/output/render/server/index.js +2 -0
  101. package/output/render/server/index.js.map +1 -0
  102. package/output/{umd/render → render/server}/server.d.ts +3 -2
  103. package/output/render/server/server.d.ts.map +1 -0
  104. package/output/render/server/server.js +314 -0
  105. package/output/render/server/server.js.map +1 -0
  106. package/output/signal/index.d.ts +4 -0
  107. package/output/signal/index.d.ts.map +1 -0
  108. package/output/signal/index.js +2 -0
  109. package/output/signal/index.js.map +1 -0
  110. package/output/{esm/signal/index.d.ts → signal/signal.d.ts} +1 -1
  111. package/output/signal/signal.d.ts.map +1 -0
  112. package/output/signal/signal.js +46 -0
  113. package/output/signal/signal.js.map +1 -0
  114. package/output/symbol/index.d.ts +2 -0
  115. package/output/symbol/index.d.ts.map +1 -0
  116. package/output/symbol/index.js +2 -0
  117. package/output/symbol/index.js.map +1 -0
  118. package/output/types/index.d.ts +2 -0
  119. package/output/types/index.d.ts.map +1 -0
  120. package/output/types/index.js +2 -0
  121. package/output/types/index.js.map +1 -0
  122. package/output/{esm → types}/types.d.ts +3 -1
  123. package/output/types/types.d.ts.map +1 -0
  124. package/output/types/types.js +2 -0
  125. package/output/types/types.js.map +1 -0
  126. package/package.json +42 -15
  127. package/output/esm/element.d.ts +0 -48
  128. package/output/esm/index.d.ts +0 -14
  129. package/output/esm/index.js +0 -1289
  130. package/output/esm/index.js.map +0 -1
  131. package/output/esm/render/common/hooks.d.ts +0 -10
  132. package/output/esm/render/common/plugins/context.d.ts +0 -5
  133. package/output/esm/render/common/plugins/index.d.ts +0 -19
  134. package/output/esm/render/common/plugins/internal/basic.d.ts +0 -10
  135. package/output/esm/render/common/plugins/internal/inject.d.ts +0 -6
  136. package/output/esm/render/index.d.ts +0 -4
  137. package/output/esm/render/server.d.ts +0 -5
  138. package/output/esm/symbol.d.ts +0 -2
  139. package/output/umd/element.d.ts +0 -48
  140. package/output/umd/index.d.ts +0 -14
  141. package/output/umd/index.js +0 -1304
  142. package/output/umd/index.js.map +0 -1
  143. package/output/umd/logger.d.ts +0 -3
  144. package/output/umd/render/browser.d.ts +0 -7
  145. package/output/umd/render/common/hooks.d.ts +0 -10
  146. package/output/umd/render/common/index.d.ts +0 -64
  147. package/output/umd/render/index.d.ts +0 -4
  148. package/output/umd/signal/index.d.ts +0 -6
  149. package/output/umd/symbol.d.ts +0 -2
  150. package/output/umd/types.d.ts +0 -1173
@@ -1,1304 +0,0 @@
1
- (function (global, factory) {
2
- typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) :
3
- typeof define === 'function' && define.amd ? define(['exports'], factory) :
4
- (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.airx = {}));
5
- })(this, (function (exports) { 'use strict';
6
-
7
- const globalContext = {
8
- current: null
9
- };
10
- function useContext() {
11
- if (globalContext.current == null) {
12
- throw new Error('Unable to find a valid component context');
13
- }
14
- return globalContext.current;
15
- }
16
- const onMounted = (listener) => {
17
- return useContext().onMounted(listener);
18
- };
19
- const onUnmounted = (listener) => {
20
- return useContext().onUnmounted(listener);
21
- };
22
- const inject = (key) => {
23
- return useContext().inject(key);
24
- };
25
- const provide = (key, value) => {
26
- return useContext().provide(key, value);
27
- };
28
-
29
- const isPrintLogs = typeof process != 'undefined'
30
- && process?.env?.NODE_ENV === 'development'
31
- && process?.env?.AIRX_DEBUG === 'true';
32
- function createLogger(name) {
33
- function getPrintPrefix() {
34
- const date = new Date().toLocaleString();
35
- return `[${date}][${name}]`;
36
- }
37
- function debug(...args) {
38
- if (isPrintLogs)
39
- console.log(getPrintPrefix(), ...args);
40
- }
41
- return { debug };
42
- }
43
-
44
- let firstSignal = undefined;
45
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
46
- const globalNS = (function () {
47
- // the only reliable means to get the global object is
48
- // `Function('return this')()`
49
- // However, this causes CSP violations in Chrome apps.
50
- if (typeof self !== 'undefined') {
51
- return self;
52
- }
53
- if (typeof window !== 'undefined') {
54
- return window;
55
- }
56
- if (typeof global !== 'undefined') {
57
- return global;
58
- }
59
- throw new Error('unable to locate global object');
60
- })();
61
- // 通过函数包装来延迟加载 Signal
62
- // 这对使用 Polyfill 的应用来说更友好
63
- function getSignal() {
64
- const globalSignal = globalNS['Signal'];
65
- if (globalSignal == null)
66
- throw new Error('Signal is undefined');
67
- if (firstSignal == null)
68
- firstSignal = globalSignal;
69
- if (firstSignal !== globalSignal)
70
- throw new Error('Signal have multiple instances');
71
- return globalSignal;
72
- }
73
- function createWatch(notify) {
74
- const signal = getSignal();
75
- return new signal.subtle.Watcher(notify);
76
- }
77
- function createComputed(computation, options) {
78
- const signal = getSignal();
79
- return new signal.Computed(computation, options);
80
- }
81
- function isState(target) {
82
- const signal = getSignal();
83
- return target instanceof signal.State;
84
- }
85
-
86
- const airxElementSymbol = Symbol('airx-element');
87
-
88
- /**
89
- * createElement 是用于创建 AirxElement 的工具函数
90
- */
91
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
92
- function createElement(type, props, ...children) {
93
- const localChildren = [];
94
- if (children.length > 0) {
95
- localChildren.push(...children);
96
- }
97
- else if (props && props.children) {
98
- localChildren.push(props.children);
99
- }
100
- return {
101
- type,
102
- props: {
103
- ...props,
104
- children: localChildren
105
- },
106
- [airxElementSymbol]: true
107
- };
108
- }
109
- function isValidElement(element) {
110
- return typeof element === 'object'
111
- && element !== null
112
- && Reflect.get(element, airxElementSymbol);
113
- }
114
- function Fragment(props) {
115
- return () => props.children;
116
- }
117
- function component(comp) {
118
- return comp;
119
- }
120
- function createErrorRender(error) {
121
- console.error(error);
122
- const handleClick = () => {
123
- // 点击输出错误是为了避免
124
- // 页面上多个组件同时出错时
125
- // 无法定位错误与之对应的组件
126
- console.error(error);
127
- };
128
- const formattingError = () => {
129
- if (error == null)
130
- return 'Unknown rendering error';
131
- if (error instanceof Error)
132
- return error.message;
133
- return JSON.stringify(error);
134
- };
135
- const errorBlockStyle = {
136
- padding: '8px',
137
- fontSize: '20px',
138
- color: 'rgb(255,255,255)',
139
- backgroundColor: 'rgb(255, 0, 0)',
140
- };
141
- return () => createElement('div', { style: errorBlockStyle, onClick: handleClick }, formattingError());
142
- }
143
-
144
- class InnerAirxComponentContext {
145
- instance;
146
- disposers = new Set();
147
- providedMap = new Map();
148
- injectedMap = new Map();
149
- mountListeners = new Set();
150
- unmountedListeners = new Set();
151
- triggerMounted() {
152
- this.mountListeners.forEach(listener => {
153
- let disposer = undefined;
154
- try {
155
- disposer = listener();
156
- }
157
- catch (err) {
158
- console.error(err, listener);
159
- }
160
- if (typeof disposer === 'function') {
161
- this.addDisposer(disposer);
162
- }
163
- });
164
- // 生命周期只会调用一次
165
- this.mountListeners.clear();
166
- }
167
- triggerUnmounted() {
168
- // 递归的调用子 child 的 Unmounted
169
- if (this.instance?.child != null) {
170
- this.instance.child.context.triggerUnmounted();
171
- }
172
- // 处理自己
173
- this.unmountedListeners.forEach(listener => {
174
- try {
175
- listener();
176
- }
177
- catch (err) {
178
- console.error(err, listener);
179
- }
180
- });
181
- // 处理兄弟节点
182
- if (this.instance?.sibling != null) {
183
- this.instance.sibling.context.triggerUnmounted();
184
- }
185
- this.dispose();
186
- }
187
- onMounted(listener) {
188
- this.mountListeners.add(listener);
189
- }
190
- onUnmounted(listener) {
191
- this.unmountedListeners.add(listener);
192
- }
193
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
194
- provide(key, value) {
195
- this.providedMap.set(key, value);
196
- return v => {
197
- if (typeof v === 'function') {
198
- const old = this.providedMap.get(key);
199
- const func = v;
200
- this.providedMap.set(key, func(old));
201
- return;
202
- }
203
- this.providedMap.set(key, v);
204
- };
205
- }
206
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
207
- inject(key) {
208
- const getProvideValueForParent = (instance, key) => {
209
- if (instance && instance.context) {
210
- const value = instance.context.providedMap.get(key);
211
- if (value != undefined)
212
- return value;
213
- if (instance.parent)
214
- return getProvideValueForParent(instance.parent, key);
215
- }
216
- return undefined;
217
- };
218
- const currentParentValue = getProvideValueForParent(this.instance.parent, key);
219
- this.injectedMap.set(key, currentParentValue); // 更新本地值
220
- return this.injectedMap.get(key);
221
- }
222
- addDisposer(...disposers) {
223
- disposers.forEach(disposer => {
224
- this.disposers.add(disposer);
225
- });
226
- }
227
- dispose() {
228
- this.disposers.forEach((disposer) => {
229
- try {
230
- disposer();
231
- }
232
- catch (err) {
233
- // eslint-disable-next-line no-console
234
- console.error(err, disposer);
235
- }
236
- });
237
- this.disposers.clear();
238
- this.injectedMap.clear();
239
- this.providedMap.clear();
240
- this.mountListeners.clear();
241
- this.unmountedListeners.clear();
242
- }
243
- /**
244
- * 当 context 传递给外部消费时,隐藏内部实现,仅暴露接口定义的内容
245
- * @returns 和 AirxComponentContext 接口完全一致的对象
246
- */
247
- getSafeContext() {
248
- return {
249
- inject: k => this.inject(k),
250
- provide: (k, v) => this.provide(k, v),
251
- onMounted: listener => this.onMounted(listener),
252
- onUnmounted: listener => this.onUnmounted(listener)
253
- };
254
- }
255
- }
256
- /**
257
- * 更新 children
258
- * @param parentInstance 当前正在处理的组件的实例
259
- * @param children 当前组件的子节点
260
- */
261
- function reconcileChildren(appContext, parentInstance, childrenElementArray) {
262
- const logger = createLogger('reconcileChildren');
263
- logger.debug('reconcileChildren', parentInstance, childrenElementArray);
264
- // parentInstance ←--------
265
- // | ↑ ↑
266
- // child parent parent
267
- // ↓ | |
268
- // instance -sibling→ instance -→ ....
269
- /**
270
- * 内部通过 index 生成 element 的 key
271
- * 通过前缀来避免和用户手动设置的 key 发生冲突
272
- * @param index
273
- * @returns 生成的 key
274
- */
275
- function getInnerElementIndexKey(index) {
276
- return `airx-element-inner-key:${index}`;
277
- }
278
- /**
279
- * 将 children 实例链转成 Map 便于此处消费
280
- * @param firstChild children 实例的第一个元素
281
- * @returns 从 firstChild 开始之后的所有 sibling 实例组成的 Map
282
- */
283
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
284
- function getChildrenInstanceMap(firstChild) {
285
- // 不使用递归是因为递归容易爆栈
286
- let nextIndex = 0;
287
- let nextChild = firstChild;
288
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
289
- const map = new Map();
290
- while (nextChild) {
291
- /* 如果有 key,则将 key 作为 map key,否则将 index 作为 key */
292
- if (nextChild.element?.props.key != null) {
293
- map.set(nextChild.element?.props.key, nextChild);
294
- }
295
- else {
296
- map.set(getInnerElementIndexKey(nextIndex), nextChild);
297
- }
298
- nextChild = nextChild.sibling;
299
- nextIndex += 1;
300
- }
301
- return map;
302
- }
303
- const newChildrenInstanceArray = [];
304
- const childrenInstanceMap = getChildrenInstanceMap(parentInstance.child);
305
- function getChildInstance(child, index) {
306
- if (child.props.key != null) {
307
- const key = child.props.key;
308
- const instance = childrenInstanceMap.get(key) || null;
309
- return [instance, () => childrenInstanceMap.delete(key)];
310
- }
311
- const innerKey = getInnerElementIndexKey(index);
312
- const instance = childrenInstanceMap.get(innerKey) || null;
313
- return [instance, () => childrenInstanceMap.delete(innerKey)];
314
- }
315
- /** 是否复用实例 */
316
- function isReuseInstance(instance, nextElement) {
317
- for (const plugin of appContext.plugins) {
318
- if (typeof plugin.isReuseInstance === 'function') {
319
- const result = plugin.isReuseInstance(instance, nextElement);
320
- // 任意一个插件要求不复用就不复用
321
- if (result === false)
322
- return false;
323
- }
324
- }
325
- return true;
326
- }
327
- function updateMemoProps(instance) {
328
- if (instance.element == null)
329
- return;
330
- if (instance.memoProps == null)
331
- instance.memoProps = {};
332
- // 简单来说就是以下几件事情
333
- // 始终保持 props 的引用不变
334
- // 1. 创建一个新对象来保存 beforeElement props 的状态
335
- // 2. 将新的 element 上的 props 引用设置为之前的 props
336
- // 3. 将新的 element 上的 props 更新到之前的 props 上去
337
- if (instance.memoProps != null) {
338
- for (const key in instance.memoProps) {
339
- delete instance.memoProps[key];
340
- }
341
- }
342
- // 将新的 props 更新上去
343
- for (const key in instance.element.props) {
344
- const value = instance.element.props[key];
345
- instance.memoProps[key] = value;
346
- }
347
- }
348
- function isNeedReRender(instance) {
349
- for (const plugin of appContext.plugins) {
350
- if (typeof plugin.isReRender === 'function') {
351
- const result = plugin.isReRender(instance);
352
- // 任意一个插件要求重新渲染就重新渲染
353
- if (result === true)
354
- return true;
355
- }
356
- }
357
- return false;
358
- }
359
- function getElementNamespace(element) {
360
- const ns = Object.keys(element.props)
361
- .filter(key => (key === 'xmlns' || key.startsWith('xmlns:')))
362
- .sort((a, b) => a.length - b.length);
363
- if (ns.length > 1) {
364
- console.log('airx currently does not support setting multiple xmlns');
365
- return '';
366
- }
367
- if (ns.some(i => i.startsWith('xmlns:'))) {
368
- console.log('airx does not currently support setting named namespaces,only supports default');
369
- return '';
370
- }
371
- // 原则上来说 html 仅支持设置默认的 namespace
372
- return element.props[ns[0]];
373
- }
374
- // 依次遍历 child 并和 instance 对比
375
- for (let index = 0; index < childrenElementArray.length; index++) {
376
- const element = childrenElementArray[index];
377
- const [instance, seize] = getChildInstance(element, index);
378
- if (instance && isReuseInstance(instance, element)) {
379
- seize(); // 从 childrenInstanceMap 中释放
380
- newChildrenInstanceArray.push(instance);
381
- instance.beforeElement = instance.element;
382
- instance.element = element;
383
- updateMemoProps(instance);
384
- // 如果父组件更新了,子组件全部都要更新
385
- if (instance.needReRender !== true && typeof element.type === 'function') {
386
- instance.needReRender = parentInstance.needReRender || isNeedReRender(instance);
387
- }
388
- }
389
- else {
390
- const context = new InnerAirxComponentContext();
391
- const elementNamespace = getElementNamespace(element);
392
- const instance = { element, context, elementNamespace };
393
- newChildrenInstanceArray.push(instance);
394
- context.instance = instance;
395
- updateMemoProps(instance);
396
- // 添加 ref 处理
397
- if ('ref' in instance.memoProps) {
398
- context.onMounted(() => {
399
- const ref = instance.memoProps.ref;
400
- // 如果组件有自己的 dom
401
- if (instance.domRef) {
402
- if (isState(ref)) {
403
- ref.set(instance.domRef);
404
- return () => ref.set(undefined);
405
- }
406
- if (typeof ref === 'function') {
407
- ref(instance.domRef);
408
- return () => ref(undefined);
409
- }
410
- }
411
- });
412
- }
413
- }
414
- }
415
- // 新的 node
416
- for (let index = 0; index < newChildrenInstanceArray.length; index++) {
417
- const instance = newChildrenInstanceArray[index];
418
- // 确保链的干净
419
- instance.parent = parentInstance;
420
- if (index === 0)
421
- parentInstance.child = instance;
422
- if (index > 0)
423
- newChildrenInstanceArray[index - 1].sibling = instance;
424
- if (index === newChildrenInstanceArray.length - 1)
425
- delete instance.sibling;
426
- // 处理 ElementNS: 继承父级 namespace
427
- if (!instance.elementNamespace && instance.parent?.elementNamespace) {
428
- // SVG 中 foreignObject 代表外来对象,起到隔离 namespace 的作用
429
- if (instance.parent.element?.type !== 'foreignObject') {
430
- instance.elementNamespace = instance.parent.elementNamespace;
431
- }
432
- }
433
- }
434
- // 剩余的是需要移除的
435
- if (childrenInstanceMap.size > 0) {
436
- if (parentInstance.deletions == null) {
437
- parentInstance.deletions = new Set();
438
- }
439
- childrenInstanceMap.forEach(instance => {
440
- parentInstance.deletions?.add(instance);
441
- });
442
- }
443
- logger.debug('parentInstance', parentInstance);
444
- }
445
- /**
446
- * 处理单个 instance
447
- * @param instance 当前处理的实例
448
- * @returns 返回下一个需要处理的 instance
449
- */
450
- function performUnitOfWork(pluginContext, instance, onUpdateRequire) {
451
- const element = instance.element;
452
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
453
- function childrenAsElements(children) {
454
- const childrenAsArray = Array.isArray(children) ? children : [children];
455
- function isComment(element) {
456
- if (element === '')
457
- return true;
458
- if (element == null)
459
- return true;
460
- if (element === false)
461
- return true;
462
- return false;
463
- }
464
- return childrenAsArray.flat(3).map(element => {
465
- if (isValidElement(element))
466
- return element;
467
- const elementType = isComment(element) ? 'comment' : 'text';
468
- const textContent = element === '' ? 'empty-string' : String(element);
469
- return createElement(elementType, { textContent });
470
- });
471
- }
472
- // airx 组件
473
- if (typeof element?.type === 'function') {
474
- if (instance.componentReturnValue == null) {
475
- const component = element.type;
476
- const beforeContext = globalContext.current;
477
- globalContext.current = instance.context.getSafeContext();
478
- let componentReturnValue = undefined;
479
- try {
480
- componentReturnValue = component(instance.memoProps);
481
- }
482
- catch (error) {
483
- componentReturnValue = createErrorRender(error);
484
- }
485
- // restore context
486
- globalContext.current = beforeContext;
487
- instance.componentReturnValue = componentReturnValue;
488
- // static function component
489
- if (isValidElement(componentReturnValue)) {
490
- const elements = childrenAsElements(componentReturnValue);
491
- reconcileChildren(pluginContext, instance, elements);
492
- }
493
- // reaction function component
494
- if (typeof componentReturnValue === 'function') {
495
- if (instance.signalWatcher == null) {
496
- // Watch 是惰性的,只有当 Signal 被读取时才会触发 --!
497
- const signalWatcher = createWatch(async () => {
498
- instance.needReRender = true;
499
- onUpdateRequire?.(instance);
500
- queueMicrotask(() => {
501
- signalWatcher.watch();
502
- const paddings = signalWatcher.getPending();
503
- for (const padding of paddings)
504
- padding.get();
505
- });
506
- });
507
- instance.signalWatcher = signalWatcher;
508
- instance.context.addDisposer(() => signalWatcher.unwatch());
509
- }
510
- const childrenComputed = createComputed(() => {
511
- try {
512
- if (typeof componentReturnValue === 'function') {
513
- return componentReturnValue();
514
- }
515
- }
516
- catch (error) {
517
- return createErrorRender(error)();
518
- }
519
- });
520
- instance.signalWatcher.watch(childrenComputed);
521
- const children = childrenComputed.get();
522
- reconcileChildren(pluginContext, instance, childrenAsElements(children));
523
- }
524
- }
525
- if (instance.needReRender) {
526
- let children;
527
- try {
528
- if (isValidElement(instance.componentReturnValue)) {
529
- children = instance.componentReturnValue;
530
- }
531
- else {
532
- // 如果是由于父组件导致的子组件渲染
533
- // 直接使用 childrenComputed.get() 将读取到缓存值
534
- // 因此这里使用 childrenRender 来更新 children 的值
535
- children = instance.componentReturnValue();
536
- }
537
- }
538
- catch (error) {
539
- children = createErrorRender(error)();
540
- }
541
- reconcileChildren(pluginContext, instance, childrenAsElements(children));
542
- delete instance.needReRender;
543
- }
544
- }
545
- // 浏览器组件/标签
546
- if (typeof element?.type === 'string') {
547
- if ('children' in element.props && Array.isArray(element.props.children)) {
548
- reconcileChildren(pluginContext, instance, childrenAsElements(element.props.children));
549
- }
550
- }
551
- // 优先处理 child
552
- if (instance.child) {
553
- return instance.child;
554
- }
555
- /**
556
- * 递归向上查找可用的兄弟 instance
557
- * @param instance
558
- * @returns 返回下一个需要处理的兄弟 instance
559
- */
560
- function returnSibling(instance) {
561
- if (instance.sibling) {
562
- return instance.sibling;
563
- }
564
- if (instance.parent) {
565
- return returnSibling(instance.parent);
566
- }
567
- return null;
568
- }
569
- return returnSibling(instance);
570
- }
571
-
572
- function camelToKebab(str) {
573
- return str.replace(/([A-Z])/g, (match, p1, offset) => {
574
- if (offset === 0) {
575
- return p1.toLowerCase();
576
- }
577
- else {
578
- return '-' + p1.toLowerCase();
579
- }
580
- });
581
- }
582
- class ServerElement {
583
- nodeName;
584
- static createTextNode(content) {
585
- const dom = new ServerElement('#text');
586
- dom.content = content;
587
- return dom;
588
- }
589
- static createComment(content) {
590
- const dom = new ServerElement('#comment');
591
- dom.content = content;
592
- return dom;
593
- }
594
- static createElement(tag) {
595
- return new ServerElement(tag);
596
- }
597
- constructor(nodeName) {
598
- this.nodeName = nodeName;
599
- this.style = {};
600
- this.content = '';
601
- this.children = [];
602
- this.className = '';
603
- this.attributes = new Map();
604
- }
605
- firstChild;
606
- nextSibling;
607
- parentNode;
608
- className;
609
- style;
610
- content;
611
- children;
612
- attributes;
613
- removeChild(dom) {
614
- /* eslint-disable @typescript-eslint/ban-ts-comment */
615
- const index = this.children.findIndex(v => v === dom);
616
- if (index != -1)
617
- this.children.splice(index, 1);
618
- // @ts-ignore
619
- this.firstChild = undefined;
620
- // @ts-ignore
621
- this.nextSibling = undefined;
622
- // @ts-ignore
623
- this.parentNode = undefined;
624
- /* eslint-enable @typescript-eslint/ban-ts-comment */
625
- }
626
- appendChild(dom) {
627
- /* eslint-disable @typescript-eslint/ban-ts-comment */
628
- // @ts-ignore
629
- dom.parentNode = this;
630
- if (this.children.length > 0) {
631
- // @ts-ignore
632
- this.children[this.children.length - 1].nextSibling = dom;
633
- }
634
- // @ts-ignore
635
- this.firstChild = this.children[0];
636
- this.children.push(dom);
637
- /* eslint-enable @typescript-eslint/ban-ts-comment */
638
- }
639
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
640
- setAttribute(name, value) {
641
- if (value === '')
642
- return this.attributes.delete(name);
643
- /* eslint-disable @typescript-eslint/ban-ts-comment */
644
- // @ts-ignore
645
- if (name === 'class')
646
- return this.className = value;
647
- // @ts-ignore
648
- if (name === 'style')
649
- return this.style = value;
650
- /* eslint-enable @typescript-eslint/ban-ts-comment */
651
- this.attributes.set(name, value);
652
- }
653
- removeAttribute(name) {
654
- this.attributes.delete(name);
655
- }
656
- toString() {
657
- if (this.nodeName === '#text')
658
- return this.content;
659
- if (this.nodeName === '#comment')
660
- return this.content;
661
- const styleString = Object.entries(this.style)
662
- .map(([key, value]) => `${camelToKebab(key)}:${value}`)
663
- .join(';');
664
- const attributes = [...this.attributes.entries()];
665
- if (styleString.length > 0)
666
- attributes.push(['style', styleString]);
667
- if (this.className.length > 0)
668
- attributes.push(['class', this.className]);
669
- const attributesString = attributes.map(([name, value]) => ` ${name}="${value}"`).join('');
670
- return `<${this.nodeName}${attributesString}>${this.children.map(child => child.toString()).join('')}</${this.nodeName}>`;
671
- }
672
- }
673
- function render$1(pluginContext, element, onComplete) {
674
- const rootInstance = {
675
- domRef: ServerElement.createElement('div'),
676
- context: new InnerAirxComponentContext()
677
- };
678
- rootInstance.context.instance = rootInstance;
679
- const context = {
680
- rootInstance,
681
- nextUnitOfWork: null,
682
- needCommitDom: false
683
- };
684
- const appInstance = {
685
- element,
686
- parent: context.rootInstance,
687
- memoProps: { ...element.props },
688
- context: new InnerAirxComponentContext()
689
- };
690
- appInstance.context.instance = appInstance;
691
- context.rootInstance.child = appInstance;
692
- context.nextUnitOfWork = appInstance;
693
- /**
694
- * 提交 Dom 变化
695
- */
696
- function commitDom(rootInstance, rootNode) {
697
- const logger = createLogger('commitDom');
698
- logger.debug('commitDom', rootInstance);
699
- function updateDomProperties(dom, nextProps, prevProps = {}) {
700
- const isKey = (key) => key === 'key';
701
- const isRef = (key) => key === 'ref';
702
- const isStyle = (key) => key === 'style';
703
- const isClass = (key) => key === 'class';
704
- const isEvent = (key) => key.startsWith("on");
705
- const isChildren = (key) => key === 'children';
706
- const isGone = (_prev, next) => (key) => !(key in next);
707
- const isNew = (prev, next) => (key) => prev[key] !== next[key];
708
- const isProperty = (key) => !isChildren(key) && !isEvent(key) && !isStyle(key) && !isClass(key) && !isKey(key) && !isRef(key);
709
- // https://developer.mozilla.org/zh-CN/docs/Web/API/Node
710
- if (dom.nodeName === '#text' || dom.nodeName === '#comment') {
711
- const textNode = dom;
712
- if (textNode.nodeValue !== nextProps.textContent) {
713
- textNode.nodeValue = String(nextProps.textContent);
714
- }
715
- return;
716
- }
717
- // remove old style
718
- const oldStyle = prevProps?.style;
719
- if (typeof oldStyle === 'object' && oldStyle != null) {
720
- Object.keys(oldStyle).forEach(key => {
721
- /* eslint-disable @typescript-eslint/no-explicit-any */
722
- delete dom.style[key];
723
- /* eslint-enable @typescript-eslint/no-explicit-any */
724
- });
725
- }
726
- // add new style
727
- const newStyle = nextProps?.style;
728
- if (typeof newStyle === 'object' && newStyle != null) {
729
- Object.keys(newStyle).forEach((key) => {
730
- /* eslint-disable @typescript-eslint/no-explicit-any */
731
- const value = newStyle?.[key];
732
- dom.style[key] = value == null ? '' : value;
733
- /* eslint-enable @typescript-eslint/no-explicit-any */
734
- });
735
- }
736
- if (dom.className !== nextProps?.class) {
737
- if (!nextProps?.class) {
738
- dom.removeAttribute('class');
739
- }
740
- else if (typeof nextProps?.class === 'string') {
741
- dom.setAttribute('class', nextProps?.class);
742
- }
743
- }
744
- // Remove old properties
745
- Object.keys(prevProps)
746
- .filter(isProperty)
747
- .filter(isGone(prevProps, nextProps))
748
- .forEach(name => dom.removeAttribute(name));
749
- // Set new or changed properties
750
- Object.keys(nextProps)
751
- .filter(isProperty)
752
- .filter(isNew(prevProps, nextProps))
753
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
754
- .forEach(name => dom.setAttribute(name, nextProps[name]));
755
- }
756
- function getParentDom(instance) {
757
- if (instance.parent?.domRef != null) {
758
- return instance.parent.domRef;
759
- }
760
- if (instance.parent) {
761
- return getParentDom(instance.parent);
762
- }
763
- throw new Error('Cant find dom');
764
- }
765
- /**
766
- * 查找 instance 下所有的一级 dom
767
- * @param instance
768
- * @returns
769
- */
770
- function getChildDoms(instance) {
771
- const domList = [];
772
- const todoList = [instance];
773
- while (todoList.length > 0) {
774
- const current = todoList.pop();
775
- // 找到真实 dom 直接提交
776
- if (current?.domRef != null) {
777
- domList.push(current.domRef);
778
- }
779
- // 有子节点但是无真实 dom,向下继续查找
780
- if (current?.domRef == null) {
781
- if (current?.child != null) {
782
- todoList.push(current.child);
783
- }
784
- }
785
- // 可能有兄弟节点,则需要继续查找
786
- if (current?.sibling != null) {
787
- todoList.push(current.sibling);
788
- }
789
- }
790
- return domList;
791
- }
792
- function commitInstanceDom(nextInstance, oldNode) {
793
- // 移除标删元素
794
- if (nextInstance.deletions) {
795
- for (const deletion of nextInstance.deletions) {
796
- const domList = getChildDoms(deletion);
797
- for (let index = 0; index < domList.length; index++) {
798
- const dom = domList[index];
799
- if (dom && dom.parentNode)
800
- dom.parentNode.removeChild(dom);
801
- }
802
- deletion.context.triggerUnmounted();
803
- }
804
- nextInstance.deletions.clear();
805
- }
806
- // 创建 dom
807
- if (nextInstance.domRef == null) {
808
- if (nextInstance.element == null)
809
- throw new Error('???');
810
- if (typeof nextInstance.element.type === 'string') {
811
- if (nextInstance.element.type === 'text') {
812
- const textContent = nextInstance.element.props.textContent;
813
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
814
- nextInstance.domRef = ServerElement.createTextNode(textContent);
815
- }
816
- else if (nextInstance.element.type === 'comment') {
817
- const textContent = nextInstance.element.props.textContent;
818
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
819
- nextInstance.domRef = ServerElement.createComment(textContent);
820
- }
821
- else {
822
- nextInstance.domRef = ServerElement.createElement(nextInstance.element.type);
823
- }
824
- }
825
- }
826
- // 更新属性
827
- if (nextInstance.domRef != null && nextInstance.element != null) {
828
- updateDomProperties(nextInstance.domRef, nextInstance.element.props, nextInstance.beforeElement?.props);
829
- }
830
- // 插入 parent
831
- // TODO: 针对仅移动时优化
832
- if (nextInstance.domRef != null) {
833
- if (oldNode !== nextInstance.domRef) {
834
- if (nextInstance.domRef.parentNode) {
835
- nextInstance.domRef.parentNode.removeChild(nextInstance.domRef);
836
- }
837
- const parentDom = getParentDom(nextInstance);
838
- parentDom.appendChild(nextInstance.domRef);
839
- }
840
- }
841
- }
842
- function commitWalkV2(initInstance, initNode) {
843
- // 创建一个栈,将根节点压入栈中
844
- const stack = [[initInstance, initNode]];
845
- while (stack.length > 0) {
846
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
847
- const stackLayer = stack.pop();
848
- if (typeof stackLayer === 'function') {
849
- stackLayer();
850
- continue;
851
- }
852
- const [instance, node] = stackLayer;
853
- commitInstanceDom(instance, node);
854
- // stack 是先入后出
855
- // 实际上是先渲染 child
856
- // 这里然后再渲染 sibling
857
- // 执行生命周期的 Mount
858
- stack.push(() => instance.context.triggerMounted());
859
- // 更新下一个兄弟节点
860
- if (instance.sibling != null) {
861
- const siblingNode = instance.domRef
862
- ? instance.domRef.nextSibling
863
- : node?.nextSibling;
864
- stack.push([instance.sibling, siblingNode || undefined]);
865
- }
866
- // 更新下一个子节点
867
- if (instance.child != null) {
868
- const childNode = instance.domRef
869
- ? instance.domRef.firstChild
870
- : node;
871
- stack.push([instance.child, childNode || undefined]);
872
- }
873
- }
874
- }
875
- commitWalkV2(rootInstance, rootNode);
876
- }
877
- while (context.nextUnitOfWork) {
878
- context.nextUnitOfWork = performUnitOfWork(pluginContext, context.nextUnitOfWork);
879
- }
880
- commitDom(context.rootInstance.child, context.rootInstance.domRef?.firstChild || undefined);
881
- onComplete(context.rootInstance.domRef?.toString() || '');
882
- }
883
-
884
- function render(pluginContext, element, domRef) {
885
- const rootInstance = {
886
- domRef,
887
- context: new InnerAirxComponentContext()
888
- };
889
- rootInstance.context.instance = rootInstance;
890
- const context = {
891
- rootInstance,
892
- nextUnitOfWork: null,
893
- needCommitDom: false
894
- };
895
- const appInstance = {
896
- element,
897
- parent: context.rootInstance,
898
- memoProps: { ...element.props },
899
- context: new InnerAirxComponentContext()
900
- };
901
- appInstance.context.instance = appInstance;
902
- context.rootInstance.child = appInstance;
903
- context.nextUnitOfWork = appInstance;
904
- /**
905
- * 提交 Dom 变化
906
- */
907
- function commitDom(rootInstance, rootNode) {
908
- const logger = createLogger('commitDom');
909
- logger.debug('commitDom', rootInstance);
910
- function updateDomProperties(dom, nextProps, prevProps = {}) {
911
- for (const plugin of pluginContext.plugins) {
912
- if (plugin.updateDom)
913
- plugin.updateDom(dom, nextProps, prevProps);
914
- }
915
- }
916
- function getParentDom(instance) {
917
- if (instance.parent?.domRef != null) {
918
- return instance.parent.domRef;
919
- }
920
- if (instance.parent) {
921
- return getParentDom(instance.parent);
922
- }
923
- throw new Error('Cant find dom');
924
- }
925
- /**
926
- * 查找 instance 下所有的一级 dom
927
- * @param instance
928
- * @returns
929
- */
930
- function getChildDoms(instance) {
931
- const domList = [];
932
- const todoList = [instance];
933
- while (todoList.length > 0) {
934
- const current = todoList.pop();
935
- // 找到真实 dom 直接提交
936
- if (current?.domRef != null) {
937
- domList.push(current.domRef);
938
- }
939
- // 有子节点但是无真实 dom,向下继续查找
940
- if (current?.domRef == null) {
941
- if (current?.child != null) {
942
- todoList.push(current.child);
943
- }
944
- }
945
- // 可能有兄弟节点,则需要继续查找
946
- if (current?.sibling != null) {
947
- todoList.push(current.sibling);
948
- }
949
- }
950
- return domList;
951
- }
952
- function commitInstanceDom(nextInstance, oldNode) {
953
- // 移除标删元素
954
- if (nextInstance.deletions) {
955
- for (const deletion of nextInstance.deletions) {
956
- const domList = getChildDoms(deletion);
957
- for (let index = 0; index < domList.length; index++) {
958
- const dom = domList[index];
959
- if (dom && dom.parentNode)
960
- dom.parentNode.removeChild(dom);
961
- }
962
- deletion.context.triggerUnmounted();
963
- }
964
- nextInstance.deletions.clear();
965
- }
966
- // 创建 dom
967
- if (nextInstance.domRef == null) {
968
- if (nextInstance.element == null)
969
- throw new Error('???');
970
- if (typeof nextInstance.element.type === 'string') {
971
- if (nextInstance.element.type === 'text') {
972
- const textContent = nextInstance.element.props.textContent;
973
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
974
- nextInstance.domRef = document.createTextNode(textContent);
975
- }
976
- else if (nextInstance.element.type === 'comment') {
977
- const textContent = nextInstance.element.props.textContent;
978
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
979
- nextInstance.domRef = document.createComment(textContent);
980
- }
981
- else {
982
- nextInstance.domRef = nextInstance.elementNamespace
983
- ? document.createElementNS(nextInstance.elementNamespace, nextInstance.element.type)
984
- : document.createElement(nextInstance.element.type);
985
- }
986
- if (nextInstance.domRef) {
987
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
988
- nextInstance.domRef.airxInstance = nextInstance;
989
- }
990
- }
991
- }
992
- // 更新属性
993
- if (nextInstance.domRef != null && nextInstance.element != null) {
994
- updateDomProperties(nextInstance.domRef, nextInstance.element.props, nextInstance.beforeElement?.props);
995
- }
996
- // 插入 parent
997
- // TODO: 针对仅移动时优化
998
- if (nextInstance.domRef != null) {
999
- if (oldNode !== nextInstance.domRef) {
1000
- if (nextInstance.domRef.parentNode) {
1001
- nextInstance.domRef.parentNode.removeChild(nextInstance.domRef);
1002
- }
1003
- const parentDom = getParentDom(nextInstance);
1004
- parentDom.appendChild(nextInstance.domRef);
1005
- }
1006
- }
1007
- }
1008
- function commitWalkV2(initInstance, initNode) {
1009
- // 创建一个栈,将根节点压入栈中
1010
- const stack = [[initInstance, initNode]];
1011
- while (stack.length > 0) {
1012
- // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
1013
- const stackLayer = stack.pop();
1014
- if (typeof stackLayer === 'function') {
1015
- stackLayer();
1016
- continue;
1017
- }
1018
- const [instance, node] = stackLayer;
1019
- commitInstanceDom(instance, node);
1020
- // stack 是先入后出
1021
- // 实际上是先渲染 child
1022
- // 这里然后再渲染 sibling
1023
- // 执行生命周期的 Mount
1024
- stack.push(() => instance.context.triggerMounted());
1025
- // 更新下一个兄弟节点
1026
- if (instance.sibling != null) {
1027
- const siblingNode = instance.domRef
1028
- ? instance.domRef.nextSibling
1029
- : node?.nextSibling;
1030
- stack.push([instance.sibling, siblingNode || undefined]);
1031
- }
1032
- // 更新下一个子节点
1033
- if (instance.child != null) {
1034
- const childNode = instance.domRef
1035
- ? instance.domRef.firstChild
1036
- : node;
1037
- stack.push([instance.child, childNode || undefined]);
1038
- }
1039
- }
1040
- }
1041
- commitWalkV2(rootInstance, rootNode);
1042
- }
1043
- function onUpdateRequire() {
1044
- if (context.nextUnitOfWork == null && context.rootInstance.child) {
1045
- context.nextUnitOfWork = context.rootInstance.child;
1046
- }
1047
- }
1048
- /**
1049
- * 调度
1050
- */
1051
- function workLoop(deadline) {
1052
- let shouldYield = false;
1053
- const logger = createLogger('workLoop');
1054
- while (context.nextUnitOfWork && !shouldYield) {
1055
- logger.debug('nextUnitOfWork', context.nextUnitOfWork);
1056
- context.nextUnitOfWork = performUnitOfWork(pluginContext, context.nextUnitOfWork, onUpdateRequire);
1057
- if (context.nextUnitOfWork == null)
1058
- context.needCommitDom = true;
1059
- if (deadline)
1060
- shouldYield = deadline.timeRemaining() < 1;
1061
- }
1062
- if (context.needCommitDom && context.rootInstance.child) {
1063
- commitDom(context.rootInstance.child, context.rootInstance.domRef?.firstChild || undefined);
1064
- context.needCommitDom = false;
1065
- }
1066
- // TODO: 兼容性
1067
- requestIdleCallback(workLoop);
1068
- }
1069
- // 开始调度
1070
- workLoop();
1071
- return context.rootInstance;
1072
- }
1073
-
1074
- class BasicLogic {
1075
- logger = createLogger('basicLogicPlugin');
1076
- isSameProps(preProps, nextProps) {
1077
- if (Object.is(nextProps, preProps)) {
1078
- return true;
1079
- }
1080
- if (typeof preProps !== 'object'
1081
- || typeof nextProps !== 'object'
1082
- || preProps === null
1083
- || nextProps === null) {
1084
- this.logger.debug('props must be an object');
1085
- return false;
1086
- }
1087
- // 对应 key 的值不相同返回 false
1088
- const prevKeys = Object.keys(preProps);
1089
- for (let index = 0; index < prevKeys.length; index++) {
1090
- const key = prevKeys[index];
1091
- if (key !== 'children' && key !== 'key') {
1092
- if (!Object.hasOwn(nextProps, key))
1093
- return false;
1094
- if (!Object.is(preProps[key], nextProps[key]))
1095
- return false;
1096
- }
1097
- if (key === 'children') {
1098
- const prevChildren = preProps['children'];
1099
- const nextChildren = nextProps['children'];
1100
- // children 都是空的,则无需更新
1101
- if (prevChildren.length === 0 && nextChildren.length === 0)
1102
- return true;
1103
- // 简单比较一下 child 的引用
1104
- for (let index = 0; index < prevChildren.length; index++) {
1105
- const prevChild = prevChildren[index];
1106
- const nextChild = nextChildren[index];
1107
- if (prevChild !== nextChild)
1108
- return false;
1109
- if (typeof prevChild !== typeof nextChild)
1110
- return false;
1111
- }
1112
- }
1113
- return true;
1114
- }
1115
- return false;
1116
- }
1117
- isReRender(instance) {
1118
- const nextProps = instance.element?.props;
1119
- const preProps = instance.beforeElement?.props;
1120
- if (!this.isSameProps(preProps, nextProps))
1121
- return true;
1122
- }
1123
- updateDom(dom, nextProps, prevProps = {}) {
1124
- this.logger.debug('updateDom', dom, nextProps, prevProps);
1125
- const isKey = (key) => key === 'key';
1126
- const isRef = (key) => key === 'ref';
1127
- const isStyle = (key) => key === 'style';
1128
- const isClass = (key) => key === 'class';
1129
- const isEvent = (key) => key.startsWith("on");
1130
- const isChildren = (key) => key === 'children';
1131
- const isGone = (_prev, next) => (key) => !(key in next);
1132
- const isNew = (prev, next) => (key) => prev[key] !== next[key];
1133
- const isProperty = (key) => !isChildren(key) && !isEvent(key) && !isStyle(key) && !isClass(key) && !isKey(key) && !isRef(key);
1134
- // https://developer.mozilla.org/zh-CN/docs/Web/API/Node
1135
- if (dom.nodeName === '#text' || dom.nodeName === '#comment') {
1136
- const textNode = dom;
1137
- if (textNode.nodeValue !== nextProps.textContent) {
1138
- textNode.nodeValue = String(nextProps.textContent);
1139
- }
1140
- return;
1141
- }
1142
- // remove old style
1143
- const oldStyle = prevProps?.style;
1144
- if (typeof oldStyle === 'object' && oldStyle != null) {
1145
- if (!('style' in dom) || !dom.style)
1146
- return;
1147
- Object.keys(oldStyle).forEach(key => {
1148
- /* eslint-disable @typescript-eslint/no-explicit-any */
1149
- if ('style' in dom && dom.style) {
1150
- delete dom.style[key];
1151
- /* eslint-enable @typescript-eslint/no-explicit-any */
1152
- }
1153
- });
1154
- }
1155
- // add new style
1156
- const newStyle = nextProps?.style;
1157
- if (typeof newStyle === 'object' && newStyle != null) {
1158
- if (!('style' in dom) || !dom.style)
1159
- return;
1160
- Object.keys(newStyle).forEach((key) => {
1161
- /* eslint-disable @typescript-eslint/no-explicit-any */
1162
- const value = newStyle?.[key];
1163
- dom.style[key] = value == null ? '' : value;
1164
- /* eslint-enable @typescript-eslint/no-explicit-any */
1165
- });
1166
- }
1167
- if (dom.className !== nextProps?.class) {
1168
- if (!nextProps?.class) {
1169
- dom.removeAttribute('class');
1170
- }
1171
- else if (typeof nextProps?.class === 'string') {
1172
- dom.setAttribute('class', nextProps?.class);
1173
- }
1174
- }
1175
- //Remove old or changed event listeners
1176
- Object.keys(prevProps)
1177
- .filter(isEvent)
1178
- .filter(key => !(key in nextProps) ||
1179
- isNew(prevProps, nextProps)(key))
1180
- .forEach(name => {
1181
- const eventType = name
1182
- .toLowerCase()
1183
- .substring(2);
1184
- if (prevProps[name] == null)
1185
- return;
1186
- if (typeof prevProps[name] !== 'function') {
1187
- console.error('EventListener is not a function');
1188
- }
1189
- else {
1190
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1191
- dom.removeEventListener(eventType, prevProps[name]);
1192
- }
1193
- });
1194
- // Add event listeners
1195
- Object.keys(nextProps)
1196
- .filter(isEvent)
1197
- .filter(isNew(prevProps, nextProps))
1198
- .forEach(name => {
1199
- const eventType = name
1200
- .toLowerCase()
1201
- .substring(2);
1202
- if (nextProps[name] == null)
1203
- return;
1204
- if (typeof nextProps[name] !== 'function') {
1205
- console.error('EventListener is not a function');
1206
- }
1207
- else {
1208
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1209
- dom.addEventListener(eventType, nextProps[name]);
1210
- }
1211
- });
1212
- // Remove old properties
1213
- Object.keys(prevProps)
1214
- .filter(isProperty)
1215
- .filter(isGone(prevProps, nextProps))
1216
- .forEach(name => dom.removeAttribute(name));
1217
- // Set new or changed properties
1218
- Object.keys(nextProps)
1219
- .filter(isProperty)
1220
- .filter(isNew(prevProps, nextProps))
1221
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1222
- .forEach(name => dom.setAttribute(name, nextProps[name]));
1223
- }
1224
- isReuseInstance(instance, nextElement) {
1225
- if (instance && instance.element && instance.element.type !== nextElement.type) {
1226
- return false;
1227
- }
1228
- }
1229
- }
1230
-
1231
- class InjectSystem {
1232
- getProvideValueForParent(instance, key) {
1233
- if (instance && instance.context) {
1234
- const value = instance.context.providedMap.get(key);
1235
- if (value != undefined)
1236
- return value;
1237
- if (instance.parent) {
1238
- return this.getProvideValueForParent(instance.parent, key);
1239
- }
1240
- }
1241
- return undefined;
1242
- }
1243
- isReuseInstance(instance) {
1244
- const injectedKeys = [...instance.context.injectedMap.keys()];
1245
- for (let index = 0; index < injectedKeys.length; index++) {
1246
- const key = injectedKeys[index];
1247
- const currentValue = instance.context.injectedMap.get(key);
1248
- const parentValue = this.getProvideValueForParent(instance.parent, key);
1249
- // 如果发现任何值发生变化,则重建实例
1250
- if (parentValue !== currentValue)
1251
- return false;
1252
- }
1253
- }
1254
- }
1255
-
1256
- class PluginContext {
1257
- plugins = [
1258
- new BasicLogic(),
1259
- new InjectSystem()
1260
- ];
1261
- registerPlugin(...plugins) {
1262
- this.plugins.push(...plugins);
1263
- }
1264
- }
1265
-
1266
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
1267
- function createApp(element) {
1268
- const appContext = new PluginContext();
1269
- const ensureAsElement = (element) => {
1270
- if (typeof element === 'function') {
1271
- return createElement(element, {});
1272
- }
1273
- return element;
1274
- };
1275
- const app = {
1276
- plugin: (...plugins) => {
1277
- appContext.registerPlugin(...plugins);
1278
- return app;
1279
- },
1280
- mount: (container) => {
1281
- container.innerHTML = ''; // 先清空再说
1282
- render(appContext, ensureAsElement(element), container);
1283
- return app;
1284
- },
1285
- renderToHTML: () => {
1286
- return new Promise(resolve => {
1287
- render$1(appContext, ensureAsElement(element), resolve);
1288
- });
1289
- }
1290
- };
1291
- return app;
1292
- }
1293
-
1294
- exports.Fragment = Fragment;
1295
- exports.component = component;
1296
- exports.createApp = createApp;
1297
- exports.createElement = createElement;
1298
- exports.inject = inject;
1299
- exports.onMounted = onMounted;
1300
- exports.onUnmounted = onUnmounted;
1301
- exports.provide = provide;
1302
-
1303
- }));
1304
- //# sourceMappingURL=index.js.map