@viewfly/core 0.0.4 → 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 +1 -0
- package/bundles/index.esm.js +170 -112
- package/bundles/index.js +171 -111
- package/bundles/model/_api.d.ts +1 -0
- package/bundles/model/component.d.ts +4 -3
- package/bundles/model/memo.d.ts +10 -0
- package/package.json +2 -2
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();
|
|
@@ -554,70 +640,6 @@ class RootComponent extends Component {
|
|
|
554
640
|
}
|
|
555
641
|
}
|
|
556
642
|
|
|
557
|
-
const refKey = 'ref';
|
|
558
|
-
function getObjectChanges(newProps, oldProps) {
|
|
559
|
-
const changes = {
|
|
560
|
-
remove: [],
|
|
561
|
-
add: [],
|
|
562
|
-
replace: []
|
|
563
|
-
};
|
|
564
|
-
Object.keys(newProps).forEach(key => {
|
|
565
|
-
const leftValue = newProps[key];
|
|
566
|
-
const rightValue = oldProps[key];
|
|
567
|
-
if (Reflect.has(oldProps, key)) {
|
|
568
|
-
if (leftValue !== rightValue) {
|
|
569
|
-
changes.replace.push([key, leftValue, rightValue]);
|
|
570
|
-
}
|
|
571
|
-
return;
|
|
572
|
-
}
|
|
573
|
-
changes.add.push([key, leftValue]);
|
|
574
|
-
});
|
|
575
|
-
Object.keys(oldProps).forEach(key => {
|
|
576
|
-
if (!Reflect.has(newProps, key)) {
|
|
577
|
-
changes.remove.push([key, oldProps[key]]);
|
|
578
|
-
}
|
|
579
|
-
});
|
|
580
|
-
return changes;
|
|
581
|
-
}
|
|
582
|
-
function classToString(config) {
|
|
583
|
-
if (!config) {
|
|
584
|
-
return '';
|
|
585
|
-
}
|
|
586
|
-
if (typeof config === 'string') {
|
|
587
|
-
return config;
|
|
588
|
-
}
|
|
589
|
-
else if (Array.isArray(config)) {
|
|
590
|
-
return config.map(i => {
|
|
591
|
-
return classToString(i);
|
|
592
|
-
}).join(' ');
|
|
593
|
-
}
|
|
594
|
-
else if (typeof config === 'object') {
|
|
595
|
-
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
596
|
-
return config.toString();
|
|
597
|
-
}
|
|
598
|
-
const classes = [];
|
|
599
|
-
for (const key in config) {
|
|
600
|
-
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
601
|
-
classes.push(key);
|
|
602
|
-
}
|
|
603
|
-
}
|
|
604
|
-
return classes.join(' ');
|
|
605
|
-
}
|
|
606
|
-
}
|
|
607
|
-
function styleToObject(style) {
|
|
608
|
-
if (typeof style !== 'string') {
|
|
609
|
-
return style;
|
|
610
|
-
}
|
|
611
|
-
const obj = {};
|
|
612
|
-
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
613
|
-
if (!v[0] || !v[1]) {
|
|
614
|
-
return;
|
|
615
|
-
}
|
|
616
|
-
obj[v[0].trim()] = v[1].trim();
|
|
617
|
-
});
|
|
618
|
-
return obj;
|
|
619
|
-
}
|
|
620
|
-
|
|
621
643
|
class RootComponentRef {
|
|
622
644
|
}
|
|
623
645
|
class Atom {
|
|
@@ -713,7 +735,7 @@ let Renderer = class Renderer {
|
|
|
713
735
|
applyChanges(component, context) {
|
|
714
736
|
const { atom, render } = this.componentAtomCaches.get(component);
|
|
715
737
|
const diffAtom = atom.child;
|
|
716
|
-
const template = render();
|
|
738
|
+
const template = render(component.props, component.props);
|
|
717
739
|
if (template) {
|
|
718
740
|
this.linkTemplate(template, component, atom);
|
|
719
741
|
}
|
|
@@ -734,26 +756,28 @@ let Renderer = class Renderer {
|
|
|
734
756
|
}
|
|
735
757
|
const commits = [];
|
|
736
758
|
const changeCommits = {
|
|
737
|
-
|
|
738
|
-
commits.push(() => {
|
|
739
|
-
const
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
}
|
|
743
|
-
const
|
|
744
|
-
|
|
745
|
-
start.jsxNode.props = newProps;
|
|
746
|
-
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
747
|
-
const template = render();
|
|
748
|
-
if (template) {
|
|
749
|
-
this.linkTemplate(template, start.jsxNode, start);
|
|
750
|
-
}
|
|
751
|
-
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, {
|
|
752
767
|
render,
|
|
753
|
-
|
|
768
|
+
template,
|
|
769
|
+
atom: newAtom
|
|
754
770
|
});
|
|
755
|
-
if (
|
|
756
|
-
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);
|
|
757
781
|
}
|
|
758
782
|
else if (reusedAtom.child) {
|
|
759
783
|
let atom = reusedAtom.child;
|
|
@@ -762,10 +786,10 @@ let Renderer = class Renderer {
|
|
|
762
786
|
atom = atom.sibling;
|
|
763
787
|
}
|
|
764
788
|
}
|
|
765
|
-
|
|
789
|
+
newAtom.jsxNode.rendered();
|
|
766
790
|
});
|
|
767
791
|
},
|
|
768
|
-
|
|
792
|
+
updateElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
769
793
|
commits.push((offset) => {
|
|
770
794
|
newAtom.nativeNode = oldAtom.nativeNode;
|
|
771
795
|
const host = context.host;
|
|
@@ -796,7 +820,7 @@ let Renderer = class Renderer {
|
|
|
796
820
|
applyRefs();
|
|
797
821
|
});
|
|
798
822
|
},
|
|
799
|
-
|
|
823
|
+
updateText: (newAtom, oldAtom) => {
|
|
800
824
|
commits.push(() => {
|
|
801
825
|
const nativeNode = oldAtom.nativeNode;
|
|
802
826
|
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
@@ -838,6 +862,40 @@ let Renderer = class Renderer {
|
|
|
838
862
|
commit(offset);
|
|
839
863
|
}
|
|
840
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
|
+
}
|
|
841
899
|
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
842
900
|
for (let i = 0; i < oldChildren.length; i++) {
|
|
843
901
|
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
@@ -850,13 +908,13 @@ let Renderer = class Renderer {
|
|
|
850
908
|
}
|
|
851
909
|
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
852
910
|
if (newAtom.jsxNode instanceof JSXElement) {
|
|
853
|
-
changeCommits.
|
|
911
|
+
changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
854
912
|
}
|
|
855
913
|
else if (newAtom.jsxNode instanceof JSXText) {
|
|
856
|
-
changeCommits.
|
|
914
|
+
changeCommits.updateText(newAtom, diffAtom);
|
|
857
915
|
}
|
|
858
916
|
else {
|
|
859
|
-
changeCommits.
|
|
917
|
+
changeCommits.updateComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
860
918
|
}
|
|
861
919
|
oldChildren.splice(i, 1);
|
|
862
920
|
return;
|
|
@@ -937,6 +995,7 @@ let Renderer = class Renderer {
|
|
|
937
995
|
}
|
|
938
996
|
this.componentAtomCaches.set(component, {
|
|
939
997
|
render,
|
|
998
|
+
template,
|
|
940
999
|
atom: from
|
|
941
1000
|
});
|
|
942
1001
|
return from;
|
|
@@ -949,7 +1008,7 @@ let Renderer = class Renderer {
|
|
|
949
1008
|
const atom = new Atom(element, parent);
|
|
950
1009
|
if (Reflect.has(element.props, 'children')) {
|
|
951
1010
|
const jsxChildren = element.props.children;
|
|
952
|
-
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
1011
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom, []);
|
|
953
1012
|
this.link(atom, children);
|
|
954
1013
|
}
|
|
955
1014
|
return atom;
|
|
@@ -957,8 +1016,7 @@ let Renderer = class Renderer {
|
|
|
957
1016
|
createChainByJSXText(node, parent) {
|
|
958
1017
|
return new Atom(node, parent);
|
|
959
1018
|
}
|
|
960
|
-
createChainByChildren(context, children, parent) {
|
|
961
|
-
const atoms = [];
|
|
1019
|
+
createChainByChildren(context, children, parent, atoms) {
|
|
962
1020
|
for (const item of children) {
|
|
963
1021
|
if (item instanceof JSXElement) {
|
|
964
1022
|
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
@@ -974,7 +1032,7 @@ let Renderer = class Renderer {
|
|
|
974
1032
|
continue;
|
|
975
1033
|
}
|
|
976
1034
|
if (Array.isArray(item)) {
|
|
977
|
-
|
|
1035
|
+
this.createChainByChildren(context, item, parent, atoms);
|
|
978
1036
|
continue;
|
|
979
1037
|
}
|
|
980
1038
|
if (item !== null && typeof item !== 'undefined') {
|
|
@@ -985,7 +1043,7 @@ let Renderer = class Renderer {
|
|
|
985
1043
|
}
|
|
986
1044
|
linkTemplate(template, component, parent) {
|
|
987
1045
|
const children = Array.isArray(template) ? template : [template];
|
|
988
|
-
this.link(parent, this.createChainByChildren(component, children, parent));
|
|
1046
|
+
this.link(parent, this.createChainByChildren(component, children, parent, []));
|
|
989
1047
|
}
|
|
990
1048
|
link(parent, children) {
|
|
991
1049
|
for (let i = 1; i < children.length; i++) {
|
|
@@ -1235,4 +1293,4 @@ class Viewfly extends ReflectiveInjector {
|
|
|
1235
1293
|
}
|
|
1236
1294
|
}
|
|
1237
1295
|
|
|
1238
|
-
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();
|
|
@@ -555,70 +641,6 @@ class RootComponent extends Component {
|
|
|
555
641
|
}
|
|
556
642
|
}
|
|
557
643
|
|
|
558
|
-
const refKey = 'ref';
|
|
559
|
-
function getObjectChanges(newProps, oldProps) {
|
|
560
|
-
const changes = {
|
|
561
|
-
remove: [],
|
|
562
|
-
add: [],
|
|
563
|
-
replace: []
|
|
564
|
-
};
|
|
565
|
-
Object.keys(newProps).forEach(key => {
|
|
566
|
-
const leftValue = newProps[key];
|
|
567
|
-
const rightValue = oldProps[key];
|
|
568
|
-
if (Reflect.has(oldProps, key)) {
|
|
569
|
-
if (leftValue !== rightValue) {
|
|
570
|
-
changes.replace.push([key, leftValue, rightValue]);
|
|
571
|
-
}
|
|
572
|
-
return;
|
|
573
|
-
}
|
|
574
|
-
changes.add.push([key, leftValue]);
|
|
575
|
-
});
|
|
576
|
-
Object.keys(oldProps).forEach(key => {
|
|
577
|
-
if (!Reflect.has(newProps, key)) {
|
|
578
|
-
changes.remove.push([key, oldProps[key]]);
|
|
579
|
-
}
|
|
580
|
-
});
|
|
581
|
-
return changes;
|
|
582
|
-
}
|
|
583
|
-
function classToString(config) {
|
|
584
|
-
if (!config) {
|
|
585
|
-
return '';
|
|
586
|
-
}
|
|
587
|
-
if (typeof config === 'string') {
|
|
588
|
-
return config;
|
|
589
|
-
}
|
|
590
|
-
else if (Array.isArray(config)) {
|
|
591
|
-
return config.map(i => {
|
|
592
|
-
return classToString(i);
|
|
593
|
-
}).join(' ');
|
|
594
|
-
}
|
|
595
|
-
else if (typeof config === 'object') {
|
|
596
|
-
if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
|
|
597
|
-
return config.toString();
|
|
598
|
-
}
|
|
599
|
-
const classes = [];
|
|
600
|
-
for (const key in config) {
|
|
601
|
-
if ({}.hasOwnProperty.call(config, key) && config[key]) {
|
|
602
|
-
classes.push(key);
|
|
603
|
-
}
|
|
604
|
-
}
|
|
605
|
-
return classes.join(' ');
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
function styleToObject(style) {
|
|
609
|
-
if (typeof style !== 'string') {
|
|
610
|
-
return style;
|
|
611
|
-
}
|
|
612
|
-
const obj = {};
|
|
613
|
-
style.split(';').map(s => s.split(':')).forEach(v => {
|
|
614
|
-
if (!v[0] || !v[1]) {
|
|
615
|
-
return;
|
|
616
|
-
}
|
|
617
|
-
obj[v[0].trim()] = v[1].trim();
|
|
618
|
-
});
|
|
619
|
-
return obj;
|
|
620
|
-
}
|
|
621
|
-
|
|
622
644
|
class RootComponentRef {
|
|
623
645
|
}
|
|
624
646
|
class Atom {
|
|
@@ -714,7 +736,7 @@ exports.Renderer = class Renderer {
|
|
|
714
736
|
applyChanges(component, context) {
|
|
715
737
|
const { atom, render } = this.componentAtomCaches.get(component);
|
|
716
738
|
const diffAtom = atom.child;
|
|
717
|
-
const template = render();
|
|
739
|
+
const template = render(component.props, component.props);
|
|
718
740
|
if (template) {
|
|
719
741
|
this.linkTemplate(template, component, atom);
|
|
720
742
|
}
|
|
@@ -735,26 +757,28 @@ exports.Renderer = class Renderer {
|
|
|
735
757
|
}
|
|
736
758
|
const commits = [];
|
|
737
759
|
const changeCommits = {
|
|
738
|
-
|
|
739
|
-
commits.push(() => {
|
|
740
|
-
const
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
}
|
|
744
|
-
const
|
|
745
|
-
|
|
746
|
-
start.jsxNode.props = newProps;
|
|
747
|
-
const { render } = this.componentAtomCaches.get(start.jsxNode);
|
|
748
|
-
const template = render();
|
|
749
|
-
if (template) {
|
|
750
|
-
this.linkTemplate(template, start.jsxNode, start);
|
|
751
|
-
}
|
|
752
|
-
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, {
|
|
753
768
|
render,
|
|
754
|
-
|
|
769
|
+
template,
|
|
770
|
+
atom: newAtom
|
|
755
771
|
});
|
|
756
|
-
if (
|
|
757
|
-
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);
|
|
758
782
|
}
|
|
759
783
|
else if (reusedAtom.child) {
|
|
760
784
|
let atom = reusedAtom.child;
|
|
@@ -763,10 +787,10 @@ exports.Renderer = class Renderer {
|
|
|
763
787
|
atom = atom.sibling;
|
|
764
788
|
}
|
|
765
789
|
}
|
|
766
|
-
|
|
790
|
+
newAtom.jsxNode.rendered();
|
|
767
791
|
});
|
|
768
792
|
},
|
|
769
|
-
|
|
793
|
+
updateElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
|
|
770
794
|
commits.push((offset) => {
|
|
771
795
|
newAtom.nativeNode = oldAtom.nativeNode;
|
|
772
796
|
const host = context.host;
|
|
@@ -797,7 +821,7 @@ exports.Renderer = class Renderer {
|
|
|
797
821
|
applyRefs();
|
|
798
822
|
});
|
|
799
823
|
},
|
|
800
|
-
|
|
824
|
+
updateText: (newAtom, oldAtom) => {
|
|
801
825
|
commits.push(() => {
|
|
802
826
|
const nativeNode = oldAtom.nativeNode;
|
|
803
827
|
if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
|
|
@@ -839,6 +863,40 @@ exports.Renderer = class Renderer {
|
|
|
839
863
|
commit(offset);
|
|
840
864
|
}
|
|
841
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
|
+
}
|
|
842
900
|
createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
|
|
843
901
|
for (let i = 0; i < oldChildren.length; i++) {
|
|
844
902
|
const { atom: diffAtom, index: diffIndex } = oldChildren[i];
|
|
@@ -851,13 +909,13 @@ exports.Renderer = class Renderer {
|
|
|
851
909
|
}
|
|
852
910
|
if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
|
|
853
911
|
if (newAtom.jsxNode instanceof JSXElement) {
|
|
854
|
-
changeCommits.
|
|
912
|
+
changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
|
|
855
913
|
}
|
|
856
914
|
else if (newAtom.jsxNode instanceof JSXText) {
|
|
857
|
-
changeCommits.
|
|
915
|
+
changeCommits.updateText(newAtom, diffAtom);
|
|
858
916
|
}
|
|
859
917
|
else {
|
|
860
|
-
changeCommits.
|
|
918
|
+
changeCommits.updateComponent(newAtom, diffAtom, expectIndex, diffIndex);
|
|
861
919
|
}
|
|
862
920
|
oldChildren.splice(i, 1);
|
|
863
921
|
return;
|
|
@@ -938,6 +996,7 @@ exports.Renderer = class Renderer {
|
|
|
938
996
|
}
|
|
939
997
|
this.componentAtomCaches.set(component, {
|
|
940
998
|
render,
|
|
999
|
+
template,
|
|
941
1000
|
atom: from
|
|
942
1001
|
});
|
|
943
1002
|
return from;
|
|
@@ -950,7 +1009,7 @@ exports.Renderer = class Renderer {
|
|
|
950
1009
|
const atom = new Atom(element, parent);
|
|
951
1010
|
if (Reflect.has(element.props, 'children')) {
|
|
952
1011
|
const jsxChildren = element.props.children;
|
|
953
|
-
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
|
|
1012
|
+
const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom, []);
|
|
954
1013
|
this.link(atom, children);
|
|
955
1014
|
}
|
|
956
1015
|
return atom;
|
|
@@ -958,8 +1017,7 @@ exports.Renderer = class Renderer {
|
|
|
958
1017
|
createChainByJSXText(node, parent) {
|
|
959
1018
|
return new Atom(node, parent);
|
|
960
1019
|
}
|
|
961
|
-
createChainByChildren(context, children, parent) {
|
|
962
|
-
const atoms = [];
|
|
1020
|
+
createChainByChildren(context, children, parent, atoms) {
|
|
963
1021
|
for (const item of children) {
|
|
964
1022
|
if (item instanceof JSXElement) {
|
|
965
1023
|
atoms.push(this.createChainByJSXElement(context, item, parent));
|
|
@@ -975,7 +1033,7 @@ exports.Renderer = class Renderer {
|
|
|
975
1033
|
continue;
|
|
976
1034
|
}
|
|
977
1035
|
if (Array.isArray(item)) {
|
|
978
|
-
|
|
1036
|
+
this.createChainByChildren(context, item, parent, atoms);
|
|
979
1037
|
continue;
|
|
980
1038
|
}
|
|
981
1039
|
if (item !== null && typeof item !== 'undefined') {
|
|
@@ -986,7 +1044,7 @@ exports.Renderer = class Renderer {
|
|
|
986
1044
|
}
|
|
987
1045
|
linkTemplate(template, component, parent) {
|
|
988
1046
|
const children = Array.isArray(template) ? template : [template];
|
|
989
|
-
this.link(parent, this.createChainByChildren(component, children, parent));
|
|
1047
|
+
this.link(parent, this.createChainByChildren(component, children, parent, []));
|
|
990
1048
|
}
|
|
991
1049
|
link(parent, children) {
|
|
992
1050
|
for (let i = 1; i < children.length; i++) {
|
|
@@ -1241,6 +1299,7 @@ exports.Fragment = Fragment;
|
|
|
1241
1299
|
exports.JSXComponent = JSXComponent;
|
|
1242
1300
|
exports.JSXElement = JSXElement;
|
|
1243
1301
|
exports.JSXText = JSXText;
|
|
1302
|
+
exports.Memo = Memo;
|
|
1244
1303
|
exports.NativeRenderer = NativeRenderer;
|
|
1245
1304
|
exports.Ref = Ref;
|
|
1246
1305
|
exports.RootComponent = RootComponent;
|
|
@@ -1259,6 +1318,7 @@ exports.useDerived = useDerived;
|
|
|
1259
1318
|
exports.useEffect = useEffect;
|
|
1260
1319
|
exports.useRef = useRef;
|
|
1261
1320
|
exports.useSignal = useSignal;
|
|
1321
|
+
exports.withMemo = withMemo;
|
|
1262
1322
|
Object.keys(di).forEach(function (k) {
|
|
1263
1323
|
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
1264
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
|
}
|
|
@@ -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
|
}
|