@viewfly/core 0.0.1-alpha.0
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/LICENSE +21 -0
- package/README.md +4 -0
- package/bundles/_utils/make-error.d.ts +1 -0
- package/bundles/foundation/_api.d.ts +2 -0
- package/bundles/foundation/_utils.d.ts +24 -0
- package/bundles/foundation/injection-tokens.d.ts +18 -0
- package/bundles/foundation/renderer.d.ts +33 -0
- package/bundles/index.esm.js +1188 -0
- package/bundles/index.js +1216 -0
- package/bundles/model/_api.d.ts +3 -0
- package/bundles/model/component.d.ts +194 -0
- package/bundles/model/jsx-element.d.ts +39 -0
- package/bundles/model/root.component.d.ts +10 -0
- package/bundles/public-api.d.ts +5 -0
- package/bundles/viewfly.d.ts +36 -0
- package/jsx-dev-runtime.js +8 -0
- package/jsx-runtime.d.ts +16 -0
- package/jsx-runtime.js +7 -0
- package/jsx-runtime.umd.js +17 -0
- package/package.json +39 -0
|
@@ -0,0 +1,1188 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { ReflectiveInjector, normalizeProvider, NullInjector, Injectable } from '@tanbo/di';
|
|
3
|
+
export * from '@tanbo/di';
|
|
4
|
+
import { Subject, Subscription, microTask } from '@tanbo/stream';
|
|
5
|
+
|
|
6
|
+
class NativeRenderer {
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/******************************************************************************
|
|
10
|
+
Copyright (c) Microsoft Corporation.
|
|
11
|
+
|
|
12
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
13
|
+
purpose with or without fee is hereby granted.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
16
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
17
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
18
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
19
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
20
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
21
|
+
PERFORMANCE OF THIS SOFTWARE.
|
|
22
|
+
***************************************************************************** */
|
|
23
|
+
/* global Reflect, Promise */
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
function __decorate(decorators, target, key, desc) {
|
|
27
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
28
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
29
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
30
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function __metadata(metadataKey, metadataValue) {
|
|
34
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function Fragment(props) {
|
|
38
|
+
return () => new JSXFragment(props);
|
|
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 (node !== null && typeof node !== 'undefined') {
|
|
76
|
+
children.push(new JSXText(String(node)));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return children;
|
|
80
|
+
}
|
|
81
|
+
class Props {
|
|
82
|
+
constructor(props) {
|
|
83
|
+
this.attrs = new Map();
|
|
84
|
+
this.styles = new Map();
|
|
85
|
+
this.classes = new Set();
|
|
86
|
+
this.listeners = {};
|
|
87
|
+
this.children = [];
|
|
88
|
+
if (!props) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
Object.keys(props).forEach(key => {
|
|
92
|
+
if (key === 'children') {
|
|
93
|
+
if (props.children !== null && typeof props.children !== 'undefined') {
|
|
94
|
+
if (Array.isArray(props.children)) {
|
|
95
|
+
this.children = flatChildren(props.children);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.children = flatChildren([props.children]);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
if (key === 'class') {
|
|
104
|
+
this.classes = new Set(Props.classToArray(props[key]));
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (key === 'style') {
|
|
108
|
+
const style = props.style || '';
|
|
109
|
+
if (typeof style === 'string') {
|
|
110
|
+
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
111
|
+
if (!v[0] || !v[1]) {
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
this.styles.set(v[0].trim(), v[1].trim());
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
else if (typeof style === 'object') {
|
|
118
|
+
Object.keys(style).forEach(key => {
|
|
119
|
+
this.styles.set(key, style[key]);
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
if (/^on[A-Z]/.test(key)) {
|
|
125
|
+
const listener = props[key];
|
|
126
|
+
if (typeof listener === 'function') {
|
|
127
|
+
this.listeners[key.replace(/^on/, '').toLowerCase()] = listener;
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
this.attrs.set(key, listener);
|
|
131
|
+
}
|
|
132
|
+
return;
|
|
133
|
+
}
|
|
134
|
+
this.attrs.set(key, props[key]);
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
static classToArray(config) {
|
|
138
|
+
const classes = [];
|
|
139
|
+
if (!config) {
|
|
140
|
+
return classes;
|
|
141
|
+
}
|
|
142
|
+
if (typeof config === 'string') {
|
|
143
|
+
const items = config.match(/\S+/g);
|
|
144
|
+
return items || classes;
|
|
145
|
+
}
|
|
146
|
+
else if (Array.isArray(config)) {
|
|
147
|
+
for (const i of config) {
|
|
148
|
+
classes.push(...Props.classToArray(i));
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
else if (typeof config === 'object') {
|
|
152
|
+
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
153
|
+
classes.push(config.toString());
|
|
154
|
+
return classes;
|
|
155
|
+
}
|
|
156
|
+
for (const key in config) {
|
|
157
|
+
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
158
|
+
classes.push(key);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
return classes;
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
class JSXElement {
|
|
166
|
+
constructor(name, config) {
|
|
167
|
+
this.name = name;
|
|
168
|
+
this.config = config;
|
|
169
|
+
this.props = new Props(config);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function makeError(name) {
|
|
174
|
+
return function viewflyError(message) {
|
|
175
|
+
const error = new Error(message);
|
|
176
|
+
error.name = `[ViewflyError: ${name}]`;
|
|
177
|
+
error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
|
|
178
|
+
return error;
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const componentStack = [];
|
|
183
|
+
const componentErrorFn = makeError('component');
|
|
184
|
+
function getComponentContext(need = true) {
|
|
185
|
+
const current = componentStack[componentStack.length - 1];
|
|
186
|
+
if (!current && need) {
|
|
187
|
+
throw componentErrorFn('cannot be called outside the component!');
|
|
188
|
+
}
|
|
189
|
+
return current;
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Viewfly 组件管理类,用于管理组件的生命周期,上下文等
|
|
193
|
+
*/
|
|
194
|
+
class Component extends ReflectiveInjector {
|
|
195
|
+
get dirty() {
|
|
196
|
+
return this._dirty;
|
|
197
|
+
}
|
|
198
|
+
get changed() {
|
|
199
|
+
return this._changed;
|
|
200
|
+
}
|
|
201
|
+
constructor(context, setup, config) {
|
|
202
|
+
super(context, []);
|
|
203
|
+
this.setup = setup;
|
|
204
|
+
this.config = config;
|
|
205
|
+
this.destroyCallbacks = [];
|
|
206
|
+
this.mountCallbacks = [];
|
|
207
|
+
this.propsChangedCallbacks = [];
|
|
208
|
+
this.updatedCallbacks = [];
|
|
209
|
+
this._dirty = true;
|
|
210
|
+
this._changed = true;
|
|
211
|
+
this.updatedDestroyCallbacks = [];
|
|
212
|
+
this.propsChangedDestroyCallbacks = [];
|
|
213
|
+
this.isFirstRending = true;
|
|
214
|
+
this.props = new Props(config);
|
|
215
|
+
this.parentComponent = this.parentInjector;
|
|
216
|
+
}
|
|
217
|
+
addProvide(providers) {
|
|
218
|
+
providers = Array.isArray(providers) ? providers : [providers];
|
|
219
|
+
providers.forEach(p => {
|
|
220
|
+
this.normalizedProviders.push(normalizeProvider(p));
|
|
221
|
+
});
|
|
222
|
+
}
|
|
223
|
+
init() {
|
|
224
|
+
componentStack.push(this);
|
|
225
|
+
const self = this;
|
|
226
|
+
const props = new Proxy({}, {
|
|
227
|
+
get(_, key) {
|
|
228
|
+
if (self.config) {
|
|
229
|
+
return self.config[key];
|
|
230
|
+
}
|
|
231
|
+
},
|
|
232
|
+
set() {
|
|
233
|
+
throw componentErrorFn('component props is readonly!');
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
const render = this.setup(props);
|
|
237
|
+
const template = render();
|
|
238
|
+
componentStack.pop();
|
|
239
|
+
return {
|
|
240
|
+
template,
|
|
241
|
+
render: () => {
|
|
242
|
+
componentStack.push(this);
|
|
243
|
+
const template = render();
|
|
244
|
+
componentStack.pop();
|
|
245
|
+
return template;
|
|
246
|
+
}
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
markAsDirtied() {
|
|
250
|
+
this._dirty = true;
|
|
251
|
+
this.markAsChanged();
|
|
252
|
+
}
|
|
253
|
+
markAsChanged() {
|
|
254
|
+
this._changed = true;
|
|
255
|
+
this.parentComponent.markAsChanged();
|
|
256
|
+
}
|
|
257
|
+
rendered() {
|
|
258
|
+
const is = this.isFirstRending;
|
|
259
|
+
this.isFirstRending = false;
|
|
260
|
+
this._dirty = this._changed = false;
|
|
261
|
+
if (is) {
|
|
262
|
+
this.invokeUpdatedHooks();
|
|
263
|
+
this.invokeMountHooks();
|
|
264
|
+
}
|
|
265
|
+
else {
|
|
266
|
+
this.invokeUpdatedHooks();
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
invokePropsChangedHooks(newProps) {
|
|
270
|
+
const oldProps = this.config;
|
|
271
|
+
this.config = newProps;
|
|
272
|
+
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
273
|
+
fn();
|
|
274
|
+
});
|
|
275
|
+
this.propsChangedDestroyCallbacks = [];
|
|
276
|
+
for (const fn of this.propsChangedCallbacks) {
|
|
277
|
+
const destroyFn = fn(newProps, oldProps);
|
|
278
|
+
if (typeof destroyFn === 'function') {
|
|
279
|
+
this.propsChangedDestroyCallbacks.push(destroyFn);
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
destroy() {
|
|
284
|
+
this.updatedDestroyCallbacks.forEach(fn => {
|
|
285
|
+
fn();
|
|
286
|
+
});
|
|
287
|
+
this.updatedDestroyCallbacks = [];
|
|
288
|
+
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
289
|
+
fn();
|
|
290
|
+
});
|
|
291
|
+
this.propsChangedDestroyCallbacks = [];
|
|
292
|
+
for (const fn of this.destroyCallbacks) {
|
|
293
|
+
fn();
|
|
294
|
+
}
|
|
295
|
+
this.updatedCallbacks = [];
|
|
296
|
+
this.mountCallbacks = [];
|
|
297
|
+
this.updatedCallbacks = [];
|
|
298
|
+
}
|
|
299
|
+
invokeMountHooks() {
|
|
300
|
+
for (const fn of this.mountCallbacks) {
|
|
301
|
+
const destroyFn = fn();
|
|
302
|
+
if (typeof destroyFn === 'function') {
|
|
303
|
+
this.destroyCallbacks.push(destroyFn);
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
invokeUpdatedHooks() {
|
|
308
|
+
this.updatedDestroyCallbacks.forEach(fn => {
|
|
309
|
+
fn();
|
|
310
|
+
});
|
|
311
|
+
this.updatedDestroyCallbacks = [];
|
|
312
|
+
for (const fn of this.updatedCallbacks) {
|
|
313
|
+
const destroyFn = fn();
|
|
314
|
+
if (typeof destroyFn === 'function') {
|
|
315
|
+
this.updatedDestroyCallbacks.push(destroyFn);
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* 当组件第一次渲染完成时触发
|
|
322
|
+
* @param callback
|
|
323
|
+
* ```tsx
|
|
324
|
+
* function App() {
|
|
325
|
+
* onMount(() => {
|
|
326
|
+
* console.log('App mounted!')
|
|
327
|
+
* })
|
|
328
|
+
* return () => <div>...</div>
|
|
329
|
+
* }
|
|
330
|
+
* ```
|
|
331
|
+
*/
|
|
332
|
+
function onMount(callback) {
|
|
333
|
+
const component = getComponentContext();
|
|
334
|
+
component.mountCallbacks.push(callback);
|
|
335
|
+
}
|
|
336
|
+
/**
|
|
337
|
+
* 当组件视图更新后调用
|
|
338
|
+
* @param callback
|
|
339
|
+
* ```tsx
|
|
340
|
+
* function App() {
|
|
341
|
+
* onUpdated(() => {
|
|
342
|
+
* console.log('App updated!')
|
|
343
|
+
* return () => {
|
|
344
|
+
* console.log('destroy prev update!')
|
|
345
|
+
* }
|
|
346
|
+
* })
|
|
347
|
+
* return () => <div>...</div>
|
|
348
|
+
* }
|
|
349
|
+
* ```
|
|
350
|
+
*/
|
|
351
|
+
function onUpdated(callback) {
|
|
352
|
+
const component = getComponentContext();
|
|
353
|
+
component.updatedCallbacks.push(callback);
|
|
354
|
+
return () => {
|
|
355
|
+
const index = component.updatedCallbacks.indexOf(callback);
|
|
356
|
+
if (index > -1) {
|
|
357
|
+
component.updatedCallbacks.splice(index, 1);
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 当组件 props 更新地调用
|
|
363
|
+
* @param callback
|
|
364
|
+
* @example
|
|
365
|
+
* ```tsx
|
|
366
|
+
* function YourComponent(props) {
|
|
367
|
+
* onPropsChanged((currentProps, prevProps) => {
|
|
368
|
+
* console.log(currentProps, prevProps)
|
|
369
|
+
*
|
|
370
|
+
* return () => {
|
|
371
|
+
* console.log('destroy prev changed!')
|
|
372
|
+
* }
|
|
373
|
+
* })
|
|
374
|
+
* return () => {
|
|
375
|
+
* return <div>xxx</div>
|
|
376
|
+
* }
|
|
377
|
+
* }
|
|
378
|
+
* ```
|
|
379
|
+
*/
|
|
380
|
+
function onPropsChanged(callback) {
|
|
381
|
+
const component = getComponentContext();
|
|
382
|
+
component.propsChangedCallbacks.push(callback);
|
|
383
|
+
return () => {
|
|
384
|
+
const index = component.propsChangedCallbacks.indexOf(callback);
|
|
385
|
+
if (index > -1) {
|
|
386
|
+
component.propsChangedCallbacks.splice(index, 1);
|
|
387
|
+
}
|
|
388
|
+
};
|
|
389
|
+
}
|
|
390
|
+
/**
|
|
391
|
+
* 当组件销毁时调用回调函数
|
|
392
|
+
* @param callback
|
|
393
|
+
*/
|
|
394
|
+
function onDestroy(callback) {
|
|
395
|
+
const component = getComponentContext();
|
|
396
|
+
component.destroyCallbacks.push(callback);
|
|
397
|
+
}
|
|
398
|
+
class Ref {
|
|
399
|
+
// private prevValue: T | null = null
|
|
400
|
+
constructor(callback, component) {
|
|
401
|
+
this.callback = callback;
|
|
402
|
+
this.component = component;
|
|
403
|
+
this.unListenFn = null;
|
|
404
|
+
component.destroyCallbacks.push(() => {
|
|
405
|
+
this.unListen();
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
update(value) {
|
|
409
|
+
// if (value === this.prevValue) {
|
|
410
|
+
// return
|
|
411
|
+
// }
|
|
412
|
+
// this.prevValue = value
|
|
413
|
+
this.unListen();
|
|
414
|
+
this.unListenFn = this.callback(value) || null;
|
|
415
|
+
}
|
|
416
|
+
unListen() {
|
|
417
|
+
if (typeof this.unListenFn === 'function') {
|
|
418
|
+
this.unListenFn();
|
|
419
|
+
this.unListenFn = null;
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
/**
|
|
424
|
+
* 用于节点渲染完成时获取 DOM 节点
|
|
425
|
+
* @param callback 获取 DOM 节点的回调函数
|
|
426
|
+
* @example
|
|
427
|
+
* ```tsx
|
|
428
|
+
* function App() {
|
|
429
|
+
* const ref = useRef(node => {
|
|
430
|
+
* function fn() {
|
|
431
|
+
* // do something...
|
|
432
|
+
* }
|
|
433
|
+
* node.addEventListener('click', fn)
|
|
434
|
+
* return () => {
|
|
435
|
+
* node.removeEventListener('click', fn)
|
|
436
|
+
* }
|
|
437
|
+
* })
|
|
438
|
+
* return () => {
|
|
439
|
+
* return <div ref={ref}>xxx</div>
|
|
440
|
+
* }
|
|
441
|
+
* }
|
|
442
|
+
* ```
|
|
443
|
+
*/
|
|
444
|
+
function useRef(callback) {
|
|
445
|
+
const component = getComponentContext();
|
|
446
|
+
return new Ref(callback, component);
|
|
447
|
+
}
|
|
448
|
+
const depsKey = Symbol('deps');
|
|
449
|
+
/**
|
|
450
|
+
* 组件状态管理器
|
|
451
|
+
* @param state 初始状态
|
|
452
|
+
* @example
|
|
453
|
+
* ```tsx
|
|
454
|
+
* function App() {
|
|
455
|
+
* // 初始化状态
|
|
456
|
+
* const state = useSignal(1)
|
|
457
|
+
*
|
|
458
|
+
* return () => {
|
|
459
|
+
* <div>
|
|
460
|
+
* <div>当前值为:{state()}</div>
|
|
461
|
+
* <div>
|
|
462
|
+
* <button type="button" onClick={() => {
|
|
463
|
+
* // 当点击时更新状态
|
|
464
|
+
* state.set(state() + 1)
|
|
465
|
+
* }
|
|
466
|
+
* }>updateState</button>
|
|
467
|
+
* </div>
|
|
468
|
+
* </div>
|
|
469
|
+
* }
|
|
470
|
+
* }
|
|
471
|
+
*/
|
|
472
|
+
function useSignal(state) {
|
|
473
|
+
const usedComponents = new Set();
|
|
474
|
+
function stateManager() {
|
|
475
|
+
const component = getComponentContext(false);
|
|
476
|
+
if (component && !usedComponents.has(component)) {
|
|
477
|
+
usedComponents.add(component);
|
|
478
|
+
component.destroyCallbacks.push(() => {
|
|
479
|
+
usedComponents.delete(component);
|
|
480
|
+
});
|
|
481
|
+
}
|
|
482
|
+
return state;
|
|
483
|
+
}
|
|
484
|
+
stateManager.set = function (newState) {
|
|
485
|
+
if (typeof newState === 'function') {
|
|
486
|
+
newState = newState(state);
|
|
487
|
+
}
|
|
488
|
+
else if (newState === state) {
|
|
489
|
+
return;
|
|
490
|
+
}
|
|
491
|
+
state = newState;
|
|
492
|
+
for (const component of usedComponents) {
|
|
493
|
+
component.markAsDirtied();
|
|
494
|
+
}
|
|
495
|
+
for (const fn of stateManager[depsKey]) {
|
|
496
|
+
fn();
|
|
497
|
+
}
|
|
498
|
+
};
|
|
499
|
+
stateManager[depsKey] = new Set();
|
|
500
|
+
return stateManager;
|
|
501
|
+
}
|
|
502
|
+
/**
|
|
503
|
+
* 监听状态变化,当任意一个状态发生变更时,触发回调。
|
|
504
|
+
* useEffect 会返回一个取消监听的函数,调用此函数,可以取消监听。
|
|
505
|
+
* 当在组件中调用时,组件销毁时会自动取消监听。
|
|
506
|
+
* @param deps 依赖的状态 Signal,可以是一个 Signal,只可以一个数包含 Signal 的数组
|
|
507
|
+
* @param effect 状态变更后的回调函数
|
|
508
|
+
*/
|
|
509
|
+
function useEffect(deps, effect) {
|
|
510
|
+
const signals = Array.isArray(deps) ? deps : [deps];
|
|
511
|
+
let prevCleanup;
|
|
512
|
+
function effectCallback() {
|
|
513
|
+
if (typeof prevCleanup === 'function') {
|
|
514
|
+
prevCleanup();
|
|
515
|
+
}
|
|
516
|
+
prevCleanup = effect();
|
|
517
|
+
}
|
|
518
|
+
for (const dep of signals) {
|
|
519
|
+
dep[depsKey].add(effectCallback);
|
|
520
|
+
}
|
|
521
|
+
const component = getComponentContext(false);
|
|
522
|
+
let isClean = false;
|
|
523
|
+
const destroyFn = () => {
|
|
524
|
+
if (isClean) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
isClean = true;
|
|
528
|
+
if (component) {
|
|
529
|
+
const index = component.destroyCallbacks.indexOf(destroyFn);
|
|
530
|
+
component.destroyCallbacks.splice(index, 1);
|
|
531
|
+
}
|
|
532
|
+
for (const dep of signals) {
|
|
533
|
+
dep[depsKey].delete(effectCallback);
|
|
534
|
+
}
|
|
535
|
+
};
|
|
536
|
+
if (component) {
|
|
537
|
+
component.destroyCallbacks.push(destroyFn);
|
|
538
|
+
}
|
|
539
|
+
return destroyFn;
|
|
540
|
+
}
|
|
541
|
+
/**
|
|
542
|
+
* 通过 IoC 容器当前组件提供上下文共享数据的方法
|
|
543
|
+
* @param provider
|
|
544
|
+
*/
|
|
545
|
+
function provide(provider) {
|
|
546
|
+
const component = getComponentContext();
|
|
547
|
+
component.addProvide(provider);
|
|
548
|
+
return component;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* 通过组件上下文获取 IoC 容器内数据的勾子方法
|
|
552
|
+
*/
|
|
553
|
+
function inject(token, notFoundValue, flags) {
|
|
554
|
+
const component = getComponentContext();
|
|
555
|
+
return component.parentInjector.get(token, notFoundValue, flags);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
/**
|
|
559
|
+
* Viewfly 根组件,用于实现组件状态更新事件通知
|
|
560
|
+
*/
|
|
561
|
+
class RootComponent extends Component {
|
|
562
|
+
constructor(factory) {
|
|
563
|
+
super(new NullInjector(), factory, new Props(null));
|
|
564
|
+
this.changeEmitter = new Subject();
|
|
565
|
+
}
|
|
566
|
+
markAsChanged() {
|
|
567
|
+
this._changed = true;
|
|
568
|
+
this.changeEmitter.next();
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const refKey = 'ref';
|
|
573
|
+
function getObjectChanges(target, source) {
|
|
574
|
+
const changes = {
|
|
575
|
+
remove: [],
|
|
576
|
+
add: []
|
|
577
|
+
};
|
|
578
|
+
// if (!target) {
|
|
579
|
+
// if (source) {
|
|
580
|
+
// Object.keys(source).forEach(key => {
|
|
581
|
+
// changes.remove.push([key, source[key]])
|
|
582
|
+
// })
|
|
583
|
+
// }
|
|
584
|
+
// return changes
|
|
585
|
+
// }
|
|
586
|
+
//
|
|
587
|
+
// if (!source) {
|
|
588
|
+
// Object.keys(target).forEach(key => {
|
|
589
|
+
// changes.add.push([key, target[key]])
|
|
590
|
+
// })
|
|
591
|
+
// return changes
|
|
592
|
+
// }
|
|
593
|
+
Object.keys(target).forEach(key => {
|
|
594
|
+
const leftValue = target[key];
|
|
595
|
+
if (!Reflect.has(source, key)) {
|
|
596
|
+
changes.add.push([key, leftValue]);
|
|
597
|
+
return;
|
|
598
|
+
}
|
|
599
|
+
const rightValue = source[key];
|
|
600
|
+
if (leftValue === rightValue) {
|
|
601
|
+
return;
|
|
602
|
+
}
|
|
603
|
+
changes.add.push([key, leftValue]);
|
|
604
|
+
changes.remove.push([key, rightValue]);
|
|
605
|
+
});
|
|
606
|
+
Object.keys(source).forEach(key => {
|
|
607
|
+
if (!Reflect.has(target, key)) {
|
|
608
|
+
changes.remove.push([key, source[key]]);
|
|
609
|
+
}
|
|
610
|
+
});
|
|
611
|
+
return changes;
|
|
612
|
+
}
|
|
613
|
+
function getMapChanges(target, source) {
|
|
614
|
+
const changes = {
|
|
615
|
+
remove: [],
|
|
616
|
+
set: []
|
|
617
|
+
};
|
|
618
|
+
// if (!target) {
|
|
619
|
+
// if (source) {
|
|
620
|
+
// source.forEach((value, key) => {
|
|
621
|
+
// changes.remove.push([key, value])
|
|
622
|
+
// })
|
|
623
|
+
// }
|
|
624
|
+
// return changes
|
|
625
|
+
// }
|
|
626
|
+
// if (!source) {
|
|
627
|
+
// target.forEach((value, key) => {
|
|
628
|
+
// changes.set.push([key, value])
|
|
629
|
+
// })
|
|
630
|
+
// return changes
|
|
631
|
+
// }
|
|
632
|
+
target.forEach((value, key) => {
|
|
633
|
+
const rightValue = source.get(key);
|
|
634
|
+
if (value === rightValue) {
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
changes.set.push([key, value]);
|
|
638
|
+
});
|
|
639
|
+
source.forEach((value, key) => {
|
|
640
|
+
if (key === refKey && value instanceof Ref) {
|
|
641
|
+
const newValue = target.get(key);
|
|
642
|
+
if (value !== newValue) {
|
|
643
|
+
changes.remove.push([key, value]);
|
|
644
|
+
}
|
|
645
|
+
return;
|
|
646
|
+
}
|
|
647
|
+
if (!target.has(key)) {
|
|
648
|
+
changes.remove.push([key, value]);
|
|
649
|
+
}
|
|
650
|
+
});
|
|
651
|
+
return changes;
|
|
652
|
+
}
|
|
653
|
+
function getSetChanges(target, source) {
|
|
654
|
+
const changes = {
|
|
655
|
+
add: [],
|
|
656
|
+
remove: []
|
|
657
|
+
};
|
|
658
|
+
// if (!target) {
|
|
659
|
+
// if (source) {
|
|
660
|
+
// source.forEach(i => {
|
|
661
|
+
// changes.remove.push(i)
|
|
662
|
+
// })
|
|
663
|
+
// }
|
|
664
|
+
// return changes
|
|
665
|
+
// }
|
|
666
|
+
//
|
|
667
|
+
// if (!source) {
|
|
668
|
+
// target.forEach(i => {
|
|
669
|
+
// changes.add.push(i)
|
|
670
|
+
// })
|
|
671
|
+
// return changes
|
|
672
|
+
// }
|
|
673
|
+
target.forEach(i => {
|
|
674
|
+
if (!source.has(i)) {
|
|
675
|
+
changes.add.push(i);
|
|
676
|
+
}
|
|
677
|
+
});
|
|
678
|
+
source.forEach(i => {
|
|
679
|
+
if (!target.has(i)) {
|
|
680
|
+
changes.remove.push(i);
|
|
681
|
+
}
|
|
682
|
+
});
|
|
683
|
+
return changes;
|
|
684
|
+
}
|
|
685
|
+
const compareText = '0'.repeat(8);
|
|
686
|
+
function getNodeChanges(newVNode, oldVNode) {
|
|
687
|
+
const newProps = newVNode.props;
|
|
688
|
+
const oldProps = oldVNode.props;
|
|
689
|
+
const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
|
|
690
|
+
const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
|
|
691
|
+
const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
|
|
692
|
+
const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
|
|
693
|
+
return {
|
|
694
|
+
styleChanges,
|
|
695
|
+
attrChanges,
|
|
696
|
+
classesChanges,
|
|
697
|
+
listenerChanges,
|
|
698
|
+
isChanged: [
|
|
699
|
+
attrChanges.set.length,
|
|
700
|
+
attrChanges.remove.length,
|
|
701
|
+
styleChanges.set.length,
|
|
702
|
+
styleChanges.remove.length,
|
|
703
|
+
classesChanges.add.length,
|
|
704
|
+
classesChanges.remove.length,
|
|
705
|
+
listenerChanges.add.length,
|
|
706
|
+
listenerChanges.remove.length
|
|
707
|
+
].join('') !== compareText
|
|
708
|
+
};
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
class RootComponentRef {
|
|
712
|
+
}
|
|
713
|
+
class Atom {
|
|
714
|
+
constructor(jsxNode, parent) {
|
|
715
|
+
this.jsxNode = jsxNode;
|
|
716
|
+
this.parent = parent;
|
|
717
|
+
this.nativeNode = null;
|
|
718
|
+
this.child = null;
|
|
719
|
+
this.sibling = null;
|
|
720
|
+
}
|
|
721
|
+
}
|
|
722
|
+
let Renderer = class Renderer {
|
|
723
|
+
constructor(nativeRenderer, rootComponentRef) {
|
|
724
|
+
this.nativeRenderer = nativeRenderer;
|
|
725
|
+
this.rootComponentRef = rootComponentRef;
|
|
726
|
+
this.componentAtomCaches = new WeakMap();
|
|
727
|
+
}
|
|
728
|
+
render() {
|
|
729
|
+
const { component, host } = this.rootComponentRef;
|
|
730
|
+
const chain = new Atom(component, null);
|
|
731
|
+
const children = this.buildView(chain);
|
|
732
|
+
children.forEach(child => {
|
|
733
|
+
this.nativeRenderer.appendChild(host, child);
|
|
734
|
+
});
|
|
735
|
+
}
|
|
736
|
+
refresh() {
|
|
737
|
+
const { component, host } = this.rootComponentRef;
|
|
738
|
+
this.reconcile(component, {
|
|
739
|
+
host,
|
|
740
|
+
isParent: true
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
reconcile(component, context) {
|
|
744
|
+
if (component.dirty) {
|
|
745
|
+
this.applyChanges(component, context);
|
|
746
|
+
}
|
|
747
|
+
else if (component.changed) {
|
|
748
|
+
const atom = this.componentAtomCaches.get(component).atom.child;
|
|
749
|
+
this.reconcileElement(atom, context);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
reconcileElement(atom, context) {
|
|
753
|
+
while (atom) {
|
|
754
|
+
if (atom.jsxNode instanceof Component) {
|
|
755
|
+
this.reconcile(atom.jsxNode, context);
|
|
756
|
+
atom = atom.sibling;
|
|
757
|
+
continue;
|
|
758
|
+
}
|
|
759
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
760
|
+
this.reconcileElement(atom.child, {
|
|
761
|
+
host: atom.nativeNode,
|
|
762
|
+
isParent: true
|
|
763
|
+
});
|
|
764
|
+
context.host = atom.nativeNode;
|
|
765
|
+
context.isParent = false;
|
|
766
|
+
}
|
|
767
|
+
atom = atom.sibling;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
applyChanges(component, context) {
|
|
771
|
+
const { atom, render } = this.componentAtomCaches.get(component);
|
|
772
|
+
const diffAtom = atom.child;
|
|
773
|
+
const template = render();
|
|
774
|
+
if (template) {
|
|
775
|
+
this.linkTemplate(template, component, atom);
|
|
776
|
+
}
|
|
777
|
+
else {
|
|
778
|
+
atom.child = null;
|
|
779
|
+
}
|
|
780
|
+
this.diff(atom.child, diffAtom, context);
|
|
781
|
+
component.rendered();
|
|
782
|
+
}
|
|
783
|
+
diff(start, diffAtom, context) {
|
|
784
|
+
const oldChildren = [];
|
|
785
|
+
while (diffAtom) {
|
|
786
|
+
oldChildren.push(diffAtom);
|
|
787
|
+
diffAtom = diffAtom.sibling;
|
|
788
|
+
}
|
|
789
|
+
const commits = [];
|
|
790
|
+
const addReuseCommit = (start, reusedAtom) => {
|
|
791
|
+
commits.push(() => {
|
|
792
|
+
const isComponent = start.jsxNode instanceof Component;
|
|
793
|
+
if (!isComponent) {
|
|
794
|
+
const host = context.host;
|
|
795
|
+
if (context.isParent) {
|
|
796
|
+
this.nativeRenderer.prependChild(host, start.nativeNode);
|
|
797
|
+
}
|
|
798
|
+
else {
|
|
799
|
+
this.nativeRenderer.insertAfter(start.nativeNode, host);
|
|
800
|
+
}
|
|
801
|
+
context.host = start.nativeNode;
|
|
802
|
+
context.isParent = false;
|
|
803
|
+
}
|
|
804
|
+
if (start.child) {
|
|
805
|
+
const childContext = start.jsxNode instanceof JSXElement ? {
|
|
806
|
+
host: start.nativeNode,
|
|
807
|
+
isParent: true
|
|
808
|
+
} : context;
|
|
809
|
+
this.diff(start.child, reusedAtom.child, childContext);
|
|
810
|
+
}
|
|
811
|
+
else if (reusedAtom.child) {
|
|
812
|
+
this.cleanView(reusedAtom.child, false);
|
|
813
|
+
}
|
|
814
|
+
if (isComponent) {
|
|
815
|
+
start.jsxNode.rendered();
|
|
816
|
+
}
|
|
817
|
+
});
|
|
818
|
+
};
|
|
819
|
+
const addCreateCommit = (start) => {
|
|
820
|
+
commits.push(() => {
|
|
821
|
+
const children = this.createViewByAtom(start);
|
|
822
|
+
children.forEach(child => {
|
|
823
|
+
if (context.isParent) {
|
|
824
|
+
this.nativeRenderer.prependChild(context.host, child);
|
|
825
|
+
}
|
|
826
|
+
else {
|
|
827
|
+
this.nativeRenderer.insertAfter(child, context.host);
|
|
828
|
+
}
|
|
829
|
+
context.host = child;
|
|
830
|
+
context.isParent = false;
|
|
831
|
+
});
|
|
832
|
+
});
|
|
833
|
+
};
|
|
834
|
+
while (start && !start.nativeNode) {
|
|
835
|
+
const reusedAtom = this.reuseAndUpdate(start, oldChildren);
|
|
836
|
+
if (reusedAtom) {
|
|
837
|
+
addReuseCommit(start, reusedAtom);
|
|
838
|
+
}
|
|
839
|
+
else {
|
|
840
|
+
addCreateCommit(start);
|
|
841
|
+
}
|
|
842
|
+
start = start.sibling;
|
|
843
|
+
}
|
|
844
|
+
for (const atom of oldChildren) {
|
|
845
|
+
this.cleanView(atom, false);
|
|
846
|
+
}
|
|
847
|
+
for (const commit of commits) {
|
|
848
|
+
commit();
|
|
849
|
+
}
|
|
850
|
+
}
|
|
851
|
+
cleanView(atom, isClean) {
|
|
852
|
+
if (atom.nativeNode) {
|
|
853
|
+
if (!isClean) {
|
|
854
|
+
this.nativeRenderer.remove(atom.nativeNode);
|
|
855
|
+
isClean = true;
|
|
856
|
+
}
|
|
857
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
858
|
+
const ref = atom.jsxNode.props.attrs.get(refKey);
|
|
859
|
+
if (ref instanceof Ref) {
|
|
860
|
+
ref.unListen();
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
let child = atom.child;
|
|
865
|
+
while (child) {
|
|
866
|
+
this.cleanView(child, isClean);
|
|
867
|
+
child = child.sibling;
|
|
868
|
+
}
|
|
869
|
+
if (atom.jsxNode instanceof Component) {
|
|
870
|
+
atom.jsxNode.destroy();
|
|
871
|
+
}
|
|
872
|
+
}
|
|
873
|
+
reuseAndUpdate(start, oldChildren) {
|
|
874
|
+
for (let i = 0; i < oldChildren.length; i++) {
|
|
875
|
+
const diffAtom = oldChildren[i];
|
|
876
|
+
if (start.jsxNode instanceof JSXElement) {
|
|
877
|
+
if (diffAtom.jsxNode instanceof JSXElement && start.jsxNode.name === diffAtom.jsxNode.name) {
|
|
878
|
+
const nativeNode = diffAtom.nativeNode;
|
|
879
|
+
start.nativeNode = nativeNode;
|
|
880
|
+
this.updateNativeNodeProperties(start.jsxNode, diffAtom.jsxNode, nativeNode);
|
|
881
|
+
oldChildren.splice(i, 1);
|
|
882
|
+
return diffAtom;
|
|
883
|
+
}
|
|
884
|
+
}
|
|
885
|
+
else if (start.jsxNode instanceof JSXText) {
|
|
886
|
+
if (diffAtom.jsxNode instanceof JSXText) {
|
|
887
|
+
const nativeNode = diffAtom.nativeNode;
|
|
888
|
+
if (start.jsxNode.text !== diffAtom.jsxNode.text) {
|
|
889
|
+
this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
|
|
890
|
+
}
|
|
891
|
+
start.nativeNode = nativeNode;
|
|
892
|
+
oldChildren.splice(i, 1);
|
|
893
|
+
return diffAtom;
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
else if (diffAtom.jsxNode instanceof Component) {
|
|
897
|
+
if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
|
|
898
|
+
const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
|
|
899
|
+
if (isChanged) {
|
|
900
|
+
diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
|
|
901
|
+
}
|
|
902
|
+
start.jsxNode = diffAtom.jsxNode;
|
|
903
|
+
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
904
|
+
const template = render();
|
|
905
|
+
if (template) {
|
|
906
|
+
this.linkTemplate(template, start.jsxNode, start);
|
|
907
|
+
}
|
|
908
|
+
this.componentAtomCaches.set(start.jsxNode, {
|
|
909
|
+
render,
|
|
910
|
+
atom: start
|
|
911
|
+
});
|
|
912
|
+
oldChildren.splice(i, 1);
|
|
913
|
+
return diffAtom;
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return null;
|
|
918
|
+
}
|
|
919
|
+
createViewByAtom(atom) {
|
|
920
|
+
if (atom.jsxNode instanceof JSXElement) {
|
|
921
|
+
const nativeNode = this.createElement(atom.jsxNode);
|
|
922
|
+
atom.nativeNode = nativeNode;
|
|
923
|
+
if (atom.child) {
|
|
924
|
+
const children = this.buildView(atom.child);
|
|
925
|
+
for (const child of children) {
|
|
926
|
+
this.nativeRenderer.appendChild(nativeNode, child);
|
|
927
|
+
}
|
|
928
|
+
}
|
|
929
|
+
return [nativeNode];
|
|
930
|
+
}
|
|
931
|
+
else if (atom.jsxNode instanceof JSXText) {
|
|
932
|
+
const nativeNode = this.createTextNode(atom.jsxNode);
|
|
933
|
+
atom.nativeNode = nativeNode;
|
|
934
|
+
return [nativeNode];
|
|
935
|
+
}
|
|
936
|
+
const { template, render } = atom.jsxNode.init();
|
|
937
|
+
this.componentAtomCaches.set(atom.jsxNode, {
|
|
938
|
+
atom,
|
|
939
|
+
render
|
|
940
|
+
});
|
|
941
|
+
if (template) {
|
|
942
|
+
this.linkTemplate(template, atom.jsxNode, atom);
|
|
943
|
+
}
|
|
944
|
+
if (atom.child) {
|
|
945
|
+
return this.buildView(atom.child);
|
|
946
|
+
}
|
|
947
|
+
return [];
|
|
948
|
+
}
|
|
949
|
+
buildView(chain) {
|
|
950
|
+
const context = [];
|
|
951
|
+
const children = [];
|
|
952
|
+
function getContext() {
|
|
953
|
+
return context[context.length - 1];
|
|
954
|
+
}
|
|
955
|
+
let atom = chain;
|
|
956
|
+
const stopAtom = chain.parent;
|
|
957
|
+
wrap: while (atom) {
|
|
958
|
+
if (atom.jsxNode instanceof Component) {
|
|
959
|
+
this.componentRender(atom.jsxNode, atom);
|
|
960
|
+
if (atom.child) {
|
|
961
|
+
atom = atom.child;
|
|
962
|
+
continue;
|
|
963
|
+
}
|
|
964
|
+
atom.jsxNode.rendered();
|
|
965
|
+
}
|
|
966
|
+
else {
|
|
967
|
+
const host = getContext();
|
|
968
|
+
const nativeNode = atom.jsxNode instanceof JSXElement ? this.createElement(atom.jsxNode) : this.createTextNode(atom.jsxNode);
|
|
969
|
+
atom.nativeNode = nativeNode;
|
|
970
|
+
if (host) {
|
|
971
|
+
this.nativeRenderer.appendChild(host, nativeNode);
|
|
972
|
+
}
|
|
973
|
+
else {
|
|
974
|
+
children.push(nativeNode);
|
|
975
|
+
}
|
|
976
|
+
if (atom.child) {
|
|
977
|
+
context.push(nativeNode);
|
|
978
|
+
atom = atom.child;
|
|
979
|
+
continue;
|
|
980
|
+
}
|
|
981
|
+
}
|
|
982
|
+
while (atom) {
|
|
983
|
+
if (atom.sibling) {
|
|
984
|
+
atom = atom.sibling;
|
|
985
|
+
break;
|
|
986
|
+
}
|
|
987
|
+
atom = atom.parent;
|
|
988
|
+
const isComponent = (atom === null || atom === void 0 ? void 0 : atom.jsxNode) instanceof Component;
|
|
989
|
+
if (isComponent) {
|
|
990
|
+
atom.jsxNode.rendered();
|
|
991
|
+
}
|
|
992
|
+
if (atom === stopAtom) {
|
|
993
|
+
break wrap;
|
|
994
|
+
}
|
|
995
|
+
if (isComponent) {
|
|
996
|
+
continue;
|
|
997
|
+
}
|
|
998
|
+
context.pop();
|
|
999
|
+
}
|
|
1000
|
+
}
|
|
1001
|
+
return children;
|
|
1002
|
+
}
|
|
1003
|
+
componentRender(component, parent) {
|
|
1004
|
+
const { template, render } = component.init();
|
|
1005
|
+
if (template) {
|
|
1006
|
+
this.linkTemplate(template, component, parent);
|
|
1007
|
+
}
|
|
1008
|
+
this.componentAtomCaches.set(component, {
|
|
1009
|
+
render,
|
|
1010
|
+
atom: parent
|
|
1011
|
+
});
|
|
1012
|
+
return parent;
|
|
1013
|
+
}
|
|
1014
|
+
createChainByComponentFactory(context, factory, parent) {
|
|
1015
|
+
const component = factory(context);
|
|
1016
|
+
if (component.setup === Fragment) {
|
|
1017
|
+
return this.createChainByChildren(component, component.props.children, parent);
|
|
1018
|
+
}
|
|
1019
|
+
return new Atom(component, parent);
|
|
1020
|
+
}
|
|
1021
|
+
createChain(context, template, parent) {
|
|
1022
|
+
if (template instanceof JSXElement) {
|
|
1023
|
+
return this.createChainByJSXElement(context, template, parent);
|
|
1024
|
+
}
|
|
1025
|
+
if (template instanceof JSXText) {
|
|
1026
|
+
return this.createChainByJSXText(template, parent);
|
|
1027
|
+
}
|
|
1028
|
+
return this.createChainByComponentFactory(context, template, parent);
|
|
1029
|
+
}
|
|
1030
|
+
createChainByJSXElement(context, element, parent) {
|
|
1031
|
+
const atom = new Atom(element, parent);
|
|
1032
|
+
const children = this.createChainByChildren(context, element.props.children, atom);
|
|
1033
|
+
this.link(atom, children);
|
|
1034
|
+
return atom;
|
|
1035
|
+
}
|
|
1036
|
+
createChainByJSXText(node, parent) {
|
|
1037
|
+
return new Atom(node, parent);
|
|
1038
|
+
}
|
|
1039
|
+
createChainByChildren(context, children, parent) {
|
|
1040
|
+
const atoms = [];
|
|
1041
|
+
for (const item of children) {
|
|
1042
|
+
const child = this.createChain(context, item, parent);
|
|
1043
|
+
if (Array.isArray(child)) {
|
|
1044
|
+
atoms.push(...child);
|
|
1045
|
+
}
|
|
1046
|
+
else {
|
|
1047
|
+
atoms.push(child);
|
|
1048
|
+
}
|
|
1049
|
+
}
|
|
1050
|
+
return atoms;
|
|
1051
|
+
}
|
|
1052
|
+
linkTemplate(template, component, parent) {
|
|
1053
|
+
if (template) {
|
|
1054
|
+
const child = this.createChain(component, template, parent);
|
|
1055
|
+
this.link(parent, Array.isArray(child) ? child : [child]);
|
|
1056
|
+
}
|
|
1057
|
+
}
|
|
1058
|
+
link(parent, children) {
|
|
1059
|
+
for (let i = 1; i < children.length; i++) {
|
|
1060
|
+
const prev = children[i - 1];
|
|
1061
|
+
prev.sibling = children[i];
|
|
1062
|
+
}
|
|
1063
|
+
parent.child = children[0] || null;
|
|
1064
|
+
}
|
|
1065
|
+
createElement(vNode) {
|
|
1066
|
+
const nativeNode = this.nativeRenderer.createElement(vNode.name);
|
|
1067
|
+
const props = vNode.props;
|
|
1068
|
+
if (props) {
|
|
1069
|
+
props.attrs.forEach((value, key) => {
|
|
1070
|
+
if (key === refKey && value instanceof Ref) {
|
|
1071
|
+
value.update(nativeNode);
|
|
1072
|
+
return;
|
|
1073
|
+
}
|
|
1074
|
+
this.nativeRenderer.setProperty(nativeNode, key, value);
|
|
1075
|
+
});
|
|
1076
|
+
props.styles.forEach((value, key) => {
|
|
1077
|
+
this.nativeRenderer.setStyle(nativeNode, key, value);
|
|
1078
|
+
});
|
|
1079
|
+
props.classes.forEach(k => this.nativeRenderer.addClass(nativeNode, k));
|
|
1080
|
+
Object.keys(props.listeners).forEach(type => {
|
|
1081
|
+
this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
|
|
1082
|
+
});
|
|
1083
|
+
}
|
|
1084
|
+
return nativeNode;
|
|
1085
|
+
}
|
|
1086
|
+
createTextNode(child) {
|
|
1087
|
+
return this.nativeRenderer.createTextNode(child.text);
|
|
1088
|
+
}
|
|
1089
|
+
updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
|
|
1090
|
+
const { styleChanges, attrChanges, classesChanges, listenerChanges, isChanged } = getNodeChanges(newVNode, oldVNode);
|
|
1091
|
+
if (!isChanged) {
|
|
1092
|
+
return;
|
|
1093
|
+
}
|
|
1094
|
+
styleChanges.remove.forEach(i => this.nativeRenderer.removeStyle(nativeNode, i[0]));
|
|
1095
|
+
styleChanges.set.forEach(i => this.nativeRenderer.setStyle(nativeNode, i[0], i[1]));
|
|
1096
|
+
attrChanges.remove.forEach(([key, value]) => {
|
|
1097
|
+
if (key === refKey && value instanceof Ref) {
|
|
1098
|
+
value.unListen();
|
|
1099
|
+
return;
|
|
1100
|
+
}
|
|
1101
|
+
this.nativeRenderer.removeProperty(nativeNode, key);
|
|
1102
|
+
});
|
|
1103
|
+
attrChanges.set.forEach(([key, value]) => {
|
|
1104
|
+
if (key === refKey && value instanceof Ref) {
|
|
1105
|
+
value.update(nativeNode);
|
|
1106
|
+
return;
|
|
1107
|
+
}
|
|
1108
|
+
this.nativeRenderer.setProperty(nativeNode, key, value);
|
|
1109
|
+
});
|
|
1110
|
+
classesChanges.remove.forEach(i => this.nativeRenderer.removeClass(nativeNode, i));
|
|
1111
|
+
classesChanges.add.forEach(i => this.nativeRenderer.addClass(nativeNode, i));
|
|
1112
|
+
listenerChanges.remove.forEach(i => {
|
|
1113
|
+
this.nativeRenderer.unListen(nativeNode, i[0], i[1]);
|
|
1114
|
+
});
|
|
1115
|
+
listenerChanges.add.forEach(i => {
|
|
1116
|
+
this.nativeRenderer.listen(nativeNode, i[0], i[1]);
|
|
1117
|
+
});
|
|
1118
|
+
}
|
|
1119
|
+
};
|
|
1120
|
+
Renderer = __decorate([
|
|
1121
|
+
Injectable(),
|
|
1122
|
+
__metadata("design:paramtypes", [NativeRenderer,
|
|
1123
|
+
RootComponentRef])
|
|
1124
|
+
], Renderer);
|
|
1125
|
+
|
|
1126
|
+
const viewflyErrorFn = makeError('Viewfly');
|
|
1127
|
+
/**
|
|
1128
|
+
* Viewfly 核心类,用于启动一个 Viewfly 应用
|
|
1129
|
+
*/
|
|
1130
|
+
class Viewfly extends ReflectiveInjector {
|
|
1131
|
+
constructor(config) {
|
|
1132
|
+
super(new NullInjector(), [
|
|
1133
|
+
...(config.providers || []),
|
|
1134
|
+
Renderer,
|
|
1135
|
+
{
|
|
1136
|
+
provide: RootComponentRef,
|
|
1137
|
+
useFactory: () => {
|
|
1138
|
+
return {
|
|
1139
|
+
host: config.host,
|
|
1140
|
+
component: this.rootComponent
|
|
1141
|
+
};
|
|
1142
|
+
}
|
|
1143
|
+
},
|
|
1144
|
+
{
|
|
1145
|
+
provide: NativeRenderer,
|
|
1146
|
+
useFactory() {
|
|
1147
|
+
throw viewflyErrorFn('You must implement the `NativeRenderer` interface to start Viewfly!');
|
|
1148
|
+
}
|
|
1149
|
+
}
|
|
1150
|
+
]);
|
|
1151
|
+
this.config = config;
|
|
1152
|
+
this.destroyed = false;
|
|
1153
|
+
this.subscription = new Subscription();
|
|
1154
|
+
this.rootComponent = this.createRootComponent(config.root);
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* 启动 Viewfly
|
|
1158
|
+
*/
|
|
1159
|
+
start() {
|
|
1160
|
+
const renderer = this.get(Renderer);
|
|
1161
|
+
renderer.render();
|
|
1162
|
+
if (this.config.autoUpdate === false) {
|
|
1163
|
+
return;
|
|
1164
|
+
}
|
|
1165
|
+
this.subscription.add(this.rootComponent.changeEmitter.pipe(microTask()).subscribe(() => {
|
|
1166
|
+
renderer.refresh();
|
|
1167
|
+
}));
|
|
1168
|
+
}
|
|
1169
|
+
/**
|
|
1170
|
+
* 销毁 Viewfly 实例
|
|
1171
|
+
*/
|
|
1172
|
+
destroy() {
|
|
1173
|
+
const renderer = this.get(Renderer);
|
|
1174
|
+
this.destroyed = true;
|
|
1175
|
+
this.rootComponent.markAsDirtied();
|
|
1176
|
+
this.subscription.unsubscribe();
|
|
1177
|
+
renderer.refresh();
|
|
1178
|
+
}
|
|
1179
|
+
createRootComponent(rootNode) {
|
|
1180
|
+
return new RootComponent(() => {
|
|
1181
|
+
return () => {
|
|
1182
|
+
return this.destroyed ? null : rootNode;
|
|
1183
|
+
};
|
|
1184
|
+
});
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
export { Component, Fragment, JSXElement, JSXFragment, JSXText, NativeRenderer, Props, Ref, Renderer, RootComponent, RootComponentRef, Viewfly, inject, jsx, jsxs, onDestroy, onMount, onPropsChanged, onUpdated, provide, useEffect, useRef, useSignal };
|