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