@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.
@@ -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;
@@ -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 template = render();
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
- const template = render();
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
- reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
738
- commits.push(() => {
739
- const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
740
- if (add.length || remove.length || replace.length) {
741
- reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
742
- }
743
- const newProps = start.jsxNode.props;
744
- start.jsxNode = reusedAtom.jsxNode;
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
- atom: start
768
+ template,
769
+ atom: newAtom
754
770
  });
755
- if (start.child) {
756
- this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
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
- start.jsxNode.rendered();
789
+ newAtom.jsxNode.rendered();
766
790
  });
767
791
  },
768
- reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
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
- reuseText: (newAtom, oldAtom) => {
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.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
911
+ changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
854
912
  }
855
913
  else if (newAtom.jsxNode instanceof JSXText) {
856
- changeCommits.reuseText(newAtom, diffAtom);
914
+ changeCommits.updateText(newAtom, diffAtom);
857
915
  }
858
916
  else {
859
- changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
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
- atoms.push(...this.createChainByChildren(context, item, parent));
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 template = render();
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
- const template = render();
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
- reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
739
- commits.push(() => {
740
- const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
741
- if (add.length || remove.length || replace.length) {
742
- reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
743
- }
744
- const newProps = start.jsxNode.props;
745
- start.jsxNode = reusedAtom.jsxNode;
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
- atom: start
769
+ template,
770
+ atom: newAtom
755
771
  });
756
- if (start.child) {
757
- this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
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
- start.jsxNode.rendered();
790
+ newAtom.jsxNode.rendered();
767
791
  });
768
792
  },
769
- reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
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
- reuseText: (newAtom, oldAtom) => {
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.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
912
+ changeCommits.updateElement(newAtom, diffAtom, expectIndex, diffIndex);
855
913
  }
856
914
  else if (newAtom.jsxNode instanceof JSXText) {
857
- changeCommits.reuseText(newAtom, diffAtom);
915
+ changeCommits.updateText(newAtom, diffAtom);
858
916
  }
859
917
  else {
860
- changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
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
- atoms.push(...this.createChainByChildren(context, item, parent));
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,
@@ -1,3 +1,4 @@
1
1
  export * from './component';
2
2
  export * from './jsx-element';
3
+ export * from './memo';
3
4
  export * from './root.component';
@@ -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.4",
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": "fed5b3e328a9f70873bb1c33ea3789c099ad3ebe"
37
+ "gitHead": "f3cb35e8a4c24063ea8431de7d001d801925e9d9"
38
38
  }