@viewfly/core 0.0.1-alpha.1 → 0.0.1-alpha.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bundles/foundation/_utils.d.ts +3 -19
- package/bundles/foundation/injection-tokens.d.ts +1 -2
- package/bundles/foundation/renderer.d.ts +3 -3
- package/bundles/index.esm.js +596 -575
- package/bundles/index.js +598 -576
- package/bundles/model/component.d.ts +48 -24
- package/bundles/model/jsx-element.d.ts +21 -29
- package/bundles/model/root.component.d.ts +2 -1
- package/bundles/public-api.d.ts +1 -0
- package/bundles/viewfly.d.ts +6 -4
- package/package.json +4 -3
package/bundles/index.esm.js
CHANGED
|
@@ -3,6 +3,15 @@ import { ReflectiveInjector, normalizeProvider, NullInjector, Injectable } from
|
|
|
3
3
|
export * from '@tanbo/di';
|
|
4
4
|
import { Subject, Subscription, microTask } from '@tanbo/stream';
|
|
5
5
|
|
|
6
|
+
function makeError(name) {
|
|
7
|
+
return function viewflyError(message) {
|
|
8
|
+
const error = new Error(message);
|
|
9
|
+
error.name = `[ViewflyError: ${name}]`;
|
|
10
|
+
error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
|
|
11
|
+
return error;
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
class NativeRenderer {
|
|
7
16
|
}
|
|
8
17
|
|
|
@@ -34,163 +43,30 @@ function __metadata(metadataKey, metadataValue) {
|
|
|
34
43
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
35
44
|
}
|
|
36
45
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
class JSXFragment {
|
|
41
|
-
constructor(props) {
|
|
42
|
-
this.props = props;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
function jsx(setup, config) {
|
|
46
|
-
if (typeof setup === 'string') {
|
|
47
|
-
return new JSXElement(setup, config);
|
|
48
|
-
}
|
|
49
|
-
return function (context) {
|
|
50
|
-
return new Component(context, setup, config);
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
function jsxs(setup, config) {
|
|
54
|
-
if (typeof setup === 'string') {
|
|
55
|
-
return new JSXElement(setup, config);
|
|
56
|
-
}
|
|
57
|
-
return function (context) {
|
|
58
|
-
return new Component(context, setup, config);
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
class JSXText {
|
|
62
|
-
constructor(text) {
|
|
63
|
-
this.text = text;
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
function flatChildren(jsxNodes) {
|
|
67
|
-
const children = [];
|
|
68
|
-
for (const node of jsxNodes) {
|
|
69
|
-
if (node instanceof JSXElement || typeof node === 'function') {
|
|
70
|
-
children.push(node);
|
|
71
|
-
}
|
|
72
|
-
else if (typeof node === 'string' && node.length) {
|
|
73
|
-
children.push(new JSXText(node));
|
|
74
|
-
}
|
|
75
|
-
else if (Array.isArray(node)) {
|
|
76
|
-
children.push(...flatChildren(node));
|
|
77
|
-
}
|
|
78
|
-
else if (node !== null && typeof node !== 'undefined') {
|
|
79
|
-
children.push(new JSXText(String(node)));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
return children;
|
|
83
|
-
}
|
|
84
|
-
class Props {
|
|
85
|
-
constructor(props) {
|
|
86
|
-
this.attrs = new Map();
|
|
87
|
-
this.styles = new Map();
|
|
88
|
-
this.classes = new Set();
|
|
89
|
-
this.listeners = {};
|
|
90
|
-
this.children = [];
|
|
91
|
-
if (!props) {
|
|
92
|
-
return;
|
|
93
|
-
}
|
|
94
|
-
Object.keys(props).forEach(key => {
|
|
95
|
-
if (key === 'children') {
|
|
96
|
-
if (props.children !== null && typeof props.children !== 'undefined') {
|
|
97
|
-
if (Array.isArray(props.children)) {
|
|
98
|
-
this.children = flatChildren(props.children);
|
|
99
|
-
}
|
|
100
|
-
else {
|
|
101
|
-
this.children = flatChildren([props.children]);
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
return;
|
|
105
|
-
}
|
|
106
|
-
if (key === 'class') {
|
|
107
|
-
this.classes = new Set(Props.classToArray(props[key]));
|
|
108
|
-
return;
|
|
109
|
-
}
|
|
110
|
-
if (key === 'style') {
|
|
111
|
-
const style = props.style || '';
|
|
112
|
-
if (typeof style === 'string') {
|
|
113
|
-
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
114
|
-
if (!v[0] || !v[1]) {
|
|
115
|
-
return;
|
|
116
|
-
}
|
|
117
|
-
this.styles.set(v[0].trim(), v[1].trim());
|
|
118
|
-
});
|
|
119
|
-
}
|
|
120
|
-
else if (typeof style === 'object') {
|
|
121
|
-
Object.keys(style).forEach(key => {
|
|
122
|
-
this.styles.set(key, style[key]);
|
|
123
|
-
});
|
|
124
|
-
}
|
|
125
|
-
return;
|
|
126
|
-
}
|
|
127
|
-
if (/^on[A-Z]/.test(key)) {
|
|
128
|
-
const listener = props[key];
|
|
129
|
-
if (typeof listener === 'function') {
|
|
130
|
-
this.listeners[key.replace(/^on/, '').toLowerCase()] = listener;
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
this.attrs.set(key, listener);
|
|
134
|
-
}
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
this.attrs.set(key, props[key]);
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
static classToArray(config) {
|
|
141
|
-
const classes = [];
|
|
142
|
-
if (!config) {
|
|
143
|
-
return classes;
|
|
144
|
-
}
|
|
145
|
-
if (typeof config === 'string') {
|
|
146
|
-
const items = config.match(/\S+/g);
|
|
147
|
-
return items || classes;
|
|
148
|
-
}
|
|
149
|
-
else if (Array.isArray(config)) {
|
|
150
|
-
for (const i of config) {
|
|
151
|
-
classes.push(...Props.classToArray(i));
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
else if (typeof config === 'object') {
|
|
155
|
-
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
156
|
-
classes.push(config.toString());
|
|
157
|
-
return classes;
|
|
158
|
-
}
|
|
159
|
-
for (const key in config) {
|
|
160
|
-
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
161
|
-
classes.push(key);
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
return classes;
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
class JSXElement {
|
|
169
|
-
constructor(name, config) {
|
|
170
|
-
this.name = name;
|
|
171
|
-
this.config = config;
|
|
172
|
-
this.props = new Props(config);
|
|
173
|
-
}
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function makeError(name) {
|
|
177
|
-
return function viewflyError(message) {
|
|
178
|
-
const error = new Error(message);
|
|
179
|
-
error.name = `[ViewflyError: ${name}]`;
|
|
180
|
-
error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
|
|
181
|
-
return error;
|
|
182
|
-
};
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
const componentStack = [];
|
|
46
|
+
const componentSetupStack = [];
|
|
47
|
+
const componentRendingStack = [];
|
|
48
|
+
const derivedStack = [];
|
|
186
49
|
const componentErrorFn = makeError('component');
|
|
187
|
-
function
|
|
188
|
-
const current =
|
|
50
|
+
function getSetupContext(need = true) {
|
|
51
|
+
const current = componentSetupStack[componentSetupStack.length - 1];
|
|
189
52
|
if (!current && need) {
|
|
53
|
+
// 防止因外部捕获异常引引起的缓存未清理的问题
|
|
54
|
+
componentRendingStack.pop();
|
|
190
55
|
throw componentErrorFn('cannot be called outside the component!');
|
|
191
56
|
}
|
|
192
57
|
return current;
|
|
193
58
|
}
|
|
59
|
+
function getRendingContext() {
|
|
60
|
+
return componentRendingStack[componentRendingStack.length - 1];
|
|
61
|
+
}
|
|
62
|
+
function getDerivedContext() {
|
|
63
|
+
return derivedStack[derivedStack.length - 1];
|
|
64
|
+
}
|
|
65
|
+
class JSXComponent {
|
|
66
|
+
constructor(createInstance) {
|
|
67
|
+
this.createInstance = createInstance;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
194
70
|
/**
|
|
195
71
|
* Viewfly 组件管理类,用于管理组件的生命周期,上下文等
|
|
196
72
|
*/
|
|
@@ -201,10 +77,12 @@ class Component extends ReflectiveInjector {
|
|
|
201
77
|
get changed() {
|
|
202
78
|
return this._changed;
|
|
203
79
|
}
|
|
204
|
-
constructor(context, setup,
|
|
80
|
+
constructor(context, setup, props, key) {
|
|
205
81
|
super(context, []);
|
|
206
82
|
this.setup = setup;
|
|
207
|
-
this.
|
|
83
|
+
this.props = props;
|
|
84
|
+
this.key = key;
|
|
85
|
+
this.$$typeOf = this.setup;
|
|
208
86
|
this.destroyCallbacks = [];
|
|
209
87
|
this.mountCallbacks = [];
|
|
210
88
|
this.propsChangedCallbacks = [];
|
|
@@ -214,9 +92,11 @@ class Component extends ReflectiveInjector {
|
|
|
214
92
|
this.updatedDestroyCallbacks = [];
|
|
215
93
|
this.propsChangedDestroyCallbacks = [];
|
|
216
94
|
this.isFirstRending = true;
|
|
217
|
-
this.props = new Props(config);
|
|
218
95
|
this.parentComponent = this.parentInjector;
|
|
219
96
|
}
|
|
97
|
+
is(target) {
|
|
98
|
+
return target.$$typeOf === this.$$typeOf;
|
|
99
|
+
}
|
|
220
100
|
addProvide(providers) {
|
|
221
101
|
providers = Array.isArray(providers) ? providers : [providers];
|
|
222
102
|
providers.forEach(p => {
|
|
@@ -224,27 +104,42 @@ class Component extends ReflectiveInjector {
|
|
|
224
104
|
});
|
|
225
105
|
}
|
|
226
106
|
init() {
|
|
227
|
-
componentStack.push(this);
|
|
228
107
|
const self = this;
|
|
229
|
-
const props = new Proxy(
|
|
108
|
+
const props = new Proxy(this.props, {
|
|
230
109
|
get(_, key) {
|
|
231
|
-
if (self.
|
|
232
|
-
return self.
|
|
110
|
+
if (self.props) {
|
|
111
|
+
return self.props[key];
|
|
233
112
|
}
|
|
234
113
|
},
|
|
235
114
|
set() {
|
|
115
|
+
// 防止因外部捕获异常引引起的缓存未清理的问题
|
|
116
|
+
if (isSetup) {
|
|
117
|
+
componentSetupStack.pop();
|
|
118
|
+
}
|
|
119
|
+
if (isRending) {
|
|
120
|
+
componentRendingStack.pop();
|
|
121
|
+
}
|
|
236
122
|
throw componentErrorFn('component props is readonly!');
|
|
237
123
|
}
|
|
238
124
|
});
|
|
125
|
+
componentSetupStack.push(this);
|
|
126
|
+
let isSetup = true;
|
|
239
127
|
const render = this.setup(props);
|
|
128
|
+
isSetup = false;
|
|
129
|
+
componentSetupStack.pop();
|
|
130
|
+
componentRendingStack.push(this);
|
|
131
|
+
let isRending = true;
|
|
240
132
|
const template = render();
|
|
241
|
-
|
|
133
|
+
isRending = false;
|
|
134
|
+
componentRendingStack.pop();
|
|
242
135
|
return {
|
|
243
136
|
template,
|
|
244
137
|
render: () => {
|
|
245
|
-
|
|
138
|
+
componentRendingStack.push(this);
|
|
139
|
+
isRending = true;
|
|
246
140
|
const template = render();
|
|
247
|
-
|
|
141
|
+
isRending = false;
|
|
142
|
+
componentRendingStack.pop();
|
|
248
143
|
return template;
|
|
249
144
|
}
|
|
250
145
|
};
|
|
@@ -270,8 +165,8 @@ class Component extends ReflectiveInjector {
|
|
|
270
165
|
}
|
|
271
166
|
}
|
|
272
167
|
invokePropsChangedHooks(newProps) {
|
|
273
|
-
const oldProps = this.
|
|
274
|
-
this.
|
|
168
|
+
const oldProps = this.props;
|
|
169
|
+
this.props = newProps;
|
|
275
170
|
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
276
171
|
fn();
|
|
277
172
|
});
|
|
@@ -333,7 +228,7 @@ class Component extends ReflectiveInjector {
|
|
|
333
228
|
* ```
|
|
334
229
|
*/
|
|
335
230
|
function onMount(callback) {
|
|
336
|
-
const component =
|
|
231
|
+
const component = getSetupContext();
|
|
337
232
|
component.mountCallbacks.push(callback);
|
|
338
233
|
}
|
|
339
234
|
/**
|
|
@@ -352,7 +247,7 @@ function onMount(callback) {
|
|
|
352
247
|
* ```
|
|
353
248
|
*/
|
|
354
249
|
function onUpdated(callback) {
|
|
355
|
-
const component =
|
|
250
|
+
const component = getSetupContext();
|
|
356
251
|
component.updatedCallbacks.push(callback);
|
|
357
252
|
return () => {
|
|
358
253
|
const index = component.updatedCallbacks.indexOf(callback);
|
|
@@ -381,7 +276,7 @@ function onUpdated(callback) {
|
|
|
381
276
|
* ```
|
|
382
277
|
*/
|
|
383
278
|
function onPropsChanged(callback) {
|
|
384
|
-
const component =
|
|
279
|
+
const component = getSetupContext();
|
|
385
280
|
component.propsChangedCallbacks.push(callback);
|
|
386
281
|
return () => {
|
|
387
282
|
const index = component.propsChangedCallbacks.indexOf(callback);
|
|
@@ -395,31 +290,34 @@ function onPropsChanged(callback) {
|
|
|
395
290
|
* @param callback
|
|
396
291
|
*/
|
|
397
292
|
function onDestroy(callback) {
|
|
398
|
-
const component =
|
|
293
|
+
const component = getSetupContext();
|
|
399
294
|
component.destroyCallbacks.push(callback);
|
|
400
295
|
}
|
|
401
296
|
class Ref {
|
|
402
|
-
|
|
403
|
-
constructor(callback, component) {
|
|
297
|
+
constructor(callback) {
|
|
404
298
|
this.callback = callback;
|
|
405
|
-
this.
|
|
406
|
-
this.
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
299
|
+
this.unBindMap = new WeakMap();
|
|
300
|
+
this.targetCaches = new Set();
|
|
301
|
+
}
|
|
302
|
+
bind(value) {
|
|
303
|
+
if (typeof value !== 'object' || value === null) {
|
|
304
|
+
return;
|
|
305
|
+
}
|
|
306
|
+
if (this.targetCaches.has(value)) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
const unBindFn = this.callback(value);
|
|
310
|
+
if (typeof unBindFn === 'function') {
|
|
311
|
+
this.unBindMap.set(value, unBindFn);
|
|
312
|
+
}
|
|
313
|
+
this.targetCaches.add(value);
|
|
410
314
|
}
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
this.unListenFn = this.callback(value) || null;
|
|
418
|
-
}
|
|
419
|
-
unListen() {
|
|
420
|
-
if (typeof this.unListenFn === 'function') {
|
|
421
|
-
this.unListenFn();
|
|
422
|
-
this.unListenFn = null;
|
|
315
|
+
unBind(value) {
|
|
316
|
+
this.targetCaches.delete(value);
|
|
317
|
+
const unBindFn = this.unBindMap.get(value);
|
|
318
|
+
this.unBindMap.delete(value);
|
|
319
|
+
if (typeof unBindFn === 'function') {
|
|
320
|
+
unBindFn();
|
|
423
321
|
}
|
|
424
322
|
}
|
|
425
323
|
}
|
|
@@ -445,8 +343,7 @@ class Ref {
|
|
|
445
343
|
* ```
|
|
446
344
|
*/
|
|
447
345
|
function useRef(callback) {
|
|
448
|
-
|
|
449
|
-
return new Ref(callback, component);
|
|
346
|
+
return new Ref(callback);
|
|
450
347
|
}
|
|
451
348
|
const depsKey = Symbol('deps');
|
|
452
349
|
/**
|
|
@@ -474,8 +371,12 @@ const depsKey = Symbol('deps');
|
|
|
474
371
|
*/
|
|
475
372
|
function useSignal(state) {
|
|
476
373
|
const usedComponents = new Set();
|
|
477
|
-
function
|
|
478
|
-
const component =
|
|
374
|
+
function signal() {
|
|
375
|
+
const component = getRendingContext();
|
|
376
|
+
const derivedContext = getDerivedContext();
|
|
377
|
+
if (derivedContext) {
|
|
378
|
+
derivedContext.push(signal);
|
|
379
|
+
}
|
|
479
380
|
if (component && !usedComponents.has(component)) {
|
|
480
381
|
usedComponents.add(component);
|
|
481
382
|
component.destroyCallbacks.push(() => {
|
|
@@ -484,44 +385,67 @@ function useSignal(state) {
|
|
|
484
385
|
}
|
|
485
386
|
return state;
|
|
486
387
|
}
|
|
487
|
-
|
|
488
|
-
if (
|
|
489
|
-
newState = newState(state);
|
|
490
|
-
}
|
|
491
|
-
else if (newState === state) {
|
|
388
|
+
signal.set = function (newState) {
|
|
389
|
+
if (newState === state) {
|
|
492
390
|
return;
|
|
493
391
|
}
|
|
494
392
|
state = newState;
|
|
495
393
|
for (const component of usedComponents) {
|
|
496
394
|
component.markAsDirtied();
|
|
497
395
|
}
|
|
498
|
-
for (const fn of
|
|
396
|
+
for (const fn of signal[depsKey]) {
|
|
499
397
|
fn();
|
|
500
398
|
}
|
|
501
399
|
};
|
|
502
|
-
|
|
503
|
-
return
|
|
400
|
+
signal[depsKey] = new Set();
|
|
401
|
+
return signal;
|
|
504
402
|
}
|
|
505
403
|
/**
|
|
506
|
-
*
|
|
507
|
-
*
|
|
508
|
-
*
|
|
509
|
-
* @param
|
|
510
|
-
* @param
|
|
404
|
+
* 使用派生值,Viewfly 会收集回调函数内同步执行时访问的 Signal,
|
|
405
|
+
* 并在你获取 useDerived 函数返回的 Signal 的值时,自动计算最新的值。
|
|
406
|
+
*
|
|
407
|
+
* @param callback
|
|
408
|
+
* @param isContinue 可选的停止函数,在每次值更新后调用,当返回值为 false 时,将不再监听依赖的变化
|
|
511
409
|
*/
|
|
410
|
+
function useDerived(callback, isContinue) {
|
|
411
|
+
const deps = [];
|
|
412
|
+
derivedStack.push(deps);
|
|
413
|
+
const data = callback();
|
|
414
|
+
derivedStack.pop();
|
|
415
|
+
const signal = useSignal(data);
|
|
416
|
+
if (deps.length) {
|
|
417
|
+
const unListen = useEffect(deps, () => {
|
|
418
|
+
const data = callback();
|
|
419
|
+
signal.set(data);
|
|
420
|
+
if (typeof isContinue === 'function' && !isContinue(data)) {
|
|
421
|
+
unListen();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
return signal;
|
|
426
|
+
}
|
|
427
|
+
/* eslint-enable max-len*/
|
|
512
428
|
function useEffect(deps, effect) {
|
|
429
|
+
if (typeof deps === 'function' &&
|
|
430
|
+
typeof deps.set === 'undefined' &&
|
|
431
|
+
typeof deps[depsKey] === 'undefined') {
|
|
432
|
+
deps = useDerived(deps);
|
|
433
|
+
}
|
|
513
434
|
const signals = Array.isArray(deps) ? deps : [deps];
|
|
435
|
+
let oldValues = signals.map(s => s());
|
|
514
436
|
let prevCleanup;
|
|
515
437
|
function effectCallback() {
|
|
516
438
|
if (typeof prevCleanup === 'function') {
|
|
517
439
|
prevCleanup();
|
|
518
440
|
}
|
|
519
|
-
|
|
441
|
+
const newValues = signals.map(s => s());
|
|
442
|
+
prevCleanup = Array.isArray(deps) ? effect(newValues, oldValues) : effect(newValues[0], oldValues[0]);
|
|
443
|
+
oldValues = newValues;
|
|
520
444
|
}
|
|
521
445
|
for (const dep of signals) {
|
|
522
446
|
dep[depsKey].add(effectCallback);
|
|
523
447
|
}
|
|
524
|
-
const component =
|
|
448
|
+
const component = getSetupContext(false);
|
|
525
449
|
let isClean = false;
|
|
526
450
|
const destroyFn = () => {
|
|
527
451
|
if (isClean) {
|
|
@@ -546,7 +470,7 @@ function useEffect(deps, effect) {
|
|
|
546
470
|
* @param provider
|
|
547
471
|
*/
|
|
548
472
|
function provide(provider) {
|
|
549
|
-
const component =
|
|
473
|
+
const component = getSetupContext();
|
|
550
474
|
component.addProvide(provider);
|
|
551
475
|
return component;
|
|
552
476
|
}
|
|
@@ -554,16 +478,57 @@ function provide(provider) {
|
|
|
554
478
|
* 通过组件上下文获取 IoC 容器内数据的勾子方法
|
|
555
479
|
*/
|
|
556
480
|
function inject(token, notFoundValue, flags) {
|
|
557
|
-
const component =
|
|
481
|
+
const component = getSetupContext();
|
|
558
482
|
return component.parentInjector.get(token, notFoundValue, flags);
|
|
559
483
|
}
|
|
560
484
|
|
|
485
|
+
const jsxErrorFn = makeError('JSX');
|
|
486
|
+
const Fragment = function Fragment() {
|
|
487
|
+
throw jsxErrorFn('Fragment does not support calling.');
|
|
488
|
+
};
|
|
489
|
+
function jsx(setup, config, key) {
|
|
490
|
+
if (typeof setup === 'string') {
|
|
491
|
+
return new JSXElement(setup, config, key);
|
|
492
|
+
}
|
|
493
|
+
return new JSXComponent(function (context) {
|
|
494
|
+
return new Component(context, setup, config, key);
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
function jsxs(setup, config, key) {
|
|
498
|
+
if (typeof setup === 'string') {
|
|
499
|
+
return new JSXElement(setup, config, key);
|
|
500
|
+
}
|
|
501
|
+
return new JSXComponent(function (context) {
|
|
502
|
+
return new Component(context, setup, config, key);
|
|
503
|
+
});
|
|
504
|
+
}
|
|
505
|
+
class JSXText {
|
|
506
|
+
constructor(text) {
|
|
507
|
+
this.text = text;
|
|
508
|
+
this.$$typeOf = '#text';
|
|
509
|
+
}
|
|
510
|
+
is(target) {
|
|
511
|
+
return target.$$typeOf === this.$$typeOf;
|
|
512
|
+
}
|
|
513
|
+
}
|
|
514
|
+
class JSXElement {
|
|
515
|
+
constructor(name, props, key) {
|
|
516
|
+
this.name = name;
|
|
517
|
+
this.props = props;
|
|
518
|
+
this.key = key;
|
|
519
|
+
this.$$typeOf = this.name;
|
|
520
|
+
}
|
|
521
|
+
is(target) {
|
|
522
|
+
return target.$$typeOf === this.$$typeOf;
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
|
|
561
526
|
/**
|
|
562
527
|
* Viewfly 根组件,用于实现组件状态更新事件通知
|
|
563
528
|
*/
|
|
564
529
|
class RootComponent extends Component {
|
|
565
|
-
constructor(factory) {
|
|
566
|
-
super(
|
|
530
|
+
constructor(factory, parentInjector = new NullInjector()) {
|
|
531
|
+
super(parentInjector, factory, {});
|
|
567
532
|
this.changeEmitter = new Subject();
|
|
568
533
|
}
|
|
569
534
|
markAsChanged() {
|
|
@@ -573,142 +538,68 @@ class RootComponent extends Component {
|
|
|
573
538
|
}
|
|
574
539
|
|
|
575
540
|
const refKey = 'ref';
|
|
576
|
-
function getObjectChanges(
|
|
541
|
+
function getObjectChanges(newProps, oldProps) {
|
|
577
542
|
const changes = {
|
|
578
543
|
remove: [],
|
|
579
|
-
add: []
|
|
544
|
+
add: [],
|
|
545
|
+
replace: []
|
|
580
546
|
};
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
// }
|
|
587
|
-
// return changes
|
|
588
|
-
// }
|
|
589
|
-
//
|
|
590
|
-
// if (!source) {
|
|
591
|
-
// Object.keys(target).forEach(key => {
|
|
592
|
-
// changes.add.push([key, target[key]])
|
|
593
|
-
// })
|
|
594
|
-
// return changes
|
|
595
|
-
// }
|
|
596
|
-
Object.keys(target).forEach(key => {
|
|
597
|
-
const leftValue = target[key];
|
|
598
|
-
if (!Reflect.has(source, key)) {
|
|
599
|
-
changes.add.push([key, leftValue]);
|
|
600
|
-
return;
|
|
547
|
+
if (!newProps) {
|
|
548
|
+
if (oldProps) {
|
|
549
|
+
Object.keys(oldProps).forEach(key => {
|
|
550
|
+
changes.remove.push([key, oldProps[key]]);
|
|
551
|
+
});
|
|
601
552
|
}
|
|
602
|
-
|
|
603
|
-
|
|
553
|
+
return changes;
|
|
554
|
+
}
|
|
555
|
+
if (!oldProps) {
|
|
556
|
+
Object.keys(newProps).forEach(key => {
|
|
557
|
+
changes.add.push([key, newProps[key]]);
|
|
558
|
+
});
|
|
559
|
+
return changes;
|
|
560
|
+
}
|
|
561
|
+
Object.keys(newProps).forEach(key => {
|
|
562
|
+
const leftValue = newProps[key];
|
|
563
|
+
const rightValue = oldProps[key];
|
|
564
|
+
if (Reflect.has(oldProps, key)) {
|
|
565
|
+
if (leftValue !== rightValue) {
|
|
566
|
+
changes.replace.push([key, leftValue, rightValue]);
|
|
567
|
+
}
|
|
604
568
|
return;
|
|
605
569
|
}
|
|
606
570
|
changes.add.push([key, leftValue]);
|
|
607
|
-
changes.remove.push([key, rightValue]);
|
|
608
571
|
});
|
|
609
|
-
Object.keys(
|
|
610
|
-
if (!Reflect.has(
|
|
611
|
-
changes.remove.push([key,
|
|
572
|
+
Object.keys(oldProps).forEach(key => {
|
|
573
|
+
if (!Reflect.has(newProps, key)) {
|
|
574
|
+
changes.remove.push([key, oldProps[key]]);
|
|
612
575
|
}
|
|
613
576
|
});
|
|
614
577
|
return changes;
|
|
615
578
|
}
|
|
616
|
-
function
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
// changes.set.push([key, value])
|
|
632
|
-
// })
|
|
633
|
-
// return changes
|
|
634
|
-
// }
|
|
635
|
-
target.forEach((value, key) => {
|
|
636
|
-
const rightValue = source.get(key);
|
|
637
|
-
if (value === rightValue) {
|
|
638
|
-
return;
|
|
579
|
+
function classToString(config) {
|
|
580
|
+
if (!config) {
|
|
581
|
+
return '';
|
|
582
|
+
}
|
|
583
|
+
if (typeof config === 'string') {
|
|
584
|
+
return config;
|
|
585
|
+
}
|
|
586
|
+
else if (Array.isArray(config)) {
|
|
587
|
+
return config.map(i => {
|
|
588
|
+
return classToString(i);
|
|
589
|
+
}).join(' ');
|
|
590
|
+
}
|
|
591
|
+
else if (typeof config === 'object') {
|
|
592
|
+
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
593
|
+
return config.toString();
|
|
639
594
|
}
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
const newValue = target.get(key);
|
|
645
|
-
if (value !== newValue) {
|
|
646
|
-
changes.remove.push([key, value]);
|
|
595
|
+
const classes = [];
|
|
596
|
+
for (const key in config) {
|
|
597
|
+
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
598
|
+
classes.push(key);
|
|
647
599
|
}
|
|
648
|
-
return;
|
|
649
|
-
}
|
|
650
|
-
if (!target.has(key)) {
|
|
651
|
-
changes.remove.push([key, value]);
|
|
652
|
-
}
|
|
653
|
-
});
|
|
654
|
-
return changes;
|
|
655
|
-
}
|
|
656
|
-
function getSetChanges(target, source) {
|
|
657
|
-
const changes = {
|
|
658
|
-
add: [],
|
|
659
|
-
remove: []
|
|
660
|
-
};
|
|
661
|
-
// if (!target) {
|
|
662
|
-
// if (source) {
|
|
663
|
-
// source.forEach(i => {
|
|
664
|
-
// changes.remove.push(i)
|
|
665
|
-
// })
|
|
666
|
-
// }
|
|
667
|
-
// return changes
|
|
668
|
-
// }
|
|
669
|
-
//
|
|
670
|
-
// if (!source) {
|
|
671
|
-
// target.forEach(i => {
|
|
672
|
-
// changes.add.push(i)
|
|
673
|
-
// })
|
|
674
|
-
// return changes
|
|
675
|
-
// }
|
|
676
|
-
target.forEach(i => {
|
|
677
|
-
if (!source.has(i)) {
|
|
678
|
-
changes.add.push(i);
|
|
679
600
|
}
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
if (!target.has(i)) {
|
|
683
|
-
changes.remove.push(i);
|
|
684
|
-
}
|
|
685
|
-
});
|
|
686
|
-
return changes;
|
|
687
|
-
}
|
|
688
|
-
const compareText = '0'.repeat(8);
|
|
689
|
-
function getNodeChanges(newVNode, oldVNode) {
|
|
690
|
-
const newProps = newVNode.props;
|
|
691
|
-
const oldProps = oldVNode.props;
|
|
692
|
-
const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
|
|
693
|
-
const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
|
|
694
|
-
const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
|
|
695
|
-
const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
|
|
696
|
-
return {
|
|
697
|
-
styleChanges,
|
|
698
|
-
attrChanges,
|
|
699
|
-
classesChanges,
|
|
700
|
-
listenerChanges,
|
|
701
|
-
isChanged: [
|
|
702
|
-
attrChanges.set.length,
|
|
703
|
-
attrChanges.remove.length,
|
|
704
|
-
styleChanges.set.length,
|
|
705
|
-
styleChanges.remove.length,
|
|
706
|
-
classesChanges.add.length,
|
|
707
|
-
classesChanges.remove.length,
|
|
708
|
-
listenerChanges.add.length,
|
|
709
|
-
listenerChanges.remove.length
|
|
710
|
-
].join('') !== compareText
|
|
711
|
-
};
|
|
601
|
+
return classes.join(' ');
|
|
602
|
+
}
|
|
712
603
|
}
|
|
713
604
|
|
|
714
605
|
class RootComponentRef {
|
|
@@ -730,10 +621,10 @@ let Renderer = class Renderer {
|
|
|
730
621
|
}
|
|
731
622
|
render() {
|
|
732
623
|
const { component, host } = this.rootComponentRef;
|
|
733
|
-
const
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
624
|
+
const atom = new Atom(component, null);
|
|
625
|
+
this.buildView(atom, {
|
|
626
|
+
isParent: true,
|
|
627
|
+
host
|
|
737
628
|
});
|
|
738
629
|
}
|
|
739
630
|
refresh() {
|
|
@@ -751,6 +642,37 @@ let Renderer = class Renderer {
|
|
|
751
642
|
const atom = this.componentAtomCaches.get(component).atom.child;
|
|
752
643
|
this.reconcileElement(atom, context);
|
|
753
644
|
}
|
|
645
|
+
else {
|
|
646
|
+
const prevSibling = this.getPrevSibling(component);
|
|
647
|
+
if (prevSibling) {
|
|
648
|
+
context.isParent = false;
|
|
649
|
+
context.host = prevSibling;
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
getPrevSibling(component) {
|
|
654
|
+
let atom = this.componentAtomCaches.get(component).atom.child;
|
|
655
|
+
const childAtoms = [];
|
|
656
|
+
while (atom) {
|
|
657
|
+
childAtoms.push(atom);
|
|
658
|
+
atom = atom.sibling;
|
|
659
|
+
}
|
|
660
|
+
const components = [];
|
|
661
|
+
while (childAtoms.length) {
|
|
662
|
+
const last = childAtoms.pop();
|
|
663
|
+
if (last.jsxNode instanceof Component) {
|
|
664
|
+
components.push(last.jsxNode);
|
|
665
|
+
continue;
|
|
666
|
+
}
|
|
667
|
+
return last.nativeNode;
|
|
668
|
+
}
|
|
669
|
+
for (const component of components) {
|
|
670
|
+
const nativeNode = this.getPrevSibling(component);
|
|
671
|
+
if (nativeNode) {
|
|
672
|
+
return nativeNode;
|
|
673
|
+
}
|
|
674
|
+
}
|
|
675
|
+
return null;
|
|
754
676
|
}
|
|
755
677
|
reconcileElement(atom, context) {
|
|
756
678
|
while (atom) {
|
|
@@ -780,76 +702,150 @@ let Renderer = class Renderer {
|
|
|
780
702
|
else {
|
|
781
703
|
atom.child = null;
|
|
782
704
|
}
|
|
783
|
-
this.diff(atom.child, diffAtom, context);
|
|
705
|
+
this.diff(atom.child, diffAtom, context, 0, 0);
|
|
784
706
|
component.rendered();
|
|
785
707
|
}
|
|
786
|
-
diff(
|
|
708
|
+
diff(newAtom, oldAtom, context, expectIndex, index) {
|
|
787
709
|
const oldChildren = [];
|
|
788
|
-
while (
|
|
789
|
-
oldChildren.push(
|
|
790
|
-
|
|
710
|
+
while (oldAtom) {
|
|
711
|
+
oldChildren.push({
|
|
712
|
+
index,
|
|
713
|
+
atom: oldAtom
|
|
714
|
+
});
|
|
715
|
+
oldAtom = oldAtom.sibling;
|
|
716
|
+
index++;
|
|
791
717
|
}
|
|
792
718
|
const commits = [];
|
|
793
|
-
const
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
this.nativeRenderer.prependChild(host, start.nativeNode);
|
|
719
|
+
const changeCommits = {
|
|
720
|
+
reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
|
|
721
|
+
commits.push(() => {
|
|
722
|
+
const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
|
|
723
|
+
if (add.length || remove.length || replace.length) {
|
|
724
|
+
reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
|
|
800
725
|
}
|
|
801
|
-
|
|
802
|
-
|
|
726
|
+
const newProps = start.jsxNode.props;
|
|
727
|
+
start.jsxNode = reusedAtom.jsxNode;
|
|
728
|
+
start.jsxNode.props = newProps;
|
|
729
|
+
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
730
|
+
const template = render();
|
|
731
|
+
if (template) {
|
|
732
|
+
this.linkTemplate(template, start.jsxNode, start);
|
|
733
|
+
}
|
|
734
|
+
this.componentAtomCaches.set(start.jsxNode, {
|
|
735
|
+
render,
|
|
736
|
+
atom: start
|
|
737
|
+
});
|
|
738
|
+
if (start.child) {
|
|
739
|
+
this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
|
|
740
|
+
}
|
|
741
|
+
else if (reusedAtom.child) {
|
|
742
|
+
let atom = reusedAtom.child;
|
|
743
|
+
while (atom) {
|
|
744
|
+
this.cleanView(atom, false);
|
|
745
|
+
atom = atom.sibling;
|
|
746
|
+
}
|
|
803
747
|
}
|
|
804
|
-
context.host = start.nativeNode;
|
|
805
|
-
context.isParent = false;
|
|
806
|
-
}
|
|
807
|
-
if (start.child) {
|
|
808
|
-
const childContext = start.jsxNode instanceof JSXElement ? {
|
|
809
|
-
host: start.nativeNode,
|
|
810
|
-
isParent: true
|
|
811
|
-
} : context;
|
|
812
|
-
this.diff(start.child, reusedAtom.child, childContext);
|
|
813
|
-
}
|
|
814
|
-
else if (reusedAtom.child) {
|
|
815
|
-
this.cleanView(reusedAtom.child, false);
|
|
816
|
-
}
|
|
817
|
-
if (isComponent) {
|
|
818
748
|
start.jsxNode.rendered();
|
|
819
|
-
}
|
|
820
|
-
}
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
749
|
+
});
|
|
750
|
+
},
|
|
751
|
+
reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
752
|
+
commits.push((offset) => {
|
|
753
|
+
newAtom.nativeNode = oldAtom.nativeNode;
|
|
754
|
+
const host = context.host;
|
|
755
|
+
if (expectIndex !== oldIndex - offset) {
|
|
756
|
+
if (context.isParent) {
|
|
757
|
+
this.nativeRenderer.prependChild(host, newAtom.nativeNode);
|
|
758
|
+
}
|
|
759
|
+
else {
|
|
760
|
+
this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
|
|
761
|
+
}
|
|
762
|
+
}
|
|
763
|
+
context.host = newAtom.nativeNode;
|
|
764
|
+
context.isParent = false;
|
|
765
|
+
const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
|
|
766
|
+
if (newAtom.child) {
|
|
767
|
+
this.diff(newAtom.child, oldAtom.child, {
|
|
768
|
+
host: newAtom.nativeNode,
|
|
769
|
+
isParent: true
|
|
770
|
+
}, 0, 0);
|
|
828
771
|
}
|
|
829
|
-
else {
|
|
830
|
-
|
|
772
|
+
else if (oldAtom.child) {
|
|
773
|
+
let atom = oldAtom.child;
|
|
774
|
+
while (atom) {
|
|
775
|
+
this.cleanView(atom, false);
|
|
776
|
+
atom = atom.sibling;
|
|
777
|
+
}
|
|
778
|
+
}
|
|
779
|
+
applyRefs();
|
|
780
|
+
});
|
|
781
|
+
},
|
|
782
|
+
reuseText: (newAtom, oldAtom) => {
|
|
783
|
+
commits.push(() => {
|
|
784
|
+
const nativeNode = oldAtom.nativeNode;
|
|
785
|
+
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
786
|
+
this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
|
|
831
787
|
}
|
|
832
|
-
|
|
788
|
+
newAtom.nativeNode = nativeNode;
|
|
789
|
+
context.host = nativeNode;
|
|
833
790
|
context.isParent = false;
|
|
834
791
|
});
|
|
835
|
-
}
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
addReuseCommit(start, reusedAtom);
|
|
792
|
+
},
|
|
793
|
+
create: (start) => {
|
|
794
|
+
commits.push(() => {
|
|
795
|
+
this.buildView(start, context);
|
|
796
|
+
});
|
|
841
797
|
}
|
|
842
|
-
|
|
843
|
-
|
|
798
|
+
};
|
|
799
|
+
while (newAtom && !newAtom.nativeNode) {
|
|
800
|
+
this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
|
|
801
|
+
newAtom = newAtom.sibling;
|
|
802
|
+
expectIndex++;
|
|
803
|
+
}
|
|
804
|
+
for (const item of oldChildren) {
|
|
805
|
+
this.cleanView(item.atom, false);
|
|
806
|
+
}
|
|
807
|
+
let j = 0;
|
|
808
|
+
let offset = 0;
|
|
809
|
+
const len = oldChildren.length;
|
|
810
|
+
for (let i = 0; i < commits.length; i++) {
|
|
811
|
+
const commit = commits[i];
|
|
812
|
+
while (j < len) {
|
|
813
|
+
const current = oldChildren[j];
|
|
814
|
+
if (current.index <= i) {
|
|
815
|
+
offset++;
|
|
816
|
+
j++;
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
break;
|
|
844
820
|
}
|
|
845
|
-
|
|
821
|
+
commit(offset);
|
|
846
822
|
}
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
823
|
+
}
|
|
824
|
+
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
825
|
+
for (let i = 0; i < oldChildren.length; i++) {
|
|
826
|
+
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
827
|
+
const key = newAtom.jsxNode.key;
|
|
828
|
+
const diffKey = diffAtom.jsxNode.key;
|
|
829
|
+
if (key !== undefined && diffKey !== undefined) {
|
|
830
|
+
if (diffKey !== key) {
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
}
|
|
834
|
+
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
835
|
+
if (newAtom.jsxNode instanceof JSXElement) {
|
|
836
|
+
changeCommits.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
837
|
+
}
|
|
838
|
+
else if (newAtom.jsxNode instanceof JSXText) {
|
|
839
|
+
changeCommits.reuseText(newAtom, diffAtom);
|
|
840
|
+
}
|
|
841
|
+
else {
|
|
842
|
+
changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
843
|
+
}
|
|
844
|
+
oldChildren.splice(i, 1);
|
|
845
|
+
return;
|
|
846
|
+
}
|
|
852
847
|
}
|
|
848
|
+
changeCommits.create(newAtom);
|
|
853
849
|
}
|
|
854
850
|
cleanView(atom, isClean) {
|
|
855
851
|
if (atom.nativeNode) {
|
|
@@ -858,10 +854,8 @@ let Renderer = class Renderer {
|
|
|
858
854
|
isClean = true;
|
|
859
855
|
}
|
|
860
856
|
if (atom.jsxNode instanceof JSXElement) {
|
|
861
|
-
const ref = atom.jsxNode.props
|
|
862
|
-
|
|
863
|
-
ref.unListen();
|
|
864
|
-
}
|
|
857
|
+
const ref = atom.jsxNode.props[refKey];
|
|
858
|
+
this.applyRefs(ref, atom.nativeNode, false);
|
|
865
859
|
}
|
|
866
860
|
}
|
|
867
861
|
let child = atom.child;
|
|
@@ -873,167 +867,77 @@ let Renderer = class Renderer {
|
|
|
873
867
|
atom.jsxNode.destroy();
|
|
874
868
|
}
|
|
875
869
|
}
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
this.updateNativeNodeProperties(start.jsxNode, diffAtom.jsxNode, nativeNode);
|
|
884
|
-
oldChildren.splice(i, 1);
|
|
885
|
-
return diffAtom;
|
|
886
|
-
}
|
|
887
|
-
}
|
|
888
|
-
else if (start.jsxNode instanceof JSXText) {
|
|
889
|
-
if (diffAtom.jsxNode instanceof JSXText) {
|
|
890
|
-
const nativeNode = diffAtom.nativeNode;
|
|
891
|
-
if (start.jsxNode.text !== diffAtom.jsxNode.text) {
|
|
892
|
-
this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
|
|
893
|
-
}
|
|
894
|
-
start.nativeNode = nativeNode;
|
|
895
|
-
oldChildren.splice(i, 1);
|
|
896
|
-
return diffAtom;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
else if (diffAtom.jsxNode instanceof Component) {
|
|
900
|
-
if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
|
|
901
|
-
const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
|
|
902
|
-
if (isChanged) {
|
|
903
|
-
diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
|
|
904
|
-
}
|
|
905
|
-
start.jsxNode = diffAtom.jsxNode;
|
|
906
|
-
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
907
|
-
const template = render();
|
|
908
|
-
if (template) {
|
|
909
|
-
this.linkTemplate(template, start.jsxNode, start);
|
|
910
|
-
}
|
|
911
|
-
this.componentAtomCaches.set(start.jsxNode, {
|
|
912
|
-
render,
|
|
913
|
-
atom: start
|
|
914
|
-
});
|
|
915
|
-
oldChildren.splice(i, 1);
|
|
916
|
-
return diffAtom;
|
|
917
|
-
}
|
|
870
|
+
buildView(atom, context) {
|
|
871
|
+
if (atom.jsxNode instanceof Component) {
|
|
872
|
+
this.componentRender(atom.jsxNode, atom);
|
|
873
|
+
let child = atom.child;
|
|
874
|
+
while (child) {
|
|
875
|
+
this.buildView(child, context);
|
|
876
|
+
child = child.sibling;
|
|
918
877
|
}
|
|
878
|
+
atom.jsxNode.rendered();
|
|
919
879
|
}
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
}
|
|
880
|
+
else {
|
|
881
|
+
let nativeNode;
|
|
882
|
+
let applyRefs = null;
|
|
883
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
884
|
+
const { nativeNode: n, applyRefs: a } = this.createElement(atom.jsxNode);
|
|
885
|
+
nativeNode = n;
|
|
886
|
+
applyRefs = a;
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
nativeNode = this.createTextNode(atom.jsxNode);
|
|
931
890
|
}
|
|
932
|
-
return [nativeNode];
|
|
933
|
-
}
|
|
934
|
-
else if (atom.jsxNode instanceof JSXText) {
|
|
935
|
-
const nativeNode = this.createTextNode(atom.jsxNode);
|
|
936
891
|
atom.nativeNode = nativeNode;
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
const { template, render } = atom.jsxNode.init();
|
|
940
|
-
this.componentAtomCaches.set(atom.jsxNode, {
|
|
941
|
-
atom,
|
|
942
|
-
render
|
|
943
|
-
});
|
|
944
|
-
if (template) {
|
|
945
|
-
this.linkTemplate(template, atom.jsxNode, atom);
|
|
946
|
-
}
|
|
947
|
-
if (atom.child) {
|
|
948
|
-
return this.buildView(atom.child);
|
|
949
|
-
}
|
|
950
|
-
return [];
|
|
951
|
-
}
|
|
952
|
-
buildView(chain) {
|
|
953
|
-
const context = [];
|
|
954
|
-
const children = [];
|
|
955
|
-
function getContext() {
|
|
956
|
-
return context[context.length - 1];
|
|
957
|
-
}
|
|
958
|
-
let atom = chain;
|
|
959
|
-
const stopAtom = chain.parent;
|
|
960
|
-
wrap: while (atom) {
|
|
961
|
-
if (atom.jsxNode instanceof Component) {
|
|
962
|
-
this.componentRender(atom.jsxNode, atom);
|
|
963
|
-
if (atom.child) {
|
|
964
|
-
atom = atom.child;
|
|
965
|
-
continue;
|
|
966
|
-
}
|
|
967
|
-
atom.jsxNode.rendered();
|
|
892
|
+
if (context.isParent) {
|
|
893
|
+
this.nativeRenderer.prependChild(context.host, nativeNode);
|
|
968
894
|
}
|
|
969
895
|
else {
|
|
970
|
-
|
|
971
|
-
const nativeNode = atom.jsxNode instanceof JSXElement ? this.createElement(atom.jsxNode) : this.createTextNode(atom.jsxNode);
|
|
972
|
-
atom.nativeNode = nativeNode;
|
|
973
|
-
if (host) {
|
|
974
|
-
this.nativeRenderer.appendChild(host, nativeNode);
|
|
975
|
-
}
|
|
976
|
-
else {
|
|
977
|
-
children.push(nativeNode);
|
|
978
|
-
}
|
|
979
|
-
if (atom.child) {
|
|
980
|
-
context.push(nativeNode);
|
|
981
|
-
atom = atom.child;
|
|
982
|
-
continue;
|
|
983
|
-
}
|
|
896
|
+
this.nativeRenderer.insertAfter(nativeNode, context.host);
|
|
984
897
|
}
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
}
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
}
|
|
995
|
-
if (atom === stopAtom) {
|
|
996
|
-
break wrap;
|
|
997
|
-
}
|
|
998
|
-
if (isComponent) {
|
|
999
|
-
continue;
|
|
898
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
899
|
+
const childContext = {
|
|
900
|
+
isParent: true,
|
|
901
|
+
host: nativeNode
|
|
902
|
+
};
|
|
903
|
+
let child = atom.child;
|
|
904
|
+
while (child) {
|
|
905
|
+
this.buildView(child, childContext);
|
|
906
|
+
child = child.sibling;
|
|
1000
907
|
}
|
|
1001
|
-
|
|
908
|
+
}
|
|
909
|
+
context.host = nativeNode;
|
|
910
|
+
context.isParent = false;
|
|
911
|
+
if (applyRefs) {
|
|
912
|
+
applyRefs();
|
|
1002
913
|
}
|
|
1003
914
|
}
|
|
1004
|
-
return children;
|
|
1005
915
|
}
|
|
1006
|
-
componentRender(component,
|
|
916
|
+
componentRender(component, from) {
|
|
1007
917
|
const { template, render } = component.init();
|
|
1008
918
|
if (template) {
|
|
1009
|
-
this.linkTemplate(template, component,
|
|
919
|
+
this.linkTemplate(template, component, from);
|
|
1010
920
|
}
|
|
1011
921
|
this.componentAtomCaches.set(component, {
|
|
1012
922
|
render,
|
|
1013
|
-
atom:
|
|
923
|
+
atom: from
|
|
1014
924
|
});
|
|
1015
|
-
return
|
|
925
|
+
return from;
|
|
1016
926
|
}
|
|
1017
927
|
createChainByComponentFactory(context, factory, parent) {
|
|
1018
|
-
const component = factory(context);
|
|
928
|
+
const component = factory.createInstance(context);
|
|
1019
929
|
if (component.setup === Fragment) {
|
|
1020
930
|
return this.createChainByChildren(component, component.props.children, parent);
|
|
1021
931
|
}
|
|
1022
932
|
return new Atom(component, parent);
|
|
1023
933
|
}
|
|
1024
|
-
createChain(context, template, parent) {
|
|
1025
|
-
if (template instanceof JSXElement) {
|
|
1026
|
-
return this.createChainByJSXElement(context, template, parent);
|
|
1027
|
-
}
|
|
1028
|
-
if (template instanceof JSXText) {
|
|
1029
|
-
return this.createChainByJSXText(template, parent);
|
|
1030
|
-
}
|
|
1031
|
-
return this.createChainByComponentFactory(context, template, parent);
|
|
1032
|
-
}
|
|
1033
934
|
createChainByJSXElement(context, element, parent) {
|
|
1034
935
|
const atom = new Atom(element, parent);
|
|
1035
|
-
|
|
1036
|
-
|
|
936
|
+
if (Reflect.has(element.props, 'children')) {
|
|
937
|
+
const jsxChildren = element.props.children;
|
|
938
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
939
|
+
this.link(atom, children);
|
|
940
|
+
}
|
|
1037
941
|
return atom;
|
|
1038
942
|
}
|
|
1039
943
|
createChainByJSXText(node, parent) {
|
|
@@ -1042,19 +946,39 @@ let Renderer = class Renderer {
|
|
|
1042
946
|
createChainByChildren(context, children, parent) {
|
|
1043
947
|
const atoms = [];
|
|
1044
948
|
for (const item of children) {
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
949
|
+
if (item instanceof JSXElement) {
|
|
950
|
+
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
951
|
+
continue;
|
|
1048
952
|
}
|
|
1049
|
-
|
|
1050
|
-
|
|
953
|
+
if (item instanceof JSXComponent) {
|
|
954
|
+
const childAtom = this.createChainByComponentFactory(context, item, parent);
|
|
955
|
+
if (Array.isArray(childAtom)) {
|
|
956
|
+
atoms.push(...childAtom);
|
|
957
|
+
}
|
|
958
|
+
else {
|
|
959
|
+
atoms.push(childAtom);
|
|
960
|
+
}
|
|
961
|
+
continue;
|
|
962
|
+
}
|
|
963
|
+
if (typeof item === 'string' && item.length) {
|
|
964
|
+
atoms.push(this.createChainByJSXText(new JSXText(item), parent));
|
|
965
|
+
continue;
|
|
966
|
+
}
|
|
967
|
+
if (Array.isArray(item)) {
|
|
968
|
+
atoms.push(...this.createChainByChildren(context, item, parent));
|
|
969
|
+
continue;
|
|
970
|
+
}
|
|
971
|
+
if (item !== null && typeof item !== 'undefined') {
|
|
972
|
+
atoms.push(this.createChainByJSXText(new JSXText(String(item)), parent));
|
|
1051
973
|
}
|
|
1052
974
|
}
|
|
1053
975
|
return atoms;
|
|
1054
976
|
}
|
|
1055
977
|
linkTemplate(template, component, parent) {
|
|
1056
978
|
if (template) {
|
|
1057
|
-
const child =
|
|
979
|
+
const child = template instanceof JSXElement ?
|
|
980
|
+
this.createChainByJSXElement(component, template, parent) :
|
|
981
|
+
this.createChainByComponentFactory(component, template, parent);
|
|
1058
982
|
this.link(parent, Array.isArray(child) ? child : [child]);
|
|
1059
983
|
}
|
|
1060
984
|
}
|
|
@@ -1068,56 +992,153 @@ let Renderer = class Renderer {
|
|
|
1068
992
|
createElement(vNode) {
|
|
1069
993
|
const nativeNode = this.nativeRenderer.createElement(vNode.name);
|
|
1070
994
|
const props = vNode.props;
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
995
|
+
let bindingRefs;
|
|
996
|
+
const keys = Object.keys(props);
|
|
997
|
+
for (const key of keys) {
|
|
998
|
+
if (key === 'children') {
|
|
999
|
+
continue;
|
|
1000
|
+
}
|
|
1001
|
+
if (key === 'class') {
|
|
1002
|
+
this.nativeRenderer.setClass(nativeNode, classToString(props[key]));
|
|
1003
|
+
continue;
|
|
1004
|
+
}
|
|
1005
|
+
if (key === 'style') {
|
|
1006
|
+
const style = props.style;
|
|
1007
|
+
Object.keys(style).forEach(key => {
|
|
1008
|
+
this.nativeRenderer.setStyle(nativeNode, key, style[key]);
|
|
1009
|
+
});
|
|
1010
|
+
continue;
|
|
1011
|
+
}
|
|
1012
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1013
|
+
const listener = props[key];
|
|
1014
|
+
if (typeof listener === 'function') {
|
|
1015
|
+
this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), listener);
|
|
1076
1016
|
}
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
|
|
1085
|
-
});
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
if (key === refKey) {
|
|
1020
|
+
bindingRefs = props[key];
|
|
1021
|
+
continue;
|
|
1022
|
+
}
|
|
1023
|
+
this.nativeRenderer.setProperty(nativeNode, key, props[key]);
|
|
1086
1024
|
}
|
|
1087
|
-
return
|
|
1025
|
+
return {
|
|
1026
|
+
nativeNode,
|
|
1027
|
+
applyRefs: () => {
|
|
1028
|
+
this.applyRefs(bindingRefs, nativeNode, true);
|
|
1029
|
+
}
|
|
1030
|
+
};
|
|
1088
1031
|
}
|
|
1089
1032
|
createTextNode(child) {
|
|
1090
1033
|
return this.nativeRenderer.createTextNode(child.text);
|
|
1091
1034
|
}
|
|
1092
1035
|
updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
|
|
1093
|
-
const
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
if (key ===
|
|
1101
|
-
|
|
1102
|
-
|
|
1036
|
+
const changes = getObjectChanges(newVNode.props, oldVNode.props);
|
|
1037
|
+
let unBindRefs;
|
|
1038
|
+
let bindRefs;
|
|
1039
|
+
for (const [key, value] of changes.remove) {
|
|
1040
|
+
if (key === 'children') {
|
|
1041
|
+
continue;
|
|
1042
|
+
}
|
|
1043
|
+
if (key === 'class') {
|
|
1044
|
+
this.nativeRenderer.setClass(nativeNode, '');
|
|
1045
|
+
continue;
|
|
1046
|
+
}
|
|
1047
|
+
if (key === 'style') {
|
|
1048
|
+
Object.keys(value).forEach(styleName => {
|
|
1049
|
+
this.nativeRenderer.removeStyle(nativeNode, styleName);
|
|
1050
|
+
});
|
|
1051
|
+
continue;
|
|
1052
|
+
}
|
|
1053
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1054
|
+
if (typeof value === 'function') {
|
|
1055
|
+
this.nativeRenderer.unListen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
|
|
1056
|
+
}
|
|
1057
|
+
continue;
|
|
1058
|
+
}
|
|
1059
|
+
if (key === refKey) {
|
|
1060
|
+
unBindRefs = value;
|
|
1061
|
+
continue;
|
|
1103
1062
|
}
|
|
1104
1063
|
this.nativeRenderer.removeProperty(nativeNode, key);
|
|
1105
|
-
}
|
|
1106
|
-
|
|
1107
|
-
if (key ===
|
|
1108
|
-
|
|
1109
|
-
|
|
1064
|
+
}
|
|
1065
|
+
for (const [key, newValue, oldValue] of changes.replace) {
|
|
1066
|
+
if (key === 'children') {
|
|
1067
|
+
continue;
|
|
1068
|
+
}
|
|
1069
|
+
if (key === 'class') {
|
|
1070
|
+
const oldClassName = classToString(oldValue);
|
|
1071
|
+
const newClassName = classToString(newValue);
|
|
1072
|
+
if (oldClassName !== newClassName) {
|
|
1073
|
+
this.nativeRenderer.setClass(nativeNode, newClassName);
|
|
1074
|
+
}
|
|
1075
|
+
continue;
|
|
1076
|
+
}
|
|
1077
|
+
if (key === 'style') {
|
|
1078
|
+
const styleChanges = getObjectChanges(newValue, oldValue);
|
|
1079
|
+
for (const [styleName] of styleChanges.remove) {
|
|
1080
|
+
this.nativeRenderer.removeStyle(nativeNode, styleName);
|
|
1081
|
+
}
|
|
1082
|
+
for (const [styleName, styleValue] of [...styleChanges.add, ...styleChanges.replace]) {
|
|
1083
|
+
this.nativeRenderer.setStyle(nativeNode, styleName, styleValue);
|
|
1084
|
+
}
|
|
1085
|
+
continue;
|
|
1086
|
+
}
|
|
1087
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1088
|
+
const listenType = key.replace(/^on/, '').toLowerCase();
|
|
1089
|
+
if (typeof oldValue === 'function') {
|
|
1090
|
+
this.nativeRenderer.unListen(nativeNode, listenType, oldValue);
|
|
1091
|
+
}
|
|
1092
|
+
if (typeof newValue === 'function') {
|
|
1093
|
+
this.nativeRenderer.listen(nativeNode, listenType, newValue);
|
|
1094
|
+
}
|
|
1095
|
+
continue;
|
|
1096
|
+
}
|
|
1097
|
+
if (key === refKey) {
|
|
1098
|
+
unBindRefs = oldValue;
|
|
1099
|
+
bindRefs = newValue;
|
|
1100
|
+
continue;
|
|
1101
|
+
}
|
|
1102
|
+
this.nativeRenderer.setProperty(nativeNode, key, newValue);
|
|
1103
|
+
}
|
|
1104
|
+
for (const [key, value] of changes.add) {
|
|
1105
|
+
if (key === 'children') {
|
|
1106
|
+
continue;
|
|
1107
|
+
}
|
|
1108
|
+
if (key === 'class') {
|
|
1109
|
+
this.nativeRenderer.setClass(nativeNode, classToString(value));
|
|
1110
|
+
continue;
|
|
1111
|
+
}
|
|
1112
|
+
if (key === 'style') {
|
|
1113
|
+
Object.keys(value).forEach(styleName => {
|
|
1114
|
+
this.nativeRenderer.setStyle(nativeNode, styleName, value[styleName]);
|
|
1115
|
+
});
|
|
1116
|
+
continue;
|
|
1117
|
+
}
|
|
1118
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1119
|
+
if (typeof value === 'function') {
|
|
1120
|
+
this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
|
|
1121
|
+
}
|
|
1122
|
+
continue;
|
|
1123
|
+
}
|
|
1124
|
+
if (key === refKey) {
|
|
1125
|
+
bindRefs = value;
|
|
1126
|
+
continue;
|
|
1110
1127
|
}
|
|
1111
1128
|
this.nativeRenderer.setProperty(nativeNode, key, value);
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1129
|
+
}
|
|
1130
|
+
return () => {
|
|
1131
|
+
this.applyRefs(unBindRefs, nativeNode, false);
|
|
1132
|
+
this.applyRefs(bindRefs, nativeNode, true);
|
|
1133
|
+
};
|
|
1134
|
+
}
|
|
1135
|
+
applyRefs(refs, nativeNode, binding) {
|
|
1136
|
+
refs = Array.isArray(refs) ? refs : [refs];
|
|
1137
|
+
for (const item of refs) {
|
|
1138
|
+
if (item instanceof Ref) {
|
|
1139
|
+
binding ? item.bind(nativeNode) : item.unBind(nativeNode);
|
|
1140
|
+
}
|
|
1141
|
+
}
|
|
1121
1142
|
}
|
|
1122
1143
|
};
|
|
1123
1144
|
Renderer = __decorate([
|
|
@@ -1159,7 +1180,7 @@ class Viewfly extends ReflectiveInjector {
|
|
|
1159
1180
|
/**
|
|
1160
1181
|
* 启动 Viewfly
|
|
1161
1182
|
*/
|
|
1162
|
-
|
|
1183
|
+
run() {
|
|
1163
1184
|
const renderer = this.get(Renderer);
|
|
1164
1185
|
renderer.render();
|
|
1165
1186
|
if (this.config.autoUpdate === false) {
|
|
@@ -1184,8 +1205,8 @@ class Viewfly extends ReflectiveInjector {
|
|
|
1184
1205
|
return () => {
|
|
1185
1206
|
return this.destroyed ? null : rootNode;
|
|
1186
1207
|
};
|
|
1187
|
-
});
|
|
1208
|
+
}, this.config.context);
|
|
1188
1209
|
}
|
|
1189
1210
|
}
|
|
1190
1211
|
|
|
1191
|
-
export { Component, Fragment,
|
|
1212
|
+
export { Component, Fragment, JSXComponent, JSXElement, JSXText, NativeRenderer, Ref, Renderer, RootComponent, RootComponentRef, Viewfly, inject, jsx, jsxs, makeError, onDestroy, onMount, onPropsChanged, onUpdated, provide, useDerived, useEffect, useRef, useSignal };
|