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