@viewfly/core 0.0.1-alpha.1 → 0.0.1-alpha.11
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 +4 -19
- package/bundles/foundation/injection-tokens.d.ts +1 -2
- package/bundles/foundation/renderer.d.ts +3 -3
- package/bundles/index.esm.js +607 -572
- package/bundles/index.js +609 -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 +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,81 @@ 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
601
|
}
|
|
654
|
-
|
|
655
|
-
|
|
602
|
+
return classes.join(' ');
|
|
603
|
+
}
|
|
656
604
|
}
|
|
657
|
-
function
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
};
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
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
|
-
}
|
|
681
|
-
});
|
|
682
|
-
source.forEach(i => {
|
|
683
|
-
if (!target.has(i)) {
|
|
684
|
-
changes.remove.push(i);
|
|
605
|
+
function styleToObject(style) {
|
|
606
|
+
if (typeof style !== 'string') {
|
|
607
|
+
return style;
|
|
608
|
+
}
|
|
609
|
+
const obj = {};
|
|
610
|
+
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
611
|
+
if (!v[0] || !v[1]) {
|
|
612
|
+
return;
|
|
685
613
|
}
|
|
614
|
+
obj[v[0].trim()] = v[1].trim();
|
|
686
615
|
});
|
|
687
|
-
return
|
|
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
|
-
};
|
|
616
|
+
return obj;
|
|
713
617
|
}
|
|
714
618
|
|
|
715
619
|
class RootComponentRef {
|
|
@@ -731,10 +635,10 @@ exports.Renderer = class Renderer {
|
|
|
731
635
|
}
|
|
732
636
|
render() {
|
|
733
637
|
const { component, host } = this.rootComponentRef;
|
|
734
|
-
const
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
638
|
+
const atom = new Atom(component, null);
|
|
639
|
+
this.buildView(atom, {
|
|
640
|
+
isParent: true,
|
|
641
|
+
host
|
|
738
642
|
});
|
|
739
643
|
}
|
|
740
644
|
refresh() {
|
|
@@ -752,6 +656,37 @@ exports.Renderer = class Renderer {
|
|
|
752
656
|
const atom = this.componentAtomCaches.get(component).atom.child;
|
|
753
657
|
this.reconcileElement(atom, context);
|
|
754
658
|
}
|
|
659
|
+
else {
|
|
660
|
+
const prevSibling = this.getPrevSibling(component);
|
|
661
|
+
if (prevSibling) {
|
|
662
|
+
context.isParent = false;
|
|
663
|
+
context.host = prevSibling;
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
getPrevSibling(component) {
|
|
668
|
+
let atom = this.componentAtomCaches.get(component).atom.child;
|
|
669
|
+
const childAtoms = [];
|
|
670
|
+
while (atom) {
|
|
671
|
+
childAtoms.push(atom);
|
|
672
|
+
atom = atom.sibling;
|
|
673
|
+
}
|
|
674
|
+
const components = [];
|
|
675
|
+
while (childAtoms.length) {
|
|
676
|
+
const last = childAtoms.pop();
|
|
677
|
+
if (last.jsxNode instanceof Component) {
|
|
678
|
+
components.push(last.jsxNode);
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
return last.nativeNode;
|
|
682
|
+
}
|
|
683
|
+
for (const component of components) {
|
|
684
|
+
const nativeNode = this.getPrevSibling(component);
|
|
685
|
+
if (nativeNode) {
|
|
686
|
+
return nativeNode;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
return null;
|
|
755
690
|
}
|
|
756
691
|
reconcileElement(atom, context) {
|
|
757
692
|
while (atom) {
|
|
@@ -781,76 +716,150 @@ exports.Renderer = class Renderer {
|
|
|
781
716
|
else {
|
|
782
717
|
atom.child = null;
|
|
783
718
|
}
|
|
784
|
-
this.diff(atom.child, diffAtom, context);
|
|
719
|
+
this.diff(atom.child, diffAtom, context, 0, 0);
|
|
785
720
|
component.rendered();
|
|
786
721
|
}
|
|
787
|
-
diff(
|
|
722
|
+
diff(newAtom, oldAtom, context, expectIndex, index) {
|
|
788
723
|
const oldChildren = [];
|
|
789
|
-
while (
|
|
790
|
-
oldChildren.push(
|
|
791
|
-
|
|
724
|
+
while (oldAtom) {
|
|
725
|
+
oldChildren.push({
|
|
726
|
+
index,
|
|
727
|
+
atom: oldAtom
|
|
728
|
+
});
|
|
729
|
+
oldAtom = oldAtom.sibling;
|
|
730
|
+
index++;
|
|
792
731
|
}
|
|
793
732
|
const commits = [];
|
|
794
|
-
const
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
this.nativeRenderer.prependChild(host, start.nativeNode);
|
|
733
|
+
const changeCommits = {
|
|
734
|
+
reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
|
|
735
|
+
commits.push(() => {
|
|
736
|
+
const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
|
|
737
|
+
if (add.length || remove.length || replace.length) {
|
|
738
|
+
reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
|
|
801
739
|
}
|
|
802
|
-
|
|
803
|
-
|
|
740
|
+
const newProps = start.jsxNode.props;
|
|
741
|
+
start.jsxNode = reusedAtom.jsxNode;
|
|
742
|
+
start.jsxNode.props = newProps;
|
|
743
|
+
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
744
|
+
const template = render();
|
|
745
|
+
if (template) {
|
|
746
|
+
this.linkTemplate(template, start.jsxNode, start);
|
|
747
|
+
}
|
|
748
|
+
this.componentAtomCaches.set(start.jsxNode, {
|
|
749
|
+
render,
|
|
750
|
+
atom: start
|
|
751
|
+
});
|
|
752
|
+
if (start.child) {
|
|
753
|
+
this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
|
|
754
|
+
}
|
|
755
|
+
else if (reusedAtom.child) {
|
|
756
|
+
let atom = reusedAtom.child;
|
|
757
|
+
while (atom) {
|
|
758
|
+
this.cleanView(atom, false);
|
|
759
|
+
atom = atom.sibling;
|
|
760
|
+
}
|
|
804
761
|
}
|
|
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
762
|
start.jsxNode.rendered();
|
|
820
|
-
}
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
763
|
+
});
|
|
764
|
+
},
|
|
765
|
+
reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
766
|
+
commits.push((offset) => {
|
|
767
|
+
newAtom.nativeNode = oldAtom.nativeNode;
|
|
768
|
+
const host = context.host;
|
|
769
|
+
if (expectIndex !== oldIndex - offset) {
|
|
770
|
+
if (context.isParent) {
|
|
771
|
+
this.nativeRenderer.prependChild(host, newAtom.nativeNode);
|
|
772
|
+
}
|
|
773
|
+
else {
|
|
774
|
+
this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
context.host = newAtom.nativeNode;
|
|
778
|
+
context.isParent = false;
|
|
779
|
+
const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
|
|
780
|
+
if (newAtom.child) {
|
|
781
|
+
this.diff(newAtom.child, oldAtom.child, {
|
|
782
|
+
host: newAtom.nativeNode,
|
|
783
|
+
isParent: true
|
|
784
|
+
}, 0, 0);
|
|
829
785
|
}
|
|
830
|
-
else {
|
|
831
|
-
|
|
786
|
+
else if (oldAtom.child) {
|
|
787
|
+
let atom = oldAtom.child;
|
|
788
|
+
while (atom) {
|
|
789
|
+
this.cleanView(atom, false);
|
|
790
|
+
atom = atom.sibling;
|
|
791
|
+
}
|
|
792
|
+
}
|
|
793
|
+
applyRefs();
|
|
794
|
+
});
|
|
795
|
+
},
|
|
796
|
+
reuseText: (newAtom, oldAtom) => {
|
|
797
|
+
commits.push(() => {
|
|
798
|
+
const nativeNode = oldAtom.nativeNode;
|
|
799
|
+
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
800
|
+
this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
|
|
832
801
|
}
|
|
833
|
-
|
|
802
|
+
newAtom.nativeNode = nativeNode;
|
|
803
|
+
context.host = nativeNode;
|
|
834
804
|
context.isParent = false;
|
|
835
805
|
});
|
|
836
|
-
}
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
addReuseCommit(start, reusedAtom);
|
|
806
|
+
},
|
|
807
|
+
create: (start) => {
|
|
808
|
+
commits.push(() => {
|
|
809
|
+
this.buildView(start, context);
|
|
810
|
+
});
|
|
842
811
|
}
|
|
843
|
-
|
|
844
|
-
|
|
812
|
+
};
|
|
813
|
+
while (newAtom && !newAtom.nativeNode) {
|
|
814
|
+
this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
|
|
815
|
+
newAtom = newAtom.sibling;
|
|
816
|
+
expectIndex++;
|
|
817
|
+
}
|
|
818
|
+
for (const item of oldChildren) {
|
|
819
|
+
this.cleanView(item.atom, false);
|
|
820
|
+
}
|
|
821
|
+
let j = 0;
|
|
822
|
+
let offset = 0;
|
|
823
|
+
const len = oldChildren.length;
|
|
824
|
+
for (let i = 0; i < commits.length; i++) {
|
|
825
|
+
const commit = commits[i];
|
|
826
|
+
while (j < len) {
|
|
827
|
+
const current = oldChildren[j];
|
|
828
|
+
if (current.index <= i) {
|
|
829
|
+
offset++;
|
|
830
|
+
j++;
|
|
831
|
+
continue;
|
|
832
|
+
}
|
|
833
|
+
break;
|
|
845
834
|
}
|
|
846
|
-
|
|
835
|
+
commit(offset);
|
|
847
836
|
}
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
837
|
+
}
|
|
838
|
+
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
839
|
+
for (let i = 0; i < oldChildren.length; i++) {
|
|
840
|
+
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
841
|
+
const key = newAtom.jsxNode.key;
|
|
842
|
+
const diffKey = diffAtom.jsxNode.key;
|
|
843
|
+
if (key !== undefined && diffKey !== undefined) {
|
|
844
|
+
if (diffKey !== key) {
|
|
845
|
+
continue;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
849
|
+
if (newAtom.jsxNode instanceof JSXElement) {
|
|
850
|
+
changeCommits.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
851
|
+
}
|
|
852
|
+
else if (newAtom.jsxNode instanceof JSXText) {
|
|
853
|
+
changeCommits.reuseText(newAtom, diffAtom);
|
|
854
|
+
}
|
|
855
|
+
else {
|
|
856
|
+
changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
857
|
+
}
|
|
858
|
+
oldChildren.splice(i, 1);
|
|
859
|
+
return;
|
|
860
|
+
}
|
|
853
861
|
}
|
|
862
|
+
changeCommits.create(newAtom);
|
|
854
863
|
}
|
|
855
864
|
cleanView(atom, isClean) {
|
|
856
865
|
if (atom.nativeNode) {
|
|
@@ -859,10 +868,8 @@ exports.Renderer = class Renderer {
|
|
|
859
868
|
isClean = true;
|
|
860
869
|
}
|
|
861
870
|
if (atom.jsxNode instanceof JSXElement) {
|
|
862
|
-
const ref = atom.jsxNode.props
|
|
863
|
-
|
|
864
|
-
ref.unListen();
|
|
865
|
-
}
|
|
871
|
+
const ref = atom.jsxNode.props[refKey];
|
|
872
|
+
this.applyRefs(ref, atom.nativeNode, false);
|
|
866
873
|
}
|
|
867
874
|
}
|
|
868
875
|
let child = atom.child;
|
|
@@ -874,167 +881,77 @@ exports.Renderer = class Renderer {
|
|
|
874
881
|
atom.jsxNode.destroy();
|
|
875
882
|
}
|
|
876
883
|
}
|
|
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
|
-
}
|
|
884
|
+
buildView(atom, context) {
|
|
885
|
+
if (atom.jsxNode instanceof Component) {
|
|
886
|
+
this.componentRender(atom.jsxNode, atom);
|
|
887
|
+
let child = atom.child;
|
|
888
|
+
while (child) {
|
|
889
|
+
this.buildView(child, context);
|
|
890
|
+
child = child.sibling;
|
|
919
891
|
}
|
|
892
|
+
atom.jsxNode.rendered();
|
|
920
893
|
}
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
}
|
|
894
|
+
else {
|
|
895
|
+
let nativeNode;
|
|
896
|
+
let applyRefs = null;
|
|
897
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
898
|
+
const { nativeNode: n, applyRefs: a } = this.createElement(atom.jsxNode);
|
|
899
|
+
nativeNode = n;
|
|
900
|
+
applyRefs = a;
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
nativeNode = this.createTextNode(atom.jsxNode);
|
|
932
904
|
}
|
|
933
|
-
return [nativeNode];
|
|
934
|
-
}
|
|
935
|
-
else if (atom.jsxNode instanceof JSXText) {
|
|
936
|
-
const nativeNode = this.createTextNode(atom.jsxNode);
|
|
937
905
|
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();
|
|
906
|
+
if (context.isParent) {
|
|
907
|
+
this.nativeRenderer.prependChild(context.host, nativeNode);
|
|
969
908
|
}
|
|
970
909
|
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
|
-
}
|
|
910
|
+
this.nativeRenderer.insertAfter(nativeNode, context.host);
|
|
985
911
|
}
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
}
|
|
996
|
-
if (atom === stopAtom) {
|
|
997
|
-
break wrap;
|
|
998
|
-
}
|
|
999
|
-
if (isComponent) {
|
|
1000
|
-
continue;
|
|
912
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
913
|
+
const childContext = {
|
|
914
|
+
isParent: true,
|
|
915
|
+
host: nativeNode
|
|
916
|
+
};
|
|
917
|
+
let child = atom.child;
|
|
918
|
+
while (child) {
|
|
919
|
+
this.buildView(child, childContext);
|
|
920
|
+
child = child.sibling;
|
|
1001
921
|
}
|
|
1002
|
-
|
|
922
|
+
}
|
|
923
|
+
context.host = nativeNode;
|
|
924
|
+
context.isParent = false;
|
|
925
|
+
if (applyRefs) {
|
|
926
|
+
applyRefs();
|
|
1003
927
|
}
|
|
1004
928
|
}
|
|
1005
|
-
return children;
|
|
1006
929
|
}
|
|
1007
|
-
componentRender(component,
|
|
930
|
+
componentRender(component, from) {
|
|
1008
931
|
const { template, render } = component.init();
|
|
1009
932
|
if (template) {
|
|
1010
|
-
this.linkTemplate(template, component,
|
|
933
|
+
this.linkTemplate(template, component, from);
|
|
1011
934
|
}
|
|
1012
935
|
this.componentAtomCaches.set(component, {
|
|
1013
936
|
render,
|
|
1014
|
-
atom:
|
|
937
|
+
atom: from
|
|
1015
938
|
});
|
|
1016
|
-
return
|
|
939
|
+
return from;
|
|
1017
940
|
}
|
|
1018
941
|
createChainByComponentFactory(context, factory, parent) {
|
|
1019
|
-
const component = factory(context);
|
|
942
|
+
const component = factory.createInstance(context);
|
|
1020
943
|
if (component.setup === Fragment) {
|
|
1021
944
|
return this.createChainByChildren(component, component.props.children, parent);
|
|
1022
945
|
}
|
|
1023
946
|
return new Atom(component, parent);
|
|
1024
947
|
}
|
|
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
948
|
createChainByJSXElement(context, element, parent) {
|
|
1035
949
|
const atom = new Atom(element, parent);
|
|
1036
|
-
|
|
1037
|
-
|
|
950
|
+
if (Reflect.has(element.props, 'children')) {
|
|
951
|
+
const jsxChildren = element.props.children;
|
|
952
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
953
|
+
this.link(atom, children);
|
|
954
|
+
}
|
|
1038
955
|
return atom;
|
|
1039
956
|
}
|
|
1040
957
|
createChainByJSXText(node, parent) {
|
|
@@ -1043,19 +960,39 @@ exports.Renderer = class Renderer {
|
|
|
1043
960
|
createChainByChildren(context, children, parent) {
|
|
1044
961
|
const atoms = [];
|
|
1045
962
|
for (const item of children) {
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
963
|
+
if (item instanceof JSXElement) {
|
|
964
|
+
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
965
|
+
continue;
|
|
1049
966
|
}
|
|
1050
|
-
|
|
1051
|
-
|
|
967
|
+
if (item instanceof JSXComponent) {
|
|
968
|
+
const childAtom = this.createChainByComponentFactory(context, item, parent);
|
|
969
|
+
if (Array.isArray(childAtom)) {
|
|
970
|
+
atoms.push(...childAtom);
|
|
971
|
+
}
|
|
972
|
+
else {
|
|
973
|
+
atoms.push(childAtom);
|
|
974
|
+
}
|
|
975
|
+
continue;
|
|
976
|
+
}
|
|
977
|
+
if (typeof item === 'string' && item.length) {
|
|
978
|
+
atoms.push(this.createChainByJSXText(new JSXText(item), parent));
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
if (Array.isArray(item)) {
|
|
982
|
+
atoms.push(...this.createChainByChildren(context, item, parent));
|
|
983
|
+
continue;
|
|
984
|
+
}
|
|
985
|
+
if (item !== null && typeof item !== 'undefined') {
|
|
986
|
+
atoms.push(this.createChainByJSXText(new JSXText(String(item)), parent));
|
|
1052
987
|
}
|
|
1053
988
|
}
|
|
1054
989
|
return atoms;
|
|
1055
990
|
}
|
|
1056
991
|
linkTemplate(template, component, parent) {
|
|
1057
992
|
if (template) {
|
|
1058
|
-
const child =
|
|
993
|
+
const child = template instanceof JSXElement ?
|
|
994
|
+
this.createChainByJSXElement(component, template, parent) :
|
|
995
|
+
this.createChainByComponentFactory(component, template, parent);
|
|
1059
996
|
this.link(parent, Array.isArray(child) ? child : [child]);
|
|
1060
997
|
}
|
|
1061
998
|
}
|
|
@@ -1069,56 +1006,154 @@ exports.Renderer = class Renderer {
|
|
|
1069
1006
|
createElement(vNode) {
|
|
1070
1007
|
const nativeNode = this.nativeRenderer.createElement(vNode.name);
|
|
1071
1008
|
const props = vNode.props;
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1009
|
+
let bindingRefs;
|
|
1010
|
+
const keys = Object.keys(props);
|
|
1011
|
+
for (const key of keys) {
|
|
1012
|
+
if (key === 'children') {
|
|
1013
|
+
continue;
|
|
1014
|
+
}
|
|
1015
|
+
if (key === 'class') {
|
|
1016
|
+
this.nativeRenderer.setClass(nativeNode, classToString(props[key]));
|
|
1017
|
+
continue;
|
|
1018
|
+
}
|
|
1019
|
+
if (key === 'style') {
|
|
1020
|
+
const style = styleToObject(props.style);
|
|
1021
|
+
Object.keys(style).forEach(key => {
|
|
1022
|
+
this.nativeRenderer.setStyle(nativeNode, key, style[key]);
|
|
1023
|
+
});
|
|
1024
|
+
continue;
|
|
1025
|
+
}
|
|
1026
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1027
|
+
const listener = props[key];
|
|
1028
|
+
if (typeof listener === 'function') {
|
|
1029
|
+
this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), listener);
|
|
1077
1030
|
}
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
|
|
1086
|
-
});
|
|
1031
|
+
continue;
|
|
1032
|
+
}
|
|
1033
|
+
if (key === refKey) {
|
|
1034
|
+
bindingRefs = props[key];
|
|
1035
|
+
continue;
|
|
1036
|
+
}
|
|
1037
|
+
this.nativeRenderer.setProperty(nativeNode, key, props[key]);
|
|
1087
1038
|
}
|
|
1088
|
-
return
|
|
1039
|
+
return {
|
|
1040
|
+
nativeNode,
|
|
1041
|
+
applyRefs: () => {
|
|
1042
|
+
this.applyRefs(bindingRefs, nativeNode, true);
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1089
1045
|
}
|
|
1090
1046
|
createTextNode(child) {
|
|
1091
1047
|
return this.nativeRenderer.createTextNode(child.text);
|
|
1092
1048
|
}
|
|
1093
1049
|
updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
|
|
1094
|
-
const
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
if (key ===
|
|
1102
|
-
|
|
1103
|
-
|
|
1050
|
+
const changes = getObjectChanges(newVNode.props, oldVNode.props);
|
|
1051
|
+
let unBindRefs;
|
|
1052
|
+
let bindRefs;
|
|
1053
|
+
for (const [key, value] of changes.remove) {
|
|
1054
|
+
if (key === 'children') {
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
if (key === 'class') {
|
|
1058
|
+
this.nativeRenderer.setClass(nativeNode, '');
|
|
1059
|
+
continue;
|
|
1060
|
+
}
|
|
1061
|
+
if (key === 'style') {
|
|
1062
|
+
Object.keys(styleToObject(value)).forEach(styleName => {
|
|
1063
|
+
this.nativeRenderer.removeStyle(nativeNode, styleName);
|
|
1064
|
+
});
|
|
1065
|
+
continue;
|
|
1066
|
+
}
|
|
1067
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1068
|
+
if (typeof value === 'function') {
|
|
1069
|
+
this.nativeRenderer.unListen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
|
|
1070
|
+
}
|
|
1071
|
+
continue;
|
|
1072
|
+
}
|
|
1073
|
+
if (key === refKey) {
|
|
1074
|
+
unBindRefs = value;
|
|
1075
|
+
continue;
|
|
1104
1076
|
}
|
|
1105
1077
|
this.nativeRenderer.removeProperty(nativeNode, key);
|
|
1106
|
-
}
|
|
1107
|
-
|
|
1108
|
-
if (key ===
|
|
1109
|
-
|
|
1110
|
-
|
|
1078
|
+
}
|
|
1079
|
+
for (const [key, newValue, oldValue] of changes.replace) {
|
|
1080
|
+
if (key === 'children') {
|
|
1081
|
+
continue;
|
|
1082
|
+
}
|
|
1083
|
+
if (key === 'class') {
|
|
1084
|
+
const oldClassName = classToString(oldValue);
|
|
1085
|
+
const newClassName = classToString(newValue);
|
|
1086
|
+
if (oldClassName !== newClassName) {
|
|
1087
|
+
this.nativeRenderer.setClass(nativeNode, newClassName);
|
|
1088
|
+
}
|
|
1089
|
+
continue;
|
|
1090
|
+
}
|
|
1091
|
+
if (key === 'style') {
|
|
1092
|
+
const styleChanges = getObjectChanges(styleToObject(newValue), styleToObject(oldValue));
|
|
1093
|
+
for (const [styleName] of styleChanges.remove) {
|
|
1094
|
+
this.nativeRenderer.removeStyle(nativeNode, styleName);
|
|
1095
|
+
}
|
|
1096
|
+
for (const [styleName, styleValue] of [...styleChanges.add, ...styleChanges.replace]) {
|
|
1097
|
+
this.nativeRenderer.setStyle(nativeNode, styleName, styleValue);
|
|
1098
|
+
}
|
|
1099
|
+
continue;
|
|
1100
|
+
}
|
|
1101
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1102
|
+
const listenType = key.replace(/^on/, '').toLowerCase();
|
|
1103
|
+
if (typeof oldValue === 'function') {
|
|
1104
|
+
this.nativeRenderer.unListen(nativeNode, listenType, oldValue);
|
|
1105
|
+
}
|
|
1106
|
+
if (typeof newValue === 'function') {
|
|
1107
|
+
this.nativeRenderer.listen(nativeNode, listenType, newValue);
|
|
1108
|
+
}
|
|
1109
|
+
continue;
|
|
1110
|
+
}
|
|
1111
|
+
if (key === refKey) {
|
|
1112
|
+
unBindRefs = oldValue;
|
|
1113
|
+
bindRefs = newValue;
|
|
1114
|
+
continue;
|
|
1115
|
+
}
|
|
1116
|
+
this.nativeRenderer.setProperty(nativeNode, key, newValue);
|
|
1117
|
+
}
|
|
1118
|
+
for (const [key, value] of changes.add) {
|
|
1119
|
+
if (key === 'children') {
|
|
1120
|
+
continue;
|
|
1121
|
+
}
|
|
1122
|
+
if (key === 'class') {
|
|
1123
|
+
this.nativeRenderer.setClass(nativeNode, classToString(value));
|
|
1124
|
+
continue;
|
|
1125
|
+
}
|
|
1126
|
+
if (key === 'style') {
|
|
1127
|
+
const styleObj = styleToObject(value);
|
|
1128
|
+
Object.keys(styleObj).forEach(styleName => {
|
|
1129
|
+
this.nativeRenderer.setStyle(nativeNode, styleName, styleObj[styleName]);
|
|
1130
|
+
});
|
|
1131
|
+
continue;
|
|
1132
|
+
}
|
|
1133
|
+
if (/^on[A-Z]/.test(key)) {
|
|
1134
|
+
if (typeof value === 'function') {
|
|
1135
|
+
this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
|
|
1136
|
+
}
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
if (key === refKey) {
|
|
1140
|
+
bindRefs = value;
|
|
1141
|
+
continue;
|
|
1111
1142
|
}
|
|
1112
1143
|
this.nativeRenderer.setProperty(nativeNode, key, value);
|
|
1113
|
-
}
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1144
|
+
}
|
|
1145
|
+
return () => {
|
|
1146
|
+
this.applyRefs(unBindRefs, nativeNode, false);
|
|
1147
|
+
this.applyRefs(bindRefs, nativeNode, true);
|
|
1148
|
+
};
|
|
1149
|
+
}
|
|
1150
|
+
applyRefs(refs, nativeNode, binding) {
|
|
1151
|
+
refs = Array.isArray(refs) ? refs : [refs];
|
|
1152
|
+
for (const item of refs) {
|
|
1153
|
+
if (item instanceof Ref) {
|
|
1154
|
+
binding ? item.bind(nativeNode) : item.unBind(nativeNode);
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1122
1157
|
}
|
|
1123
1158
|
};
|
|
1124
1159
|
exports.Renderer = __decorate([
|
|
@@ -1160,7 +1195,7 @@ class Viewfly extends di.ReflectiveInjector {
|
|
|
1160
1195
|
/**
|
|
1161
1196
|
* 启动 Viewfly
|
|
1162
1197
|
*/
|
|
1163
|
-
|
|
1198
|
+
run() {
|
|
1164
1199
|
const renderer = this.get(exports.Renderer);
|
|
1165
1200
|
renderer.render();
|
|
1166
1201
|
if (this.config.autoUpdate === false) {
|
|
@@ -1185,17 +1220,16 @@ class Viewfly extends di.ReflectiveInjector {
|
|
|
1185
1220
|
return () => {
|
|
1186
1221
|
return this.destroyed ? null : rootNode;
|
|
1187
1222
|
};
|
|
1188
|
-
});
|
|
1223
|
+
}, this.config.context);
|
|
1189
1224
|
}
|
|
1190
1225
|
}
|
|
1191
1226
|
|
|
1192
1227
|
exports.Component = Component;
|
|
1193
1228
|
exports.Fragment = Fragment;
|
|
1229
|
+
exports.JSXComponent = JSXComponent;
|
|
1194
1230
|
exports.JSXElement = JSXElement;
|
|
1195
|
-
exports.JSXFragment = JSXFragment;
|
|
1196
1231
|
exports.JSXText = JSXText;
|
|
1197
1232
|
exports.NativeRenderer = NativeRenderer;
|
|
1198
|
-
exports.Props = Props;
|
|
1199
1233
|
exports.Ref = Ref;
|
|
1200
1234
|
exports.RootComponent = RootComponent;
|
|
1201
1235
|
exports.RootComponentRef = RootComponentRef;
|
|
@@ -1203,11 +1237,13 @@ exports.Viewfly = Viewfly;
|
|
|
1203
1237
|
exports.inject = inject;
|
|
1204
1238
|
exports.jsx = jsx;
|
|
1205
1239
|
exports.jsxs = jsxs;
|
|
1240
|
+
exports.makeError = makeError;
|
|
1206
1241
|
exports.onDestroy = onDestroy;
|
|
1207
1242
|
exports.onMount = onMount;
|
|
1208
1243
|
exports.onPropsChanged = onPropsChanged;
|
|
1209
1244
|
exports.onUpdated = onUpdated;
|
|
1210
1245
|
exports.provide = provide;
|
|
1246
|
+
exports.useDerived = useDerived;
|
|
1211
1247
|
exports.useEffect = useEffect;
|
|
1212
1248
|
exports.useRef = useRef;
|
|
1213
1249
|
exports.useSignal = useSignal;
|