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