@viewfly/core 0.0.3 → 0.0.5
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/renderer.d.ts +2 -0
- package/bundles/index.esm.js +235 -141
- package/bundles/index.js +236 -140
- package/bundles/model/_api.d.ts +1 -0
- package/bundles/model/component.d.ts +4 -3
- package/bundles/model/jsx-element.d.ts +5 -0
- package/bundles/model/memo.d.ts +10 -0
- package/package.json +2 -2
|
@@ -16,6 +16,7 @@ export declare class Renderer {
|
|
|
16
16
|
private reconcileElement;
|
|
17
17
|
private applyChanges;
|
|
18
18
|
private diff;
|
|
19
|
+
private reuseComponentView;
|
|
19
20
|
private createChanges;
|
|
20
21
|
private cleanView;
|
|
21
22
|
private buildView;
|
|
@@ -30,4 +31,5 @@ export declare class Renderer {
|
|
|
30
31
|
private createTextNode;
|
|
31
32
|
private updateNativeNodeProperties;
|
|
32
33
|
private applyRefs;
|
|
34
|
+
private bindEvent;
|
|
33
35
|
}
|
package/bundles/index.esm.js
CHANGED
|
@@ -43,6 +43,80 @@ function __metadata(metadataKey, metadataValue) {
|
|
|
43
43
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
44
44
|
}
|
|
45
45
|
|
|
46
|
+
const refKey = 'ref';
|
|
47
|
+
function getObjectChanges(newProps, oldProps) {
|
|
48
|
+
const changes = {
|
|
49
|
+
remove: [],
|
|
50
|
+
add: [],
|
|
51
|
+
replace: []
|
|
52
|
+
};
|
|
53
|
+
Object.keys(newProps).forEach(key => {
|
|
54
|
+
const leftValue = newProps[key];
|
|
55
|
+
const rightValue = oldProps[key];
|
|
56
|
+
if (Reflect.has(oldProps, key)) {
|
|
57
|
+
if (leftValue !== rightValue) {
|
|
58
|
+
changes.replace.push([key, leftValue, rightValue]);
|
|
59
|
+
}
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
changes.add.push([key, leftValue]);
|
|
63
|
+
});
|
|
64
|
+
Object.keys(oldProps).forEach(key => {
|
|
65
|
+
if (!Reflect.has(newProps, key)) {
|
|
66
|
+
changes.remove.push([key, oldProps[key]]);
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return changes;
|
|
70
|
+
}
|
|
71
|
+
function classToString(config) {
|
|
72
|
+
if (!config) {
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
if (typeof config === 'string') {
|
|
76
|
+
return config;
|
|
77
|
+
}
|
|
78
|
+
else if (Array.isArray(config)) {
|
|
79
|
+
return config.map(i => {
|
|
80
|
+
return classToString(i);
|
|
81
|
+
}).join(' ');
|
|
82
|
+
}
|
|
83
|
+
else if (typeof config === 'object') {
|
|
84
|
+
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
85
|
+
return config.toString();
|
|
86
|
+
}
|
|
87
|
+
const classes = [];
|
|
88
|
+
for (const key in config) {
|
|
89
|
+
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
90
|
+
classes.push(key);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return classes.join(' ');
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function styleToObject(style) {
|
|
97
|
+
if (typeof style !== 'string') {
|
|
98
|
+
return style;
|
|
99
|
+
}
|
|
100
|
+
const obj = {};
|
|
101
|
+
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
102
|
+
if (!v[0] || !v[1]) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
obj[v[0].trim()] = v[1].trim();
|
|
106
|
+
});
|
|
107
|
+
return obj;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
class Memo {
|
|
111
|
+
constructor(shouldUpdate, render) {
|
|
112
|
+
this.shouldUpdate = shouldUpdate;
|
|
113
|
+
this.render = render;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
function withMemo(shouldUpdate, render) {
|
|
117
|
+
return new Memo(shouldUpdate, render);
|
|
118
|
+
}
|
|
119
|
+
|
|
46
120
|
const componentSetupStack = [];
|
|
47
121
|
const signalDepsStack = [];
|
|
48
122
|
const componentErrorFn = makeError('component');
|
|
@@ -122,17 +196,29 @@ class Component extends ReflectiveInjector {
|
|
|
122
196
|
isSetup = false;
|
|
123
197
|
componentSetupStack.pop();
|
|
124
198
|
signalDepsStack.push([]);
|
|
125
|
-
const
|
|
199
|
+
const templateRender = render instanceof Memo ? render.render : render;
|
|
200
|
+
let template = templateRender();
|
|
126
201
|
const deps = signalDepsStack.pop();
|
|
127
202
|
this.unWatch = useEffect(deps, () => {
|
|
128
203
|
this.markAsDirtied();
|
|
129
204
|
});
|
|
130
205
|
return {
|
|
131
206
|
template,
|
|
132
|
-
render: () => {
|
|
207
|
+
render: (newProps, oldProps) => {
|
|
208
|
+
if (newProps !== oldProps) {
|
|
209
|
+
const { add, remove, replace } = getObjectChanges(newProps, oldProps);
|
|
210
|
+
if (add.length || remove.length || replace.length) {
|
|
211
|
+
this.invokePropsChangedHooks(newProps);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
if (render instanceof Memo) {
|
|
215
|
+
if (!render.shouldUpdate(newProps, oldProps)) {
|
|
216
|
+
return template;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
133
219
|
this.unWatch();
|
|
134
220
|
signalDepsStack.push([]);
|
|
135
|
-
|
|
221
|
+
template = templateRender();
|
|
136
222
|
const deps = signalDepsStack.pop();
|
|
137
223
|
this.unWatch = useEffect(deps, () => {
|
|
138
224
|
this.markAsDirtied();
|
|
@@ -164,20 +250,6 @@ class Component extends ReflectiveInjector {
|
|
|
164
250
|
this.invokeUpdatedHooks();
|
|
165
251
|
}
|
|
166
252
|
}
|
|
167
|
-
invokePropsChangedHooks(newProps) {
|
|
168
|
-
const oldProps = this.props;
|
|
169
|
-
this.props = newProps;
|
|
170
|
-
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
171
|
-
fn();
|
|
172
|
-
});
|
|
173
|
-
this.propsChangedDestroyCallbacks = [];
|
|
174
|
-
for (const fn of this.propsChangedCallbacks) {
|
|
175
|
-
const destroyFn = fn(newProps, oldProps);
|
|
176
|
-
if (typeof destroyFn === 'function') {
|
|
177
|
-
this.propsChangedDestroyCallbacks.push(destroyFn);
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
253
|
destroy() {
|
|
182
254
|
this.unWatch();
|
|
183
255
|
this.updatedDestroyCallbacks.forEach(fn => {
|
|
@@ -195,6 +267,20 @@ class Component extends ReflectiveInjector {
|
|
|
195
267
|
this.mountCallbacks = [];
|
|
196
268
|
this.updatedCallbacks = [];
|
|
197
269
|
}
|
|
270
|
+
invokePropsChangedHooks(newProps) {
|
|
271
|
+
const oldProps = this.props;
|
|
272
|
+
this.props = newProps;
|
|
273
|
+
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
274
|
+
fn();
|
|
275
|
+
});
|
|
276
|
+
this.propsChangedDestroyCallbacks = [];
|
|
277
|
+
for (const fn of this.propsChangedCallbacks) {
|
|
278
|
+
const destroyFn = fn(newProps, oldProps);
|
|
279
|
+
if (typeof destroyFn === 'function') {
|
|
280
|
+
this.propsChangedDestroyCallbacks.push(destroyFn);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}
|
|
198
284
|
invokeMountHooks() {
|
|
199
285
|
for (const fn of this.mountCallbacks) {
|
|
200
286
|
const destroyFn = fn();
|
|
@@ -383,13 +469,25 @@ function useSignal(state) {
|
|
|
383
469
|
return;
|
|
384
470
|
}
|
|
385
471
|
state = newState;
|
|
386
|
-
|
|
472
|
+
const depCallbacks = Array.from(signal[depsKey]);
|
|
473
|
+
for (const fn of depCallbacks) {
|
|
474
|
+
// 回调中可能会对依赖做出修改,故需先缓存起来
|
|
387
475
|
fn();
|
|
388
476
|
}
|
|
389
477
|
};
|
|
390
478
|
signal[depsKey] = new Set();
|
|
391
479
|
return signal;
|
|
392
480
|
}
|
|
481
|
+
function invokeDepFn(fn) {
|
|
482
|
+
const deps = [];
|
|
483
|
+
signalDepsStack.push(deps);
|
|
484
|
+
const data = fn();
|
|
485
|
+
signalDepsStack.pop();
|
|
486
|
+
return {
|
|
487
|
+
deps,
|
|
488
|
+
data
|
|
489
|
+
};
|
|
490
|
+
}
|
|
393
491
|
/**
|
|
394
492
|
* 使用派生值,Viewfly 会收集回调函数内同步执行时访问的 Signal,
|
|
395
493
|
* 并在你获取 useDerived 函数返回的 Signal 的值时,自动计算最新的值。
|
|
@@ -398,18 +496,32 @@ function useSignal(state) {
|
|
|
398
496
|
* @param isContinue 可选的停止函数,在每次值更新后调用,当返回值为 false 时,将不再监听依赖的变化
|
|
399
497
|
*/
|
|
400
498
|
function useDerived(callback, isContinue) {
|
|
401
|
-
|
|
402
|
-
signalDepsStack.push(deps);
|
|
403
|
-
const data = callback();
|
|
404
|
-
signalDepsStack.pop();
|
|
499
|
+
let { data, deps } = invokeDepFn(callback);
|
|
405
500
|
const signal = useSignal(data);
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
501
|
+
const component = getSetupContext(false);
|
|
502
|
+
const unListenRef = {};
|
|
503
|
+
function listen(model, deps, callback, unListenRef, isContinue) {
|
|
504
|
+
const nextListen = () => {
|
|
505
|
+
unListenRef.unListen();
|
|
506
|
+
const { data: nextData, deps: nextDeps } = invokeDepFn(callback);
|
|
507
|
+
model.set(nextData);
|
|
508
|
+
if (typeof isContinue !== 'function' || isContinue(nextData) !== false) {
|
|
509
|
+
listen(model, nextDeps, callback, unListenRef, isContinue);
|
|
510
|
+
}
|
|
511
|
+
};
|
|
512
|
+
unListenRef.unListen = () => {
|
|
513
|
+
for (const s of deps) {
|
|
514
|
+
s[depsKey].delete(nextListen);
|
|
412
515
|
}
|
|
516
|
+
};
|
|
517
|
+
for (const s of deps) {
|
|
518
|
+
s[depsKey].add(nextListen);
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
listen(signal, deps, callback, unListenRef, isContinue);
|
|
522
|
+
if (component) {
|
|
523
|
+
component.destroyCallbacks.push(() => {
|
|
524
|
+
unListenRef.unListen();
|
|
413
525
|
});
|
|
414
526
|
}
|
|
415
527
|
return signal;
|
|
@@ -528,70 +640,6 @@ class RootComponent extends Component {
|
|
|
528
640
|
}
|
|
529
641
|
}
|
|
530
642
|
|
|
531
|
-
const refKey = 'ref';
|
|
532
|
-
function getObjectChanges(newProps, oldProps) {
|
|
533
|
-
const changes = {
|
|
534
|
-
remove: [],
|
|
535
|
-
add: [],
|
|
536
|
-
replace: []
|
|
537
|
-
};
|
|
538
|
-
Object.keys(newProps).forEach(key => {
|
|
539
|
-
const leftValue = newProps[key];
|
|
540
|
-
const rightValue = oldProps[key];
|
|
541
|
-
if (Reflect.has(oldProps, key)) {
|
|
542
|
-
if (leftValue !== rightValue) {
|
|
543
|
-
changes.replace.push([key, leftValue, rightValue]);
|
|
544
|
-
}
|
|
545
|
-
return;
|
|
546
|
-
}
|
|
547
|
-
changes.add.push([key, leftValue]);
|
|
548
|
-
});
|
|
549
|
-
Object.keys(oldProps).forEach(key => {
|
|
550
|
-
if (!Reflect.has(newProps, key)) {
|
|
551
|
-
changes.remove.push([key, oldProps[key]]);
|
|
552
|
-
}
|
|
553
|
-
});
|
|
554
|
-
return changes;
|
|
555
|
-
}
|
|
556
|
-
function classToString(config) {
|
|
557
|
-
if (!config) {
|
|
558
|
-
return '';
|
|
559
|
-
}
|
|
560
|
-
if (typeof config === 'string') {
|
|
561
|
-
return config;
|
|
562
|
-
}
|
|
563
|
-
else if (Array.isArray(config)) {
|
|
564
|
-
return config.map(i => {
|
|
565
|
-
return classToString(i);
|
|
566
|
-
}).join(' ');
|
|
567
|
-
}
|
|
568
|
-
else if (typeof config === 'object') {
|
|
569
|
-
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
570
|
-
return config.toString();
|
|
571
|
-
}
|
|
572
|
-
const classes = [];
|
|
573
|
-
for (const key in config) {
|
|
574
|
-
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
575
|
-
classes.push(key);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
return classes.join(' ');
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
function styleToObject(style) {
|
|
582
|
-
if (typeof style !== 'string') {
|
|
583
|
-
return style;
|
|
584
|
-
}
|
|
585
|
-
const obj = {};
|
|
586
|
-
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
587
|
-
if (!v[0] || !v[1]) {
|
|
588
|
-
return;
|
|
589
|
-
}
|
|
590
|
-
obj[v[0].trim()] = v[1].trim();
|
|
591
|
-
});
|
|
592
|
-
return obj;
|
|
593
|
-
}
|
|
594
|
-
|
|
595
643
|
class RootComponentRef {
|
|
596
644
|
}
|
|
597
645
|
class Atom {
|
|
@@ -687,7 +735,7 @@ let Renderer = class Renderer {
|
|
|
687
735
|
applyChanges(component, context) {
|
|
688
736
|
const { atom, render } = this.componentAtomCaches.get(component);
|
|
689
737
|
const diffAtom = atom.child;
|
|
690
|
-
const template = render();
|
|
738
|
+
const template = render(component.props, component.props);
|
|
691
739
|
if (template) {
|
|
692
740
|
this.linkTemplate(template, component, atom);
|
|
693
741
|
}
|
|
@@ -708,26 +756,28 @@ let Renderer = class Renderer {
|
|
|
708
756
|
}
|
|
709
757
|
const commits = [];
|
|
710
758
|
const changeCommits = {
|
|
711
|
-
|
|
712
|
-
commits.push(() => {
|
|
713
|
-
const
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
}
|
|
717
|
-
const
|
|
718
|
-
|
|
719
|
-
start.jsxNode.props = newProps;
|
|
720
|
-
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
721
|
-
const template = render();
|
|
722
|
-
if (template) {
|
|
723
|
-
this.linkTemplate(template, start.jsxNode, start);
|
|
724
|
-
}
|
|
725
|
-
this.componentAtomCaches.set(start.jsxNode, {
|
|
759
|
+
updateComponent: (newAtom, reusedAtom, expectIndex, diffIndex) => {
|
|
760
|
+
commits.push((offset) => {
|
|
761
|
+
const newProps = newAtom.jsxNode.props;
|
|
762
|
+
const oldProps = reusedAtom.jsxNode.props;
|
|
763
|
+
newAtom.jsxNode = reusedAtom.jsxNode;
|
|
764
|
+
const { render, template } = this.componentAtomCaches.get(newAtom.jsxNode);
|
|
765
|
+
const newTemplate = render(newProps, oldProps);
|
|
766
|
+
this.componentAtomCaches.set(newAtom.jsxNode, {
|
|
726
767
|
render,
|
|
727
|
-
|
|
768
|
+
template,
|
|
769
|
+
atom: newAtom
|
|
728
770
|
});
|
|
729
|
-
if (
|
|
730
|
-
this.
|
|
771
|
+
if (newTemplate === template) {
|
|
772
|
+
this.reuseComponentView(newAtom, reusedAtom, context, expectIndex !== diffIndex - offset);
|
|
773
|
+
// (newAtom.jsxNode as Component).rendered()
|
|
774
|
+
return;
|
|
775
|
+
}
|
|
776
|
+
if (newTemplate) {
|
|
777
|
+
this.linkTemplate(newTemplate, newAtom.jsxNode, newAtom);
|
|
778
|
+
}
|
|
779
|
+
if (newAtom.child) {
|
|
780
|
+
this.diff(newAtom.child, reusedAtom.child, context, expectIndex, diffIndex);
|
|
731
781
|
}
|
|
732
782
|
else if (reusedAtom.child) {
|
|
733
783
|
let atom = reusedAtom.child;
|
|
@@ -736,10 +786,10 @@ let Renderer = class Renderer {
|
|
|
736
786
|
atom = atom.sibling;
|
|
737
787
|
}
|
|
738
788
|
}
|
|
739
|
-
|
|
789
|
+
newAtom.jsxNode.rendered();
|
|
740
790
|
});
|
|
741
791
|
},
|
|
742
|
-
|
|
792
|
+
updateElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
743
793
|
commits.push((offset) => {
|
|
744
794
|
newAtom.nativeNode = oldAtom.nativeNode;
|
|
745
795
|
const host = context.host;
|
|
@@ -770,7 +820,7 @@ let Renderer = class Renderer {
|
|
|
770
820
|
applyRefs();
|
|
771
821
|
});
|
|
772
822
|
},
|
|
773
|
-
|
|
823
|
+
updateText: (newAtom, oldAtom) => {
|
|
774
824
|
commits.push(() => {
|
|
775
825
|
const nativeNode = oldAtom.nativeNode;
|
|
776
826
|
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
@@ -812,6 +862,40 @@ let Renderer = class Renderer {
|
|
|
812
862
|
commit(offset);
|
|
813
863
|
}
|
|
814
864
|
}
|
|
865
|
+
reuseComponentView(newAtom, reusedAtom, context, moveView) {
|
|
866
|
+
let child = reusedAtom.child;
|
|
867
|
+
newAtom.child = child;
|
|
868
|
+
const children = [];
|
|
869
|
+
while (child) {
|
|
870
|
+
children.push(child);
|
|
871
|
+
child.parent = newAtom;
|
|
872
|
+
child = child.sibling;
|
|
873
|
+
}
|
|
874
|
+
const updateContext = (atom) => {
|
|
875
|
+
if (atom.jsxNode instanceof Component) {
|
|
876
|
+
let child = atom.child;
|
|
877
|
+
while (child) {
|
|
878
|
+
updateContext(child);
|
|
879
|
+
child = child.sibling;
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
else {
|
|
883
|
+
if (moveView) {
|
|
884
|
+
if (context.isParent) {
|
|
885
|
+
this.nativeRenderer.prependChild(context.host, atom.nativeNode);
|
|
886
|
+
}
|
|
887
|
+
else {
|
|
888
|
+
this.nativeRenderer.insertAfter(atom.nativeNode, context.host);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
context.isParent = false;
|
|
892
|
+
context.host = atom.nativeNode;
|
|
893
|
+
}
|
|
894
|
+
};
|
|
895
|
+
for (const atom of children) {
|
|
896
|
+
updateContext(atom);
|
|
897
|
+
}
|
|
898
|
+
}
|
|
815
899
|
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
816
900
|
for (let i = 0; i < oldChildren.length; i++) {
|
|
817
901
|
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
@@ -824,13 +908,13 @@ let Renderer = class Renderer {
|
|
|
824
908
|
}
|
|
825
909
|
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
826
910
|
if (newAtom.jsxNode instanceof JSXElement) {
|
|
827
|
-
changeCommits.
|
|
911
|
+
changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
828
912
|
}
|
|
829
913
|
else if (newAtom.jsxNode instanceof JSXText) {
|
|
830
|
-
changeCommits.
|
|
914
|
+
changeCommits.updateText(newAtom, diffAtom);
|
|
831
915
|
}
|
|
832
916
|
else {
|
|
833
|
-
changeCommits.
|
|
917
|
+
changeCommits.updateComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
834
918
|
}
|
|
835
919
|
oldChildren.splice(i, 1);
|
|
836
920
|
return;
|
|
@@ -911,27 +995,20 @@ let Renderer = class Renderer {
|
|
|
911
995
|
}
|
|
912
996
|
this.componentAtomCaches.set(component, {
|
|
913
997
|
render,
|
|
998
|
+
template,
|
|
914
999
|
atom: from
|
|
915
1000
|
});
|
|
916
1001
|
return from;
|
|
917
1002
|
}
|
|
918
1003
|
createChainByComponentFactory(context, factory, parent) {
|
|
919
1004
|
const component = factory.createInstance(context);
|
|
920
|
-
// if (component.setup === Fragment) {
|
|
921
|
-
// const children = component.props.children
|
|
922
|
-
// return this.createChainByChildren(
|
|
923
|
-
// component,
|
|
924
|
-
// Array.isArray(children) ? children : [children],
|
|
925
|
-
// parent
|
|
926
|
-
// )
|
|
927
|
-
// }
|
|
928
1005
|
return new Atom(component, parent);
|
|
929
1006
|
}
|
|
930
1007
|
createChainByJSXElement(context, element, parent) {
|
|
931
1008
|
const atom = new Atom(element, parent);
|
|
932
1009
|
if (Reflect.has(element.props, 'children')) {
|
|
933
1010
|
const jsxChildren = element.props.children;
|
|
934
|
-
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
1011
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom, []);
|
|
935
1012
|
this.link(atom, children);
|
|
936
1013
|
}
|
|
937
1014
|
return atom;
|
|
@@ -939,8 +1016,7 @@ let Renderer = class Renderer {
|
|
|
939
1016
|
createChainByJSXText(node, parent) {
|
|
940
1017
|
return new Atom(node, parent);
|
|
941
1018
|
}
|
|
942
|
-
createChainByChildren(context, children, parent) {
|
|
943
|
-
const atoms = [];
|
|
1019
|
+
createChainByChildren(context, children, parent, atoms) {
|
|
944
1020
|
for (const item of children) {
|
|
945
1021
|
if (item instanceof JSXElement) {
|
|
946
1022
|
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
@@ -956,7 +1032,7 @@ let Renderer = class Renderer {
|
|
|
956
1032
|
continue;
|
|
957
1033
|
}
|
|
958
1034
|
if (Array.isArray(item)) {
|
|
959
|
-
|
|
1035
|
+
this.createChainByChildren(context, item, parent, atoms);
|
|
960
1036
|
continue;
|
|
961
1037
|
}
|
|
962
1038
|
if (item !== null && typeof item !== 'undefined') {
|
|
@@ -967,7 +1043,7 @@ let Renderer = class Renderer {
|
|
|
967
1043
|
}
|
|
968
1044
|
linkTemplate(template, component, parent) {
|
|
969
1045
|
const children = Array.isArray(template) ? template : [template];
|
|
970
|
-
this.link(parent, this.createChainByChildren(component, children, parent));
|
|
1046
|
+
this.link(parent, this.createChainByChildren(component, children, parent, []));
|
|
971
1047
|
}
|
|
972
1048
|
link(parent, children) {
|
|
973
1049
|
for (let i = 1; i < children.length; i++) {
|
|
@@ -986,7 +1062,10 @@ let Renderer = class Renderer {
|
|
|
986
1062
|
continue;
|
|
987
1063
|
}
|
|
988
1064
|
if (key === 'class') {
|
|
989
|
-
|
|
1065
|
+
const className = classToString(props[key]);
|
|
1066
|
+
if (className) {
|
|
1067
|
+
this.nativeRenderer.setClass(nativeNode, className);
|
|
1068
|
+
}
|
|
990
1069
|
continue;
|
|
991
1070
|
}
|
|
992
1071
|
if (key === 'style') {
|
|
@@ -999,7 +1078,7 @@ let Renderer = class Renderer {
|
|
|
999
1078
|
if (/^on[A-Z]/.test(key)) {
|
|
1000
1079
|
const listener = props[key];
|
|
1001
1080
|
if (typeof listener === 'function') {
|
|
1002
|
-
this.
|
|
1081
|
+
this.bindEvent(vNode, key, nativeNode, listener);
|
|
1003
1082
|
}
|
|
1004
1083
|
continue;
|
|
1005
1084
|
}
|
|
@@ -1039,7 +1118,10 @@ let Renderer = class Renderer {
|
|
|
1039
1118
|
}
|
|
1040
1119
|
if (/^on[A-Z]/.test(key)) {
|
|
1041
1120
|
if (typeof value === 'function') {
|
|
1042
|
-
|
|
1121
|
+
const type = key.replace(/^on/, '').toLowerCase();
|
|
1122
|
+
const oldOn = oldVNode.on;
|
|
1123
|
+
this.nativeRenderer.unListen(nativeNode, type, oldOn[type].delegate);
|
|
1124
|
+
Reflect.deleteProperty(oldOn, type);
|
|
1043
1125
|
}
|
|
1044
1126
|
continue;
|
|
1045
1127
|
}
|
|
@@ -1073,12 +1155,8 @@ let Renderer = class Renderer {
|
|
|
1073
1155
|
}
|
|
1074
1156
|
if (/^on[A-Z]/.test(key)) {
|
|
1075
1157
|
const listenType = key.replace(/^on/, '').toLowerCase();
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
}
|
|
1079
|
-
if (typeof newValue === 'function') {
|
|
1080
|
-
this.nativeRenderer.listen(nativeNode, listenType, newValue);
|
|
1081
|
-
}
|
|
1158
|
+
newVNode.on = oldVNode.on;
|
|
1159
|
+
newVNode.on[listenType].listenFn = newValue;
|
|
1082
1160
|
continue;
|
|
1083
1161
|
}
|
|
1084
1162
|
if (key === refKey) {
|
|
@@ -1105,7 +1183,7 @@ let Renderer = class Renderer {
|
|
|
1105
1183
|
}
|
|
1106
1184
|
if (/^on[A-Z]/.test(key)) {
|
|
1107
1185
|
if (typeof value === 'function') {
|
|
1108
|
-
this.
|
|
1186
|
+
this.bindEvent(newVNode, key, nativeNode, value);
|
|
1109
1187
|
}
|
|
1110
1188
|
continue;
|
|
1111
1189
|
}
|
|
@@ -1128,6 +1206,22 @@ let Renderer = class Renderer {
|
|
|
1128
1206
|
}
|
|
1129
1207
|
}
|
|
1130
1208
|
}
|
|
1209
|
+
bindEvent(vNode, key, nativeNode, listenFn) {
|
|
1210
|
+
let on = vNode.on;
|
|
1211
|
+
if (!on) {
|
|
1212
|
+
vNode.on = on = {};
|
|
1213
|
+
}
|
|
1214
|
+
const type = key.replace(/^on/, '').toLowerCase();
|
|
1215
|
+
const delegate = function (...args) {
|
|
1216
|
+
return delegateObj.listenFn.apply(this, args);
|
|
1217
|
+
};
|
|
1218
|
+
const delegateObj = {
|
|
1219
|
+
delegate,
|
|
1220
|
+
listenFn
|
|
1221
|
+
};
|
|
1222
|
+
on[type] = delegateObj;
|
|
1223
|
+
this.nativeRenderer.listen(nativeNode, type, delegate);
|
|
1224
|
+
}
|
|
1131
1225
|
};
|
|
1132
1226
|
Renderer = __decorate([
|
|
1133
1227
|
Injectable(),
|
|
@@ -1199,4 +1293,4 @@ class Viewfly extends ReflectiveInjector {
|
|
|
1199
1293
|
}
|
|
1200
1294
|
}
|
|
1201
1295
|
|
|
1202
|
-
export { Component, Fragment, JSXComponent, JSXElement, JSXText, NativeRenderer, Ref, Renderer, RootComponent, RootComponentRef, Viewfly, inject, jsx, jsxs, makeError, onDestroy, onMounted, onPropsChanged, onUpdated, provide, useDerived, useEffect, useRef, useSignal };
|
|
1296
|
+
export { Component, Fragment, JSXComponent, JSXElement, JSXText, Memo, NativeRenderer, Ref, Renderer, RootComponent, RootComponentRef, Viewfly, inject, jsx, jsxs, makeError, onDestroy, onMounted, onPropsChanged, onUpdated, provide, useDerived, useEffect, useRef, useSignal, withMemo };
|
package/bundles/index.js
CHANGED
|
@@ -44,6 +44,80 @@ function __metadata(metadataKey, metadataValue) {
|
|
|
44
44
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
const refKey = 'ref';
|
|
48
|
+
function getObjectChanges(newProps, oldProps) {
|
|
49
|
+
const changes = {
|
|
50
|
+
remove: [],
|
|
51
|
+
add: [],
|
|
52
|
+
replace: []
|
|
53
|
+
};
|
|
54
|
+
Object.keys(newProps).forEach(key => {
|
|
55
|
+
const leftValue = newProps[key];
|
|
56
|
+
const rightValue = oldProps[key];
|
|
57
|
+
if (Reflect.has(oldProps, key)) {
|
|
58
|
+
if (leftValue !== rightValue) {
|
|
59
|
+
changes.replace.push([key, leftValue, rightValue]);
|
|
60
|
+
}
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
changes.add.push([key, leftValue]);
|
|
64
|
+
});
|
|
65
|
+
Object.keys(oldProps).forEach(key => {
|
|
66
|
+
if (!Reflect.has(newProps, key)) {
|
|
67
|
+
changes.remove.push([key, oldProps[key]]);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
return changes;
|
|
71
|
+
}
|
|
72
|
+
function classToString(config) {
|
|
73
|
+
if (!config) {
|
|
74
|
+
return '';
|
|
75
|
+
}
|
|
76
|
+
if (typeof config === 'string') {
|
|
77
|
+
return config;
|
|
78
|
+
}
|
|
79
|
+
else if (Array.isArray(config)) {
|
|
80
|
+
return config.map(i => {
|
|
81
|
+
return classToString(i);
|
|
82
|
+
}).join(' ');
|
|
83
|
+
}
|
|
84
|
+
else if (typeof config === 'object') {
|
|
85
|
+
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
86
|
+
return config.toString();
|
|
87
|
+
}
|
|
88
|
+
const classes = [];
|
|
89
|
+
for (const key in config) {
|
|
90
|
+
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
91
|
+
classes.push(key);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
return classes.join(' ');
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
function styleToObject(style) {
|
|
98
|
+
if (typeof style !== 'string') {
|
|
99
|
+
return style;
|
|
100
|
+
}
|
|
101
|
+
const obj = {};
|
|
102
|
+
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
103
|
+
if (!v[0] || !v[1]) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
obj[v[0].trim()] = v[1].trim();
|
|
107
|
+
});
|
|
108
|
+
return obj;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
class Memo {
|
|
112
|
+
constructor(shouldUpdate, render) {
|
|
113
|
+
this.shouldUpdate = shouldUpdate;
|
|
114
|
+
this.render = render;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
function withMemo(shouldUpdate, render) {
|
|
118
|
+
return new Memo(shouldUpdate, render);
|
|
119
|
+
}
|
|
120
|
+
|
|
47
121
|
const componentSetupStack = [];
|
|
48
122
|
const signalDepsStack = [];
|
|
49
123
|
const componentErrorFn = makeError('component');
|
|
@@ -123,17 +197,29 @@ class Component extends di.ReflectiveInjector {
|
|
|
123
197
|
isSetup = false;
|
|
124
198
|
componentSetupStack.pop();
|
|
125
199
|
signalDepsStack.push([]);
|
|
126
|
-
const
|
|
200
|
+
const templateRender = render instanceof Memo ? render.render : render;
|
|
201
|
+
let template = templateRender();
|
|
127
202
|
const deps = signalDepsStack.pop();
|
|
128
203
|
this.unWatch = useEffect(deps, () => {
|
|
129
204
|
this.markAsDirtied();
|
|
130
205
|
});
|
|
131
206
|
return {
|
|
132
207
|
template,
|
|
133
|
-
render: () => {
|
|
208
|
+
render: (newProps, oldProps) => {
|
|
209
|
+
if (newProps !== oldProps) {
|
|
210
|
+
const { add, remove, replace } = getObjectChanges(newProps, oldProps);
|
|
211
|
+
if (add.length || remove.length || replace.length) {
|
|
212
|
+
this.invokePropsChangedHooks(newProps);
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
if (render instanceof Memo) {
|
|
216
|
+
if (!render.shouldUpdate(newProps, oldProps)) {
|
|
217
|
+
return template;
|
|
218
|
+
}
|
|
219
|
+
}
|
|
134
220
|
this.unWatch();
|
|
135
221
|
signalDepsStack.push([]);
|
|
136
|
-
|
|
222
|
+
template = templateRender();
|
|
137
223
|
const deps = signalDepsStack.pop();
|
|
138
224
|
this.unWatch = useEffect(deps, () => {
|
|
139
225
|
this.markAsDirtied();
|
|
@@ -165,20 +251,6 @@ class Component extends di.ReflectiveInjector {
|
|
|
165
251
|
this.invokeUpdatedHooks();
|
|
166
252
|
}
|
|
167
253
|
}
|
|
168
|
-
invokePropsChangedHooks(newProps) {
|
|
169
|
-
const oldProps = this.props;
|
|
170
|
-
this.props = newProps;
|
|
171
|
-
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
172
|
-
fn();
|
|
173
|
-
});
|
|
174
|
-
this.propsChangedDestroyCallbacks = [];
|
|
175
|
-
for (const fn of this.propsChangedCallbacks) {
|
|
176
|
-
const destroyFn = fn(newProps, oldProps);
|
|
177
|
-
if (typeof destroyFn === 'function') {
|
|
178
|
-
this.propsChangedDestroyCallbacks.push(destroyFn);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
254
|
destroy() {
|
|
183
255
|
this.unWatch();
|
|
184
256
|
this.updatedDestroyCallbacks.forEach(fn => {
|
|
@@ -196,6 +268,20 @@ class Component extends di.ReflectiveInjector {
|
|
|
196
268
|
this.mountCallbacks = [];
|
|
197
269
|
this.updatedCallbacks = [];
|
|
198
270
|
}
|
|
271
|
+
invokePropsChangedHooks(newProps) {
|
|
272
|
+
const oldProps = this.props;
|
|
273
|
+
this.props = newProps;
|
|
274
|
+
this.propsChangedDestroyCallbacks.forEach(fn => {
|
|
275
|
+
fn();
|
|
276
|
+
});
|
|
277
|
+
this.propsChangedDestroyCallbacks = [];
|
|
278
|
+
for (const fn of this.propsChangedCallbacks) {
|
|
279
|
+
const destroyFn = fn(newProps, oldProps);
|
|
280
|
+
if (typeof destroyFn === 'function') {
|
|
281
|
+
this.propsChangedDestroyCallbacks.push(destroyFn);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
199
285
|
invokeMountHooks() {
|
|
200
286
|
for (const fn of this.mountCallbacks) {
|
|
201
287
|
const destroyFn = fn();
|
|
@@ -384,13 +470,25 @@ function useSignal(state) {
|
|
|
384
470
|
return;
|
|
385
471
|
}
|
|
386
472
|
state = newState;
|
|
387
|
-
|
|
473
|
+
const depCallbacks = Array.from(signal[depsKey]);
|
|
474
|
+
for (const fn of depCallbacks) {
|
|
475
|
+
// 回调中可能会对依赖做出修改,故需先缓存起来
|
|
388
476
|
fn();
|
|
389
477
|
}
|
|
390
478
|
};
|
|
391
479
|
signal[depsKey] = new Set();
|
|
392
480
|
return signal;
|
|
393
481
|
}
|
|
482
|
+
function invokeDepFn(fn) {
|
|
483
|
+
const deps = [];
|
|
484
|
+
signalDepsStack.push(deps);
|
|
485
|
+
const data = fn();
|
|
486
|
+
signalDepsStack.pop();
|
|
487
|
+
return {
|
|
488
|
+
deps,
|
|
489
|
+
data
|
|
490
|
+
};
|
|
491
|
+
}
|
|
394
492
|
/**
|
|
395
493
|
* 使用派生值,Viewfly 会收集回调函数内同步执行时访问的 Signal,
|
|
396
494
|
* 并在你获取 useDerived 函数返回的 Signal 的值时,自动计算最新的值。
|
|
@@ -399,18 +497,32 @@ function useSignal(state) {
|
|
|
399
497
|
* @param isContinue 可选的停止函数,在每次值更新后调用,当返回值为 false 时,将不再监听依赖的变化
|
|
400
498
|
*/
|
|
401
499
|
function useDerived(callback, isContinue) {
|
|
402
|
-
|
|
403
|
-
signalDepsStack.push(deps);
|
|
404
|
-
const data = callback();
|
|
405
|
-
signalDepsStack.pop();
|
|
500
|
+
let { data, deps } = invokeDepFn(callback);
|
|
406
501
|
const signal = useSignal(data);
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
502
|
+
const component = getSetupContext(false);
|
|
503
|
+
const unListenRef = {};
|
|
504
|
+
function listen(model, deps, callback, unListenRef, isContinue) {
|
|
505
|
+
const nextListen = () => {
|
|
506
|
+
unListenRef.unListen();
|
|
507
|
+
const { data: nextData, deps: nextDeps } = invokeDepFn(callback);
|
|
508
|
+
model.set(nextData);
|
|
509
|
+
if (typeof isContinue !== 'function' || isContinue(nextData) !== false) {
|
|
510
|
+
listen(model, nextDeps, callback, unListenRef, isContinue);
|
|
511
|
+
}
|
|
512
|
+
};
|
|
513
|
+
unListenRef.unListen = () => {
|
|
514
|
+
for (const s of deps) {
|
|
515
|
+
s[depsKey].delete(nextListen);
|
|
413
516
|
}
|
|
517
|
+
};
|
|
518
|
+
for (const s of deps) {
|
|
519
|
+
s[depsKey].add(nextListen);
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
listen(signal, deps, callback, unListenRef, isContinue);
|
|
523
|
+
if (component) {
|
|
524
|
+
component.destroyCallbacks.push(() => {
|
|
525
|
+
unListenRef.unListen();
|
|
414
526
|
});
|
|
415
527
|
}
|
|
416
528
|
return signal;
|
|
@@ -529,70 +641,6 @@ class RootComponent extends Component {
|
|
|
529
641
|
}
|
|
530
642
|
}
|
|
531
643
|
|
|
532
|
-
const refKey = 'ref';
|
|
533
|
-
function getObjectChanges(newProps, oldProps) {
|
|
534
|
-
const changes = {
|
|
535
|
-
remove: [],
|
|
536
|
-
add: [],
|
|
537
|
-
replace: []
|
|
538
|
-
};
|
|
539
|
-
Object.keys(newProps).forEach(key => {
|
|
540
|
-
const leftValue = newProps[key];
|
|
541
|
-
const rightValue = oldProps[key];
|
|
542
|
-
if (Reflect.has(oldProps, key)) {
|
|
543
|
-
if (leftValue !== rightValue) {
|
|
544
|
-
changes.replace.push([key, leftValue, rightValue]);
|
|
545
|
-
}
|
|
546
|
-
return;
|
|
547
|
-
}
|
|
548
|
-
changes.add.push([key, leftValue]);
|
|
549
|
-
});
|
|
550
|
-
Object.keys(oldProps).forEach(key => {
|
|
551
|
-
if (!Reflect.has(newProps, key)) {
|
|
552
|
-
changes.remove.push([key, oldProps[key]]);
|
|
553
|
-
}
|
|
554
|
-
});
|
|
555
|
-
return changes;
|
|
556
|
-
}
|
|
557
|
-
function classToString(config) {
|
|
558
|
-
if (!config) {
|
|
559
|
-
return '';
|
|
560
|
-
}
|
|
561
|
-
if (typeof config === 'string') {
|
|
562
|
-
return config;
|
|
563
|
-
}
|
|
564
|
-
else if (Array.isArray(config)) {
|
|
565
|
-
return config.map(i => {
|
|
566
|
-
return classToString(i);
|
|
567
|
-
}).join(' ');
|
|
568
|
-
}
|
|
569
|
-
else if (typeof config === 'object') {
|
|
570
|
-
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
571
|
-
return config.toString();
|
|
572
|
-
}
|
|
573
|
-
const classes = [];
|
|
574
|
-
for (const key in config) {
|
|
575
|
-
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
576
|
-
classes.push(key);
|
|
577
|
-
}
|
|
578
|
-
}
|
|
579
|
-
return classes.join(' ');
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
function styleToObject(style) {
|
|
583
|
-
if (typeof style !== 'string') {
|
|
584
|
-
return style;
|
|
585
|
-
}
|
|
586
|
-
const obj = {};
|
|
587
|
-
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
588
|
-
if (!v[0] || !v[1]) {
|
|
589
|
-
return;
|
|
590
|
-
}
|
|
591
|
-
obj[v[0].trim()] = v[1].trim();
|
|
592
|
-
});
|
|
593
|
-
return obj;
|
|
594
|
-
}
|
|
595
|
-
|
|
596
644
|
class RootComponentRef {
|
|
597
645
|
}
|
|
598
646
|
class Atom {
|
|
@@ -688,7 +736,7 @@ exports.Renderer = class Renderer {
|
|
|
688
736
|
applyChanges(component, context) {
|
|
689
737
|
const { atom, render } = this.componentAtomCaches.get(component);
|
|
690
738
|
const diffAtom = atom.child;
|
|
691
|
-
const template = render();
|
|
739
|
+
const template = render(component.props, component.props);
|
|
692
740
|
if (template) {
|
|
693
741
|
this.linkTemplate(template, component, atom);
|
|
694
742
|
}
|
|
@@ -709,26 +757,28 @@ exports.Renderer = class Renderer {
|
|
|
709
757
|
}
|
|
710
758
|
const commits = [];
|
|
711
759
|
const changeCommits = {
|
|
712
|
-
|
|
713
|
-
commits.push(() => {
|
|
714
|
-
const
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
}
|
|
718
|
-
const
|
|
719
|
-
|
|
720
|
-
start.jsxNode.props = newProps;
|
|
721
|
-
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
722
|
-
const template = render();
|
|
723
|
-
if (template) {
|
|
724
|
-
this.linkTemplate(template, start.jsxNode, start);
|
|
725
|
-
}
|
|
726
|
-
this.componentAtomCaches.set(start.jsxNode, {
|
|
760
|
+
updateComponent: (newAtom, reusedAtom, expectIndex, diffIndex) => {
|
|
761
|
+
commits.push((offset) => {
|
|
762
|
+
const newProps = newAtom.jsxNode.props;
|
|
763
|
+
const oldProps = reusedAtom.jsxNode.props;
|
|
764
|
+
newAtom.jsxNode = reusedAtom.jsxNode;
|
|
765
|
+
const { render, template } = this.componentAtomCaches.get(newAtom.jsxNode);
|
|
766
|
+
const newTemplate = render(newProps, oldProps);
|
|
767
|
+
this.componentAtomCaches.set(newAtom.jsxNode, {
|
|
727
768
|
render,
|
|
728
|
-
|
|
769
|
+
template,
|
|
770
|
+
atom: newAtom
|
|
729
771
|
});
|
|
730
|
-
if (
|
|
731
|
-
this.
|
|
772
|
+
if (newTemplate === template) {
|
|
773
|
+
this.reuseComponentView(newAtom, reusedAtom, context, expectIndex !== diffIndex - offset);
|
|
774
|
+
// (newAtom.jsxNode as Component).rendered()
|
|
775
|
+
return;
|
|
776
|
+
}
|
|
777
|
+
if (newTemplate) {
|
|
778
|
+
this.linkTemplate(newTemplate, newAtom.jsxNode, newAtom);
|
|
779
|
+
}
|
|
780
|
+
if (newAtom.child) {
|
|
781
|
+
this.diff(newAtom.child, reusedAtom.child, context, expectIndex, diffIndex);
|
|
732
782
|
}
|
|
733
783
|
else if (reusedAtom.child) {
|
|
734
784
|
let atom = reusedAtom.child;
|
|
@@ -737,10 +787,10 @@ exports.Renderer = class Renderer {
|
|
|
737
787
|
atom = atom.sibling;
|
|
738
788
|
}
|
|
739
789
|
}
|
|
740
|
-
|
|
790
|
+
newAtom.jsxNode.rendered();
|
|
741
791
|
});
|
|
742
792
|
},
|
|
743
|
-
|
|
793
|
+
updateElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
744
794
|
commits.push((offset) => {
|
|
745
795
|
newAtom.nativeNode = oldAtom.nativeNode;
|
|
746
796
|
const host = context.host;
|
|
@@ -771,7 +821,7 @@ exports.Renderer = class Renderer {
|
|
|
771
821
|
applyRefs();
|
|
772
822
|
});
|
|
773
823
|
},
|
|
774
|
-
|
|
824
|
+
updateText: (newAtom, oldAtom) => {
|
|
775
825
|
commits.push(() => {
|
|
776
826
|
const nativeNode = oldAtom.nativeNode;
|
|
777
827
|
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
@@ -813,6 +863,40 @@ exports.Renderer = class Renderer {
|
|
|
813
863
|
commit(offset);
|
|
814
864
|
}
|
|
815
865
|
}
|
|
866
|
+
reuseComponentView(newAtom, reusedAtom, context, moveView) {
|
|
867
|
+
let child = reusedAtom.child;
|
|
868
|
+
newAtom.child = child;
|
|
869
|
+
const children = [];
|
|
870
|
+
while (child) {
|
|
871
|
+
children.push(child);
|
|
872
|
+
child.parent = newAtom;
|
|
873
|
+
child = child.sibling;
|
|
874
|
+
}
|
|
875
|
+
const updateContext = (atom) => {
|
|
876
|
+
if (atom.jsxNode instanceof Component) {
|
|
877
|
+
let child = atom.child;
|
|
878
|
+
while (child) {
|
|
879
|
+
updateContext(child);
|
|
880
|
+
child = child.sibling;
|
|
881
|
+
}
|
|
882
|
+
}
|
|
883
|
+
else {
|
|
884
|
+
if (moveView) {
|
|
885
|
+
if (context.isParent) {
|
|
886
|
+
this.nativeRenderer.prependChild(context.host, atom.nativeNode);
|
|
887
|
+
}
|
|
888
|
+
else {
|
|
889
|
+
this.nativeRenderer.insertAfter(atom.nativeNode, context.host);
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
context.isParent = false;
|
|
893
|
+
context.host = atom.nativeNode;
|
|
894
|
+
}
|
|
895
|
+
};
|
|
896
|
+
for (const atom of children) {
|
|
897
|
+
updateContext(atom);
|
|
898
|
+
}
|
|
899
|
+
}
|
|
816
900
|
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
817
901
|
for (let i = 0; i < oldChildren.length; i++) {
|
|
818
902
|
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
@@ -825,13 +909,13 @@ exports.Renderer = class Renderer {
|
|
|
825
909
|
}
|
|
826
910
|
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
827
911
|
if (newAtom.jsxNode instanceof JSXElement) {
|
|
828
|
-
changeCommits.
|
|
912
|
+
changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
829
913
|
}
|
|
830
914
|
else if (newAtom.jsxNode instanceof JSXText) {
|
|
831
|
-
changeCommits.
|
|
915
|
+
changeCommits.updateText(newAtom, diffAtom);
|
|
832
916
|
}
|
|
833
917
|
else {
|
|
834
|
-
changeCommits.
|
|
918
|
+
changeCommits.updateComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
835
919
|
}
|
|
836
920
|
oldChildren.splice(i, 1);
|
|
837
921
|
return;
|
|
@@ -912,27 +996,20 @@ exports.Renderer = class Renderer {
|
|
|
912
996
|
}
|
|
913
997
|
this.componentAtomCaches.set(component, {
|
|
914
998
|
render,
|
|
999
|
+
template,
|
|
915
1000
|
atom: from
|
|
916
1001
|
});
|
|
917
1002
|
return from;
|
|
918
1003
|
}
|
|
919
1004
|
createChainByComponentFactory(context, factory, parent) {
|
|
920
1005
|
const component = factory.createInstance(context);
|
|
921
|
-
// if (component.setup === Fragment) {
|
|
922
|
-
// const children = component.props.children
|
|
923
|
-
// return this.createChainByChildren(
|
|
924
|
-
// component,
|
|
925
|
-
// Array.isArray(children) ? children : [children],
|
|
926
|
-
// parent
|
|
927
|
-
// )
|
|
928
|
-
// }
|
|
929
1006
|
return new Atom(component, parent);
|
|
930
1007
|
}
|
|
931
1008
|
createChainByJSXElement(context, element, parent) {
|
|
932
1009
|
const atom = new Atom(element, parent);
|
|
933
1010
|
if (Reflect.has(element.props, 'children')) {
|
|
934
1011
|
const jsxChildren = element.props.children;
|
|
935
|
-
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
1012
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom, []);
|
|
936
1013
|
this.link(atom, children);
|
|
937
1014
|
}
|
|
938
1015
|
return atom;
|
|
@@ -940,8 +1017,7 @@ exports.Renderer = class Renderer {
|
|
|
940
1017
|
createChainByJSXText(node, parent) {
|
|
941
1018
|
return new Atom(node, parent);
|
|
942
1019
|
}
|
|
943
|
-
createChainByChildren(context, children, parent) {
|
|
944
|
-
const atoms = [];
|
|
1020
|
+
createChainByChildren(context, children, parent, atoms) {
|
|
945
1021
|
for (const item of children) {
|
|
946
1022
|
if (item instanceof JSXElement) {
|
|
947
1023
|
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
@@ -957,7 +1033,7 @@ exports.Renderer = class Renderer {
|
|
|
957
1033
|
continue;
|
|
958
1034
|
}
|
|
959
1035
|
if (Array.isArray(item)) {
|
|
960
|
-
|
|
1036
|
+
this.createChainByChildren(context, item, parent, atoms);
|
|
961
1037
|
continue;
|
|
962
1038
|
}
|
|
963
1039
|
if (item !== null && typeof item !== 'undefined') {
|
|
@@ -968,7 +1044,7 @@ exports.Renderer = class Renderer {
|
|
|
968
1044
|
}
|
|
969
1045
|
linkTemplate(template, component, parent) {
|
|
970
1046
|
const children = Array.isArray(template) ? template : [template];
|
|
971
|
-
this.link(parent, this.createChainByChildren(component, children, parent));
|
|
1047
|
+
this.link(parent, this.createChainByChildren(component, children, parent, []));
|
|
972
1048
|
}
|
|
973
1049
|
link(parent, children) {
|
|
974
1050
|
for (let i = 1; i < children.length; i++) {
|
|
@@ -987,7 +1063,10 @@ exports.Renderer = class Renderer {
|
|
|
987
1063
|
continue;
|
|
988
1064
|
}
|
|
989
1065
|
if (key === 'class') {
|
|
990
|
-
|
|
1066
|
+
const className = classToString(props[key]);
|
|
1067
|
+
if (className) {
|
|
1068
|
+
this.nativeRenderer.setClass(nativeNode, className);
|
|
1069
|
+
}
|
|
991
1070
|
continue;
|
|
992
1071
|
}
|
|
993
1072
|
if (key === 'style') {
|
|
@@ -1000,7 +1079,7 @@ exports.Renderer = class Renderer {
|
|
|
1000
1079
|
if (/^on[A-Z]/.test(key)) {
|
|
1001
1080
|
const listener = props[key];
|
|
1002
1081
|
if (typeof listener === 'function') {
|
|
1003
|
-
this.
|
|
1082
|
+
this.bindEvent(vNode, key, nativeNode, listener);
|
|
1004
1083
|
}
|
|
1005
1084
|
continue;
|
|
1006
1085
|
}
|
|
@@ -1040,7 +1119,10 @@ exports.Renderer = class Renderer {
|
|
|
1040
1119
|
}
|
|
1041
1120
|
if (/^on[A-Z]/.test(key)) {
|
|
1042
1121
|
if (typeof value === 'function') {
|
|
1043
|
-
|
|
1122
|
+
const type = key.replace(/^on/, '').toLowerCase();
|
|
1123
|
+
const oldOn = oldVNode.on;
|
|
1124
|
+
this.nativeRenderer.unListen(nativeNode, type, oldOn[type].delegate);
|
|
1125
|
+
Reflect.deleteProperty(oldOn, type);
|
|
1044
1126
|
}
|
|
1045
1127
|
continue;
|
|
1046
1128
|
}
|
|
@@ -1074,12 +1156,8 @@ exports.Renderer = class Renderer {
|
|
|
1074
1156
|
}
|
|
1075
1157
|
if (/^on[A-Z]/.test(key)) {
|
|
1076
1158
|
const listenType = key.replace(/^on/, '').toLowerCase();
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
}
|
|
1080
|
-
if (typeof newValue === 'function') {
|
|
1081
|
-
this.nativeRenderer.listen(nativeNode, listenType, newValue);
|
|
1082
|
-
}
|
|
1159
|
+
newVNode.on = oldVNode.on;
|
|
1160
|
+
newVNode.on[listenType].listenFn = newValue;
|
|
1083
1161
|
continue;
|
|
1084
1162
|
}
|
|
1085
1163
|
if (key === refKey) {
|
|
@@ -1106,7 +1184,7 @@ exports.Renderer = class Renderer {
|
|
|
1106
1184
|
}
|
|
1107
1185
|
if (/^on[A-Z]/.test(key)) {
|
|
1108
1186
|
if (typeof value === 'function') {
|
|
1109
|
-
this.
|
|
1187
|
+
this.bindEvent(newVNode, key, nativeNode, value);
|
|
1110
1188
|
}
|
|
1111
1189
|
continue;
|
|
1112
1190
|
}
|
|
@@ -1129,6 +1207,22 @@ exports.Renderer = class Renderer {
|
|
|
1129
1207
|
}
|
|
1130
1208
|
}
|
|
1131
1209
|
}
|
|
1210
|
+
bindEvent(vNode, key, nativeNode, listenFn) {
|
|
1211
|
+
let on = vNode.on;
|
|
1212
|
+
if (!on) {
|
|
1213
|
+
vNode.on = on = {};
|
|
1214
|
+
}
|
|
1215
|
+
const type = key.replace(/^on/, '').toLowerCase();
|
|
1216
|
+
const delegate = function (...args) {
|
|
1217
|
+
return delegateObj.listenFn.apply(this, args);
|
|
1218
|
+
};
|
|
1219
|
+
const delegateObj = {
|
|
1220
|
+
delegate,
|
|
1221
|
+
listenFn
|
|
1222
|
+
};
|
|
1223
|
+
on[type] = delegateObj;
|
|
1224
|
+
this.nativeRenderer.listen(nativeNode, type, delegate);
|
|
1225
|
+
}
|
|
1132
1226
|
};
|
|
1133
1227
|
exports.Renderer = __decorate([
|
|
1134
1228
|
di.Injectable(),
|
|
@@ -1205,6 +1299,7 @@ exports.Fragment = Fragment;
|
|
|
1205
1299
|
exports.JSXComponent = JSXComponent;
|
|
1206
1300
|
exports.JSXElement = JSXElement;
|
|
1207
1301
|
exports.JSXText = JSXText;
|
|
1302
|
+
exports.Memo = Memo;
|
|
1208
1303
|
exports.NativeRenderer = NativeRenderer;
|
|
1209
1304
|
exports.Ref = Ref;
|
|
1210
1305
|
exports.RootComponent = RootComponent;
|
|
@@ -1223,6 +1318,7 @@ exports.useDerived = useDerived;
|
|
|
1223
1318
|
exports.useEffect = useEffect;
|
|
1224
1319
|
exports.useRef = useRef;
|
|
1225
1320
|
exports.useSignal = useSignal;
|
|
1321
|
+
exports.withMemo = withMemo;
|
|
1226
1322
|
Object.keys(di).forEach(function (k) {
|
|
1227
1323
|
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
1228
1324
|
enumerable: true,
|
package/bundles/model/_api.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Provider, ReflectiveInjector, AbstractType, Type, InjectionToken, InjectFlags, Injector } from '@tanbo/di';
|
|
2
2
|
import { Props, Key, JSXTypeof, JSXChildNode } from './jsx-element';
|
|
3
|
+
import { Memo } from './memo';
|
|
3
4
|
export declare class JSXComponent {
|
|
4
5
|
props: Props;
|
|
5
6
|
private factory;
|
|
@@ -7,7 +8,7 @@ export declare class JSXComponent {
|
|
|
7
8
|
createInstance(injector: Component): Component;
|
|
8
9
|
}
|
|
9
10
|
export interface ComponentSetup<T extends Props<any> = Props<any>> {
|
|
10
|
-
(props?: T): () => JSXChildNode
|
|
11
|
+
(props?: T): (() => JSXChildNode) | Memo<T>;
|
|
11
12
|
}
|
|
12
13
|
/**
|
|
13
14
|
* Viewfly 组件管理类,用于管理组件的生命周期,上下文等
|
|
@@ -35,13 +36,13 @@ export declare class Component extends ReflectiveInjector implements JSXTypeof {
|
|
|
35
36
|
addProvide<T>(providers: Provider<T> | Provider<T>[]): void;
|
|
36
37
|
init(): {
|
|
37
38
|
template: JSXChildNode;
|
|
38
|
-
render: () => JSXChildNode;
|
|
39
|
+
render: (newProps: Props, oldProps: Props) => JSXChildNode;
|
|
39
40
|
};
|
|
40
41
|
markAsDirtied(): void;
|
|
41
42
|
markAsChanged(): void;
|
|
42
43
|
rendered(): void;
|
|
43
|
-
invokePropsChangedHooks(newProps: Props<any>): void;
|
|
44
44
|
destroy(): void;
|
|
45
|
+
private invokePropsChangedHooks;
|
|
45
46
|
private invokeMountHooks;
|
|
46
47
|
private invokeUpdatedHooks;
|
|
47
48
|
}
|
|
@@ -21,11 +21,16 @@ export declare class JSXText implements JSXTypeof {
|
|
|
21
21
|
constructor(text: string);
|
|
22
22
|
is(target: JSXTypeof): boolean;
|
|
23
23
|
}
|
|
24
|
+
export interface ListenDelegate {
|
|
25
|
+
delegate: () => any;
|
|
26
|
+
listenFn: ((...args: any[]) => any) | void;
|
|
27
|
+
}
|
|
24
28
|
export declare class JSXElement implements JSXTypeof {
|
|
25
29
|
type: string;
|
|
26
30
|
props: Props<any>;
|
|
27
31
|
key?: Key | undefined;
|
|
28
32
|
$$typeOf: string;
|
|
33
|
+
on?: Record<string, ListenDelegate>;
|
|
29
34
|
constructor(type: string, props: Props<any>, key?: Key | undefined);
|
|
30
35
|
is(target: JSXTypeof): boolean;
|
|
31
36
|
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { JSXChildNode, Props } from './jsx-element';
|
|
2
|
+
export interface ShouldUpdate<T extends Props> {
|
|
3
|
+
(currentProps: T, prevProps: T): unknown;
|
|
4
|
+
}
|
|
5
|
+
export declare class Memo<T extends Props> {
|
|
6
|
+
shouldUpdate: ShouldUpdate<T>;
|
|
7
|
+
render: () => JSXChildNode;
|
|
8
|
+
constructor(shouldUpdate: ShouldUpdate<T>, render: () => JSXChildNode);
|
|
9
|
+
}
|
|
10
|
+
export declare function withMemo<T extends Props = Props>(shouldUpdate: ShouldUpdate<T>, render: () => JSXChildNode): Memo<T>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@viewfly/core",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"description": "Viewfly is a simple and easy-to-use JavaScript framework with an intuitive development experience.",
|
|
5
5
|
"main": "./bundles/index.js",
|
|
6
6
|
"module": "./bundles/index.esm.js",
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"bugs": {
|
|
35
35
|
"url": "https://github.com/viewfly/viewfly.git/issues"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "f3cb35e8a4c24063ea8431de7d001d801925e9d9"
|
|
38
38
|
}
|