@viewfly/core 0.0.1-alpha.7 → 0.0.1-alpha.8

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.
@@ -3,10 +3,6 @@ export interface MapChanges {
3
3
  remove: [string, any][];
4
4
  set: [string, any][];
5
5
  }
6
- export interface ArrayChanges {
7
- remove: string[];
8
- add: string[];
9
- }
10
6
  export interface ObjectChanges {
11
7
  remove: [string, any][];
12
8
  add: [string, any][];
@@ -14,11 +10,9 @@ export interface ObjectChanges {
14
10
  export declare const refKey = "ref";
15
11
  export declare function getObjectChanges(target: Record<string, any>, source: Record<string, any>): ObjectChanges;
16
12
  export declare function getMapChanges(target: Map<string, any>, source: Map<string, any>): MapChanges;
17
- export declare function getSetChanges(target: Set<string>, source: Set<string>): ArrayChanges;
18
13
  export declare function getNodeChanges(newVNode: JSXElement | Component, oldVNode: JSXElement | Component): {
19
14
  styleChanges: MapChanges;
20
15
  attrChanges: MapChanges;
21
- classesChanges: ArrayChanges;
22
16
  listenerChanges: ObjectChanges;
23
17
  isChanged: boolean;
24
18
  };
@@ -8,8 +8,7 @@ export declare abstract class NativeRenderer<ElementNode = NativeNode, TextNode
8
8
  abstract removeProperty(node: ElementNode, key: string): void;
9
9
  abstract setStyle(target: ElementNode, key: string, value: any): void;
10
10
  abstract removeStyle(target: ElementNode, key: string): void;
11
- abstract addClass(target: ElementNode, name: string): void;
12
- abstract removeClass(target: ElementNode, name: string): void;
11
+ abstract setClass(target: ElementNode, value: string): void;
13
12
  abstract listen<T = any>(node: ElementNode, type: string, callback: (ev: T) => any): void;
14
13
  abstract unListen(node: ElementNode, type: string, callback: (ev: any) => any): void;
15
14
  abstract remove(node: ElementNode | TextNode): void;
@@ -16,8 +16,8 @@ export declare class Renderer {
16
16
  private reconcileElement;
17
17
  private applyChanges;
18
18
  private diff;
19
+ private createChanges;
19
20
  private cleanView;
20
- private reuseAndUpdate;
21
21
  private temporarilyRemove;
22
22
  private buildView;
23
23
  private componentRender;
@@ -66,6 +66,10 @@ function jsxs(setup, config, key) {
66
66
  class JSXText {
67
67
  constructor(text) {
68
68
  this.text = text;
69
+ this.$$typeOf = '#text';
70
+ }
71
+ is(target) {
72
+ return target.$$typeOf === this.$$typeOf;
69
73
  }
70
74
  }
71
75
  function flatChildren(jsxNodes) {
@@ -90,7 +94,7 @@ class Props {
90
94
  constructor(props) {
91
95
  this.attrs = new Map();
92
96
  this.styles = new Map();
93
- this.classes = new Set();
97
+ this.classes = '';
94
98
  this.listeners = {};
95
99
  this.children = [];
96
100
  if (!props) {
@@ -109,7 +113,7 @@ class Props {
109
113
  return;
110
114
  }
111
115
  if (key === 'class') {
112
- this.classes = new Set(Props.classToArray(props[key]));
116
+ this.classes = Props.classToString(props[key]);
113
117
  return;
114
118
  }
115
119
  if (key === 'style') {
@@ -142,6 +146,31 @@ class Props {
142
146
  this.attrs.set(key, props[key]);
143
147
  });
144
148
  }
149
+ static classToString(config) {
150
+ if (!config) {
151
+ return '';
152
+ }
153
+ if (typeof config === 'string') {
154
+ return config;
155
+ }
156
+ else if (Array.isArray(config)) {
157
+ return config.map(i => {
158
+ return Props.classToString(i);
159
+ }).join(' ');
160
+ }
161
+ else if (typeof config === 'object') {
162
+ if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
163
+ return config.toString();
164
+ }
165
+ const classes = [];
166
+ for (const key in config) {
167
+ if ({}.hasOwnProperty.call(config, key) && config[key]) {
168
+ classes.push(key);
169
+ }
170
+ }
171
+ return classes.join(' ');
172
+ }
173
+ }
145
174
  static classToArray(config) {
146
175
  const classes = [];
147
176
  if (!config) {
@@ -175,8 +204,12 @@ class JSXElement {
175
204
  this.name = name;
176
205
  this.config = config;
177
206
  this.key = key;
207
+ this.$$typeOf = this.name;
178
208
  this.props = new Props(config);
179
209
  }
210
+ is(target) {
211
+ return target.$$typeOf === this.$$typeOf;
212
+ }
180
213
  }
181
214
 
182
215
  const componentSetupStack = [];
@@ -218,6 +251,7 @@ class Component extends ReflectiveInjector {
218
251
  this.setup = setup;
219
252
  this.config = config;
220
253
  this.key = key;
254
+ this.$$typeOf = this.setup;
221
255
  this.destroyCallbacks = [];
222
256
  this.mountCallbacks = [];
223
257
  this.propsChangedCallbacks = [];
@@ -230,6 +264,9 @@ class Component extends ReflectiveInjector {
230
264
  this.props = new Props(config);
231
265
  this.parentComponent = this.parentInjector;
232
266
  }
267
+ is(target) {
268
+ return target.$$typeOf === this.$$typeOf;
269
+ }
233
270
  addProvide(providers) {
234
271
  providers = Array.isArray(providers) ? providers : [providers];
235
272
  providers.forEach(p => {
@@ -635,21 +672,6 @@ function getObjectChanges(target, source) {
635
672
  remove: [],
636
673
  add: []
637
674
  };
638
- // if (!target) {
639
- // if (source) {
640
- // Object.keys(source).forEach(key => {
641
- // changes.remove.push([key, source[key]])
642
- // })
643
- // }
644
- // return changes
645
- // }
646
- //
647
- // if (!source) {
648
- // Object.keys(target).forEach(key => {
649
- // changes.add.push([key, target[key]])
650
- // })
651
- // return changes
652
- // }
653
675
  Object.keys(target).forEach(key => {
654
676
  const leftValue = target[key];
655
677
  if (!Reflect.has(source, key)) {
@@ -675,20 +697,6 @@ function getMapChanges(target, source) {
675
697
  remove: [],
676
698
  set: []
677
699
  };
678
- // if (!target) {
679
- // if (source) {
680
- // source.forEach((value, key) => {
681
- // changes.remove.push([key, value])
682
- // })
683
- // }
684
- // return changes
685
- // }
686
- // if (!source) {
687
- // target.forEach((value, key) => {
688
- // changes.set.push([key, value])
689
- // })
690
- // return changes
691
- // }
692
700
  target.forEach((value, key) => {
693
701
  const rightValue = source.get(key);
694
702
  if (value === rightValue) {
@@ -710,58 +718,22 @@ function getMapChanges(target, source) {
710
718
  });
711
719
  return changes;
712
720
  }
713
- function getSetChanges(target, source) {
714
- const changes = {
715
- add: [],
716
- remove: []
717
- };
718
- // if (!target) {
719
- // if (source) {
720
- // source.forEach(i => {
721
- // changes.remove.push(i)
722
- // })
723
- // }
724
- // return changes
725
- // }
726
- //
727
- // if (!source) {
728
- // target.forEach(i => {
729
- // changes.add.push(i)
730
- // })
731
- // return changes
732
- // }
733
- target.forEach(i => {
734
- if (!source.has(i)) {
735
- changes.add.push(i);
736
- }
737
- });
738
- source.forEach(i => {
739
- if (!target.has(i)) {
740
- changes.remove.push(i);
741
- }
742
- });
743
- return changes;
744
- }
745
- const compareText = '0'.repeat(8);
721
+ const compareText = '0'.repeat(6);
746
722
  function getNodeChanges(newVNode, oldVNode) {
747
723
  const newProps = newVNode.props;
748
724
  const oldProps = oldVNode.props;
749
725
  const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
750
726
  const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
751
- const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
752
727
  const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
753
728
  return {
754
729
  styleChanges,
755
730
  attrChanges,
756
- classesChanges,
757
731
  listenerChanges,
758
732
  isChanged: [
759
733
  attrChanges.set.length,
760
734
  attrChanges.remove.length,
761
735
  styleChanges.set.length,
762
736
  styleChanges.remove.length,
763
- classesChanges.add.length,
764
- classesChanges.remove.length,
765
737
  listenerChanges.add.length,
766
738
  listenerChanges.remove.length
767
739
  ].join('') !== compareText
@@ -868,110 +840,120 @@ let Renderer = class Renderer {
868
840
  else {
869
841
  atom.child = null;
870
842
  }
871
- this.diff(atom.child, diffAtom, context);
843
+ this.diff(atom.child, diffAtom, context, 0, 0);
872
844
  component.rendered();
873
845
  }
874
- diff(start, diffAtom, context) {
846
+ diff(newAtom, oldAtom, context, expectIndex, index) {
875
847
  const oldChildren = [];
876
- let index = 0;
877
- while (diffAtom) {
848
+ while (oldAtom) {
878
849
  oldChildren.push({
879
850
  index,
880
- atom: diffAtom
851
+ atom: oldAtom
881
852
  });
882
- diffAtom = diffAtom.sibling;
853
+ oldAtom = oldAtom.sibling;
883
854
  index++;
884
855
  }
885
856
  const commits = [];
886
- const addUpdateCommit = (start, reusedAtom) => {
887
- commits.push(() => {
888
- const isComponent = start.jsxNode instanceof Component;
889
- if (!isComponent) {
890
- const host = context.host;
891
- if (context.isParent) {
892
- this.nativeRenderer.prependChild(host, start.nativeNode);
857
+ const changeCommits = {
858
+ reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
859
+ commits.push(() => {
860
+ const { isChanged } = getNodeChanges(start.jsxNode, reusedAtom.jsxNode);
861
+ if (isChanged) {
862
+ reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
893
863
  }
894
- else {
895
- this.nativeRenderer.insertAfter(start.nativeNode, host);
864
+ const newProps = start.jsxNode.props;
865
+ start.jsxNode = reusedAtom.jsxNode;
866
+ start.jsxNode.props = newProps;
867
+ const { render } = this.componentAtomCaches.get(start.jsxNode);
868
+ const template = render();
869
+ if (template) {
870
+ this.linkTemplate(template, start.jsxNode, start);
896
871
  }
897
- context.host = start.nativeNode;
898
- context.isParent = false;
899
- }
900
- let applyRefs = null;
901
- if (start.jsxNode instanceof JSXElement) {
902
- applyRefs = this.updateNativeNodeProperties(start.jsxNode, reusedAtom.jsxNode, start.nativeNode);
903
- }
904
- if (start.child) {
905
- const childContext = start.jsxNode instanceof JSXElement ? {
906
- host: start.nativeNode,
907
- isParent: true
908
- } : context;
909
- this.diff(start.child, reusedAtom.child, childContext);
910
- }
911
- else if (reusedAtom.child) {
912
- let atom = reusedAtom.child;
913
- while (atom) {
914
- this.cleanView(atom, false);
915
- atom = atom.sibling;
872
+ this.componentAtomCaches.set(start.jsxNode, {
873
+ render,
874
+ atom: start
875
+ });
876
+ if (start.child) {
877
+ this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
878
+ }
879
+ else if (reusedAtom.child) {
880
+ let atom = reusedAtom.child;
881
+ while (atom) {
882
+ this.cleanView(atom, false);
883
+ atom = atom.sibling;
884
+ }
916
885
  }
917
- }
918
- if (applyRefs) {
919
- applyRefs();
920
- }
921
- if (isComponent) {
922
886
  start.jsxNode.rendered();
923
- }
924
- });
925
- };
926
- const addCreateCommit = (start) => {
927
- commits.push(() => {
928
- this.buildView(start, context);
929
- });
930
- };
931
- let i = 0;
932
- while (start && !start.nativeNode) {
933
- const reusedAtom = this.reuseAndUpdate(start, i, oldChildren);
934
- if (reusedAtom) {
935
- addUpdateCommit(start, reusedAtom);
936
- }
937
- else {
938
- addCreateCommit(start);
887
+ });
888
+ },
889
+ reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
890
+ commits.push(() => {
891
+ newAtom.nativeNode = oldAtom.nativeNode;
892
+ const host = context.host;
893
+ if (expectIndex !== oldIndex) {
894
+ if (context.isParent) {
895
+ this.nativeRenderer.prependChild(host, newAtom.nativeNode);
896
+ }
897
+ else {
898
+ this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
899
+ }
900
+ }
901
+ context.host = newAtom.nativeNode;
902
+ context.isParent = false;
903
+ const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
904
+ if (newAtom.child) {
905
+ this.diff(newAtom.child, oldAtom.child, {
906
+ host: newAtom.nativeNode,
907
+ isParent: true
908
+ }, 0, 0);
909
+ }
910
+ else if (oldAtom.child) {
911
+ let atom = oldAtom.child;
912
+ while (atom) {
913
+ this.cleanView(atom, false);
914
+ atom = atom.sibling;
915
+ }
916
+ }
917
+ if (applyRefs) {
918
+ applyRefs();
919
+ }
920
+ });
921
+ },
922
+ reuseText: (newAtom, oldAtom) => {
923
+ commits.push(() => {
924
+ const nativeNode = oldAtom.nativeNode;
925
+ if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
926
+ this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
927
+ }
928
+ newAtom.nativeNode = nativeNode;
929
+ context.host = nativeNode;
930
+ context.isParent = false;
931
+ });
932
+ },
933
+ create: (start) => {
934
+ commits.push(() => {
935
+ this.buildView(start, context);
936
+ });
939
937
  }
940
- i++;
941
- start = start.sibling;
938
+ };
939
+ while (newAtom && !newAtom.nativeNode) {
940
+ this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
941
+ newAtom = newAtom.sibling;
942
+ expectIndex++;
942
943
  }
943
944
  for (const item of oldChildren) {
944
945
  this.cleanView(item.atom, false);
945
946
  }
946
- for (const commit of commits) {
947
+ for (let i = 0; i < commits.length; i++) {
948
+ const commit = commits[i];
947
949
  commit();
948
950
  }
949
951
  }
950
- cleanView(atom, isClean) {
951
- if (atom.nativeNode) {
952
- if (!isClean) {
953
- this.nativeRenderer.remove(atom.nativeNode);
954
- isClean = true;
955
- }
956
- if (atom.jsxNode instanceof JSXElement) {
957
- const ref = atom.jsxNode.props.attrs.get(refKey);
958
- this.applyRefs(ref, atom.nativeNode, false);
959
- }
960
- }
961
- let child = atom.child;
962
- while (child) {
963
- this.cleanView(child, isClean);
964
- child = child.sibling;
965
- }
966
- if (atom.jsxNode instanceof Component) {
967
- atom.jsxNode.destroy();
968
- }
969
- }
970
- reuseAndUpdate(start, lastIndex, oldChildren) {
952
+ createChanges(newAtom, lastIndex, oldChildren, changeCommits) {
971
953
  let isReuse = false;
972
954
  for (let i = 0; i < oldChildren.length; i++) {
973
955
  const { atom: diffAtom, index: diffIndex } = oldChildren[i];
974
- const key = start.jsxNode.key;
956
+ const key = newAtom.jsxNode.key;
975
957
  const diffKey = diffAtom.jsxNode.key;
976
958
  if (key !== undefined && diffKey !== undefined) {
977
959
  if (diffKey !== key) {
@@ -979,55 +961,47 @@ let Renderer = class Renderer {
979
961
  }
980
962
  isReuse = lastIndex > diffIndex;
981
963
  }
982
- if (start.jsxNode instanceof JSXElement) {
983
- if (diffAtom.jsxNode instanceof JSXElement && start.jsxNode.name === diffAtom.jsxNode.name) {
984
- const nativeNode = diffAtom.nativeNode;
964
+ if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
965
+ if (newAtom.jsxNode instanceof JSXElement) {
985
966
  if (isReuse) {
986
- this.nativeRenderer.remove(nativeNode);
967
+ this.nativeRenderer.remove(diffAtom.nativeNode);
987
968
  }
988
- start.nativeNode = nativeNode;
989
- oldChildren.splice(i, 1);
990
- return diffAtom;
969
+ changeCommits.reuseElement(newAtom, diffAtom, lastIndex, diffIndex);
991
970
  }
992
- }
993
- else if (start.jsxNode instanceof JSXText) {
994
- if (diffAtom.jsxNode instanceof JSXText) {
995
- const nativeNode = diffAtom.nativeNode;
996
- if (start.jsxNode.text !== diffAtom.jsxNode.text) {
997
- this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
998
- }
999
- start.nativeNode = nativeNode;
1000
- oldChildren.splice(i, 1);
1001
- return diffAtom;
971
+ else if (newAtom.jsxNode instanceof JSXText) {
972
+ changeCommits.reuseText(newAtom, diffAtom);
1002
973
  }
1003
- }
1004
- else if (diffAtom.jsxNode instanceof Component) {
1005
- if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
974
+ else {
1006
975
  if (isReuse) {
1007
976
  this.temporarilyRemove(diffAtom);
1008
977
  }
1009
- const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
1010
- if (isChanged) {
1011
- diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
1012
- }
1013
- const newProps = start.jsxNode.props;
1014
- start.jsxNode = diffAtom.jsxNode;
1015
- start.jsxNode.props = newProps;
1016
- const { render } = this.componentAtomCaches.get(start.jsxNode);
1017
- const template = render();
1018
- if (template) {
1019
- this.linkTemplate(template, start.jsxNode, start);
1020
- }
1021
- this.componentAtomCaches.set(start.jsxNode, {
1022
- render,
1023
- atom: start
1024
- });
1025
- oldChildren.splice(i, 1);
1026
- return diffAtom;
978
+ changeCommits.reuseComponent(newAtom, diffAtom, lastIndex, diffIndex);
1027
979
  }
980
+ oldChildren.splice(i, 1);
981
+ return;
1028
982
  }
1029
983
  }
1030
- return null;
984
+ changeCommits.create(newAtom);
985
+ }
986
+ cleanView(atom, isClean) {
987
+ if (atom.nativeNode) {
988
+ if (!isClean) {
989
+ this.nativeRenderer.remove(atom.nativeNode);
990
+ isClean = true;
991
+ }
992
+ if (atom.jsxNode instanceof JSXElement) {
993
+ const ref = atom.jsxNode.props.attrs.get(refKey);
994
+ this.applyRefs(ref, atom.nativeNode, false);
995
+ }
996
+ }
997
+ let child = atom.child;
998
+ while (child) {
999
+ this.cleanView(child, isClean);
1000
+ child = child.sibling;
1001
+ }
1002
+ if (atom.jsxNode instanceof Component) {
1003
+ atom.jsxNode.destroy();
1004
+ }
1031
1005
  }
1032
1006
  temporarilyRemove(atom) {
1033
1007
  let next = atom.child;
@@ -1163,7 +1137,9 @@ let Renderer = class Renderer {
1163
1137
  props.styles.forEach((value, key) => {
1164
1138
  this.nativeRenderer.setStyle(nativeNode, key, value);
1165
1139
  });
1166
- props.classes.forEach(k => this.nativeRenderer.addClass(nativeNode, k));
1140
+ if (props.classes) {
1141
+ this.nativeRenderer.setClass(nativeNode, props.classes);
1142
+ }
1167
1143
  Object.keys(props.listeners).forEach(type => {
1168
1144
  this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
1169
1145
  });
@@ -1178,10 +1154,11 @@ let Renderer = class Renderer {
1178
1154
  return this.nativeRenderer.createTextNode(child.text);
1179
1155
  }
1180
1156
  updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
1181
- const { styleChanges, attrChanges, classesChanges, listenerChanges, isChanged } = getNodeChanges(newVNode, oldVNode);
1182
- if (!isChanged) {
1183
- return null;
1184
- }
1157
+ const newProps = newVNode.props;
1158
+ const oldProps = oldVNode.props;
1159
+ const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
1160
+ const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
1161
+ const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
1185
1162
  styleChanges.remove.forEach(i => this.nativeRenderer.removeStyle(nativeNode, i[0]));
1186
1163
  styleChanges.set.forEach(i => this.nativeRenderer.setStyle(nativeNode, i[0], i[1]));
1187
1164
  let unBindRefs;
@@ -1200,8 +1177,9 @@ let Renderer = class Renderer {
1200
1177
  }
1201
1178
  this.nativeRenderer.setProperty(nativeNode, key, value);
1202
1179
  });
1203
- classesChanges.remove.forEach(i => this.nativeRenderer.removeClass(nativeNode, i));
1204
- classesChanges.add.forEach(i => this.nativeRenderer.addClass(nativeNode, i));
1180
+ if (newProps.classes !== oldProps.classes) {
1181
+ this.nativeRenderer.setClass(nativeNode, newProps.classes);
1182
+ }
1205
1183
  listenerChanges.remove.forEach(i => {
1206
1184
  this.nativeRenderer.unListen(nativeNode, i[0], i[1]);
1207
1185
  });
package/bundles/index.js CHANGED
@@ -67,6 +67,10 @@ function jsxs(setup, config, key) {
67
67
  class JSXText {
68
68
  constructor(text) {
69
69
  this.text = text;
70
+ this.$$typeOf = '#text';
71
+ }
72
+ is(target) {
73
+ return target.$$typeOf === this.$$typeOf;
70
74
  }
71
75
  }
72
76
  function flatChildren(jsxNodes) {
@@ -91,7 +95,7 @@ class Props {
91
95
  constructor(props) {
92
96
  this.attrs = new Map();
93
97
  this.styles = new Map();
94
- this.classes = new Set();
98
+ this.classes = '';
95
99
  this.listeners = {};
96
100
  this.children = [];
97
101
  if (!props) {
@@ -110,7 +114,7 @@ class Props {
110
114
  return;
111
115
  }
112
116
  if (key === 'class') {
113
- this.classes = new Set(Props.classToArray(props[key]));
117
+ this.classes = Props.classToString(props[key]);
114
118
  return;
115
119
  }
116
120
  if (key === 'style') {
@@ -143,6 +147,31 @@ class Props {
143
147
  this.attrs.set(key, props[key]);
144
148
  });
145
149
  }
150
+ static classToString(config) {
151
+ if (!config) {
152
+ return '';
153
+ }
154
+ if (typeof config === 'string') {
155
+ return config;
156
+ }
157
+ else if (Array.isArray(config)) {
158
+ return config.map(i => {
159
+ return Props.classToString(i);
160
+ }).join(' ');
161
+ }
162
+ else if (typeof config === 'object') {
163
+ if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
164
+ return config.toString();
165
+ }
166
+ const classes = [];
167
+ for (const key in config) {
168
+ if ({}.hasOwnProperty.call(config, key) && config[key]) {
169
+ classes.push(key);
170
+ }
171
+ }
172
+ return classes.join(' ');
173
+ }
174
+ }
146
175
  static classToArray(config) {
147
176
  const classes = [];
148
177
  if (!config) {
@@ -176,8 +205,12 @@ class JSXElement {
176
205
  this.name = name;
177
206
  this.config = config;
178
207
  this.key = key;
208
+ this.$$typeOf = this.name;
179
209
  this.props = new Props(config);
180
210
  }
211
+ is(target) {
212
+ return target.$$typeOf === this.$$typeOf;
213
+ }
181
214
  }
182
215
 
183
216
  const componentSetupStack = [];
@@ -219,6 +252,7 @@ class Component extends di.ReflectiveInjector {
219
252
  this.setup = setup;
220
253
  this.config = config;
221
254
  this.key = key;
255
+ this.$$typeOf = this.setup;
222
256
  this.destroyCallbacks = [];
223
257
  this.mountCallbacks = [];
224
258
  this.propsChangedCallbacks = [];
@@ -231,6 +265,9 @@ class Component extends di.ReflectiveInjector {
231
265
  this.props = new Props(config);
232
266
  this.parentComponent = this.parentInjector;
233
267
  }
268
+ is(target) {
269
+ return target.$$typeOf === this.$$typeOf;
270
+ }
234
271
  addProvide(providers) {
235
272
  providers = Array.isArray(providers) ? providers : [providers];
236
273
  providers.forEach(p => {
@@ -636,21 +673,6 @@ function getObjectChanges(target, source) {
636
673
  remove: [],
637
674
  add: []
638
675
  };
639
- // if (!target) {
640
- // if (source) {
641
- // Object.keys(source).forEach(key => {
642
- // changes.remove.push([key, source[key]])
643
- // })
644
- // }
645
- // return changes
646
- // }
647
- //
648
- // if (!source) {
649
- // Object.keys(target).forEach(key => {
650
- // changes.add.push([key, target[key]])
651
- // })
652
- // return changes
653
- // }
654
676
  Object.keys(target).forEach(key => {
655
677
  const leftValue = target[key];
656
678
  if (!Reflect.has(source, key)) {
@@ -676,20 +698,6 @@ function getMapChanges(target, source) {
676
698
  remove: [],
677
699
  set: []
678
700
  };
679
- // if (!target) {
680
- // if (source) {
681
- // source.forEach((value, key) => {
682
- // changes.remove.push([key, value])
683
- // })
684
- // }
685
- // return changes
686
- // }
687
- // if (!source) {
688
- // target.forEach((value, key) => {
689
- // changes.set.push([key, value])
690
- // })
691
- // return changes
692
- // }
693
701
  target.forEach((value, key) => {
694
702
  const rightValue = source.get(key);
695
703
  if (value === rightValue) {
@@ -711,58 +719,22 @@ function getMapChanges(target, source) {
711
719
  });
712
720
  return changes;
713
721
  }
714
- function getSetChanges(target, source) {
715
- const changes = {
716
- add: [],
717
- remove: []
718
- };
719
- // if (!target) {
720
- // if (source) {
721
- // source.forEach(i => {
722
- // changes.remove.push(i)
723
- // })
724
- // }
725
- // return changes
726
- // }
727
- //
728
- // if (!source) {
729
- // target.forEach(i => {
730
- // changes.add.push(i)
731
- // })
732
- // return changes
733
- // }
734
- target.forEach(i => {
735
- if (!source.has(i)) {
736
- changes.add.push(i);
737
- }
738
- });
739
- source.forEach(i => {
740
- if (!target.has(i)) {
741
- changes.remove.push(i);
742
- }
743
- });
744
- return changes;
745
- }
746
- const compareText = '0'.repeat(8);
722
+ const compareText = '0'.repeat(6);
747
723
  function getNodeChanges(newVNode, oldVNode) {
748
724
  const newProps = newVNode.props;
749
725
  const oldProps = oldVNode.props;
750
726
  const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
751
727
  const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
752
- const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
753
728
  const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
754
729
  return {
755
730
  styleChanges,
756
731
  attrChanges,
757
- classesChanges,
758
732
  listenerChanges,
759
733
  isChanged: [
760
734
  attrChanges.set.length,
761
735
  attrChanges.remove.length,
762
736
  styleChanges.set.length,
763
737
  styleChanges.remove.length,
764
- classesChanges.add.length,
765
- classesChanges.remove.length,
766
738
  listenerChanges.add.length,
767
739
  listenerChanges.remove.length
768
740
  ].join('') !== compareText
@@ -869,110 +841,120 @@ exports.Renderer = class Renderer {
869
841
  else {
870
842
  atom.child = null;
871
843
  }
872
- this.diff(atom.child, diffAtom, context);
844
+ this.diff(atom.child, diffAtom, context, 0, 0);
873
845
  component.rendered();
874
846
  }
875
- diff(start, diffAtom, context) {
847
+ diff(newAtom, oldAtom, context, expectIndex, index) {
876
848
  const oldChildren = [];
877
- let index = 0;
878
- while (diffAtom) {
849
+ while (oldAtom) {
879
850
  oldChildren.push({
880
851
  index,
881
- atom: diffAtom
852
+ atom: oldAtom
882
853
  });
883
- diffAtom = diffAtom.sibling;
854
+ oldAtom = oldAtom.sibling;
884
855
  index++;
885
856
  }
886
857
  const commits = [];
887
- const addUpdateCommit = (start, reusedAtom) => {
888
- commits.push(() => {
889
- const isComponent = start.jsxNode instanceof Component;
890
- if (!isComponent) {
891
- const host = context.host;
892
- if (context.isParent) {
893
- this.nativeRenderer.prependChild(host, start.nativeNode);
858
+ const changeCommits = {
859
+ reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
860
+ commits.push(() => {
861
+ const { isChanged } = getNodeChanges(start.jsxNode, reusedAtom.jsxNode);
862
+ if (isChanged) {
863
+ reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
894
864
  }
895
- else {
896
- this.nativeRenderer.insertAfter(start.nativeNode, host);
865
+ const newProps = start.jsxNode.props;
866
+ start.jsxNode = reusedAtom.jsxNode;
867
+ start.jsxNode.props = newProps;
868
+ const { render } = this.componentAtomCaches.get(start.jsxNode);
869
+ const template = render();
870
+ if (template) {
871
+ this.linkTemplate(template, start.jsxNode, start);
897
872
  }
898
- context.host = start.nativeNode;
899
- context.isParent = false;
900
- }
901
- let applyRefs = null;
902
- if (start.jsxNode instanceof JSXElement) {
903
- applyRefs = this.updateNativeNodeProperties(start.jsxNode, reusedAtom.jsxNode, start.nativeNode);
904
- }
905
- if (start.child) {
906
- const childContext = start.jsxNode instanceof JSXElement ? {
907
- host: start.nativeNode,
908
- isParent: true
909
- } : context;
910
- this.diff(start.child, reusedAtom.child, childContext);
911
- }
912
- else if (reusedAtom.child) {
913
- let atom = reusedAtom.child;
914
- while (atom) {
915
- this.cleanView(atom, false);
916
- atom = atom.sibling;
873
+ this.componentAtomCaches.set(start.jsxNode, {
874
+ render,
875
+ atom: start
876
+ });
877
+ if (start.child) {
878
+ this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
879
+ }
880
+ else if (reusedAtom.child) {
881
+ let atom = reusedAtom.child;
882
+ while (atom) {
883
+ this.cleanView(atom, false);
884
+ atom = atom.sibling;
885
+ }
917
886
  }
918
- }
919
- if (applyRefs) {
920
- applyRefs();
921
- }
922
- if (isComponent) {
923
887
  start.jsxNode.rendered();
924
- }
925
- });
926
- };
927
- const addCreateCommit = (start) => {
928
- commits.push(() => {
929
- this.buildView(start, context);
930
- });
931
- };
932
- let i = 0;
933
- while (start && !start.nativeNode) {
934
- const reusedAtom = this.reuseAndUpdate(start, i, oldChildren);
935
- if (reusedAtom) {
936
- addUpdateCommit(start, reusedAtom);
937
- }
938
- else {
939
- addCreateCommit(start);
888
+ });
889
+ },
890
+ reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
891
+ commits.push(() => {
892
+ newAtom.nativeNode = oldAtom.nativeNode;
893
+ const host = context.host;
894
+ if (expectIndex !== oldIndex) {
895
+ if (context.isParent) {
896
+ this.nativeRenderer.prependChild(host, newAtom.nativeNode);
897
+ }
898
+ else {
899
+ this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
900
+ }
901
+ }
902
+ context.host = newAtom.nativeNode;
903
+ context.isParent = false;
904
+ const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
905
+ if (newAtom.child) {
906
+ this.diff(newAtom.child, oldAtom.child, {
907
+ host: newAtom.nativeNode,
908
+ isParent: true
909
+ }, 0, 0);
910
+ }
911
+ else if (oldAtom.child) {
912
+ let atom = oldAtom.child;
913
+ while (atom) {
914
+ this.cleanView(atom, false);
915
+ atom = atom.sibling;
916
+ }
917
+ }
918
+ if (applyRefs) {
919
+ applyRefs();
920
+ }
921
+ });
922
+ },
923
+ reuseText: (newAtom, oldAtom) => {
924
+ commits.push(() => {
925
+ const nativeNode = oldAtom.nativeNode;
926
+ if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
927
+ this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
928
+ }
929
+ newAtom.nativeNode = nativeNode;
930
+ context.host = nativeNode;
931
+ context.isParent = false;
932
+ });
933
+ },
934
+ create: (start) => {
935
+ commits.push(() => {
936
+ this.buildView(start, context);
937
+ });
940
938
  }
941
- i++;
942
- start = start.sibling;
939
+ };
940
+ while (newAtom && !newAtom.nativeNode) {
941
+ this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
942
+ newAtom = newAtom.sibling;
943
+ expectIndex++;
943
944
  }
944
945
  for (const item of oldChildren) {
945
946
  this.cleanView(item.atom, false);
946
947
  }
947
- for (const commit of commits) {
948
+ for (let i = 0; i < commits.length; i++) {
949
+ const commit = commits[i];
948
950
  commit();
949
951
  }
950
952
  }
951
- cleanView(atom, isClean) {
952
- if (atom.nativeNode) {
953
- if (!isClean) {
954
- this.nativeRenderer.remove(atom.nativeNode);
955
- isClean = true;
956
- }
957
- if (atom.jsxNode instanceof JSXElement) {
958
- const ref = atom.jsxNode.props.attrs.get(refKey);
959
- this.applyRefs(ref, atom.nativeNode, false);
960
- }
961
- }
962
- let child = atom.child;
963
- while (child) {
964
- this.cleanView(child, isClean);
965
- child = child.sibling;
966
- }
967
- if (atom.jsxNode instanceof Component) {
968
- atom.jsxNode.destroy();
969
- }
970
- }
971
- reuseAndUpdate(start, lastIndex, oldChildren) {
953
+ createChanges(newAtom, lastIndex, oldChildren, changeCommits) {
972
954
  let isReuse = false;
973
955
  for (let i = 0; i < oldChildren.length; i++) {
974
956
  const { atom: diffAtom, index: diffIndex } = oldChildren[i];
975
- const key = start.jsxNode.key;
957
+ const key = newAtom.jsxNode.key;
976
958
  const diffKey = diffAtom.jsxNode.key;
977
959
  if (key !== undefined && diffKey !== undefined) {
978
960
  if (diffKey !== key) {
@@ -980,55 +962,47 @@ exports.Renderer = class Renderer {
980
962
  }
981
963
  isReuse = lastIndex > diffIndex;
982
964
  }
983
- if (start.jsxNode instanceof JSXElement) {
984
- if (diffAtom.jsxNode instanceof JSXElement && start.jsxNode.name === diffAtom.jsxNode.name) {
985
- const nativeNode = diffAtom.nativeNode;
965
+ if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
966
+ if (newAtom.jsxNode instanceof JSXElement) {
986
967
  if (isReuse) {
987
- this.nativeRenderer.remove(nativeNode);
968
+ this.nativeRenderer.remove(diffAtom.nativeNode);
988
969
  }
989
- start.nativeNode = nativeNode;
990
- oldChildren.splice(i, 1);
991
- return diffAtom;
970
+ changeCommits.reuseElement(newAtom, diffAtom, lastIndex, diffIndex);
992
971
  }
993
- }
994
- else if (start.jsxNode instanceof JSXText) {
995
- if (diffAtom.jsxNode instanceof JSXText) {
996
- const nativeNode = diffAtom.nativeNode;
997
- if (start.jsxNode.text !== diffAtom.jsxNode.text) {
998
- this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
999
- }
1000
- start.nativeNode = nativeNode;
1001
- oldChildren.splice(i, 1);
1002
- return diffAtom;
972
+ else if (newAtom.jsxNode instanceof JSXText) {
973
+ changeCommits.reuseText(newAtom, diffAtom);
1003
974
  }
1004
- }
1005
- else if (diffAtom.jsxNode instanceof Component) {
1006
- if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
975
+ else {
1007
976
  if (isReuse) {
1008
977
  this.temporarilyRemove(diffAtom);
1009
978
  }
1010
- const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
1011
- if (isChanged) {
1012
- diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
1013
- }
1014
- const newProps = start.jsxNode.props;
1015
- start.jsxNode = diffAtom.jsxNode;
1016
- start.jsxNode.props = newProps;
1017
- const { render } = this.componentAtomCaches.get(start.jsxNode);
1018
- const template = render();
1019
- if (template) {
1020
- this.linkTemplate(template, start.jsxNode, start);
1021
- }
1022
- this.componentAtomCaches.set(start.jsxNode, {
1023
- render,
1024
- atom: start
1025
- });
1026
- oldChildren.splice(i, 1);
1027
- return diffAtom;
979
+ changeCommits.reuseComponent(newAtom, diffAtom, lastIndex, diffIndex);
1028
980
  }
981
+ oldChildren.splice(i, 1);
982
+ return;
1029
983
  }
1030
984
  }
1031
- return null;
985
+ changeCommits.create(newAtom);
986
+ }
987
+ cleanView(atom, isClean) {
988
+ if (atom.nativeNode) {
989
+ if (!isClean) {
990
+ this.nativeRenderer.remove(atom.nativeNode);
991
+ isClean = true;
992
+ }
993
+ if (atom.jsxNode instanceof JSXElement) {
994
+ const ref = atom.jsxNode.props.attrs.get(refKey);
995
+ this.applyRefs(ref, atom.nativeNode, false);
996
+ }
997
+ }
998
+ let child = atom.child;
999
+ while (child) {
1000
+ this.cleanView(child, isClean);
1001
+ child = child.sibling;
1002
+ }
1003
+ if (atom.jsxNode instanceof Component) {
1004
+ atom.jsxNode.destroy();
1005
+ }
1032
1006
  }
1033
1007
  temporarilyRemove(atom) {
1034
1008
  let next = atom.child;
@@ -1164,7 +1138,9 @@ exports.Renderer = class Renderer {
1164
1138
  props.styles.forEach((value, key) => {
1165
1139
  this.nativeRenderer.setStyle(nativeNode, key, value);
1166
1140
  });
1167
- props.classes.forEach(k => this.nativeRenderer.addClass(nativeNode, k));
1141
+ if (props.classes) {
1142
+ this.nativeRenderer.setClass(nativeNode, props.classes);
1143
+ }
1168
1144
  Object.keys(props.listeners).forEach(type => {
1169
1145
  this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
1170
1146
  });
@@ -1179,10 +1155,11 @@ exports.Renderer = class Renderer {
1179
1155
  return this.nativeRenderer.createTextNode(child.text);
1180
1156
  }
1181
1157
  updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
1182
- const { styleChanges, attrChanges, classesChanges, listenerChanges, isChanged } = getNodeChanges(newVNode, oldVNode);
1183
- if (!isChanged) {
1184
- return null;
1185
- }
1158
+ const newProps = newVNode.props;
1159
+ const oldProps = oldVNode.props;
1160
+ const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
1161
+ const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
1162
+ const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
1186
1163
  styleChanges.remove.forEach(i => this.nativeRenderer.removeStyle(nativeNode, i[0]));
1187
1164
  styleChanges.set.forEach(i => this.nativeRenderer.setStyle(nativeNode, i[0], i[1]));
1188
1165
  let unBindRefs;
@@ -1201,8 +1178,9 @@ exports.Renderer = class Renderer {
1201
1178
  }
1202
1179
  this.nativeRenderer.setProperty(nativeNode, key, value);
1203
1180
  });
1204
- classesChanges.remove.forEach(i => this.nativeRenderer.removeClass(nativeNode, i));
1205
- classesChanges.add.forEach(i => this.nativeRenderer.addClass(nativeNode, i));
1181
+ if (newProps.classes !== oldProps.classes) {
1182
+ this.nativeRenderer.setClass(nativeNode, newProps.classes);
1183
+ }
1206
1184
  listenerChanges.remove.forEach(i => {
1207
1185
  this.nativeRenderer.unListen(nativeNode, i[0], i[1]);
1208
1186
  });
@@ -1,5 +1,5 @@
1
1
  import { Provider, ReflectiveInjector, AbstractType, Type, InjectionToken, InjectFlags, Injector } from '@tanbo/di';
2
- import { JSXProps, JSXElement, Props, Key } from './jsx-element';
2
+ import { JSXProps, JSXElement, Props, Key, JSXTypeof } from './jsx-element';
3
3
  export declare class JSXComponent {
4
4
  createInstance: (injector: Component) => Component;
5
5
  constructor(createInstance: (injector: Component) => Component);
@@ -11,10 +11,11 @@ export interface ComponentSetup<T extends JSXProps<any> = JSXProps<any>> {
11
11
  /**
12
12
  * Viewfly 组件管理类,用于管理组件的生命周期,上下文等
13
13
  */
14
- export declare class Component extends ReflectiveInjector {
14
+ export declare class Component extends ReflectiveInjector implements JSXTypeof {
15
15
  setup: ComponentSetup;
16
16
  config?: JSXProps<any> | null | undefined;
17
17
  key?: Key | undefined;
18
+ $$typeOf: ComponentSetup<JSXProps<any>>;
18
19
  destroyCallbacks: LifeCycleCallback[];
19
20
  mountCallbacks: LifeCycleCallback[];
20
21
  propsChangedCallbacks: PropsChangedCallback<any>[];
@@ -29,6 +30,7 @@ export declare class Component extends ReflectiveInjector {
29
30
  private propsChangedDestroyCallbacks;
30
31
  private isFirstRending;
31
32
  constructor(context: Injector, setup: ComponentSetup, config?: JSXProps<any> | null | undefined, key?: Key | undefined);
33
+ is(target: JSXTypeof): boolean;
32
34
  addProvide<T>(providers: Provider<T> | Provider<T>[]): void;
33
35
  init(): {
34
36
  template: JSXTemplate;
@@ -11,27 +11,36 @@ export declare function jsx<T extends JSXChildNode>(name: string, config?: JSXPr
11
11
  export declare function jsx<T extends JSXChildNode>(setup: ComponentSetup, config?: JSXProps<T> | null, key?: Key): JSXComponent;
12
12
  export declare function jsxs<T extends JSXChildNode[]>(name: string, config?: JSXProps<T> | null, key?: Key): JSXElement;
13
13
  export declare function jsxs<T extends JSXChildNode[]>(setup: ComponentSetup, config?: JSXProps<T> | null, key?: Key): JSXComponent;
14
+ export interface JSXTypeof {
15
+ $$typeOf: string | ComponentSetup;
16
+ is(target: JSXTypeof): boolean;
17
+ }
14
18
  export interface VElementListeners {
15
19
  [listenKey: string]: <T extends Event>(ev: T) => any;
16
20
  }
17
- export declare class JSXText {
21
+ export declare class JSXText implements JSXTypeof {
18
22
  text: string;
23
+ $$typeOf: string;
19
24
  constructor(text: string);
25
+ is(target: JSXTypeof): boolean;
20
26
  }
21
- export type VNode = JSXElement | JSXComponent | JSXText;
27
+ export type JSXNode = JSXElement | JSXComponent | JSXText;
22
28
  export declare class Props {
23
29
  attrs: Map<string, any>;
24
30
  styles: Map<string, string | number>;
25
- classes: Set<string>;
31
+ classes: string;
26
32
  listeners: VElementListeners;
27
- children: VNode[];
33
+ children: JSXNode[];
28
34
  constructor(props?: JSXProps<JSXChildNode> | JSXProps<JSXChildNode[]> | null);
35
+ static classToString(config: unknown): any;
29
36
  static classToArray(config: unknown): string[];
30
37
  }
31
- export declare class JSXElement {
38
+ export declare class JSXElement implements JSXTypeof {
32
39
  name: string;
33
40
  config?: JSXProps<any> | null | undefined;
34
41
  key?: Key | undefined;
42
+ $$typeOf: string;
35
43
  props: Props;
36
44
  constructor(name: string, config?: JSXProps<any> | null | undefined, key?: Key | undefined);
45
+ is(target: JSXTypeof): boolean;
37
46
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@viewfly/core",
3
- "version": "0.0.1-alpha.7",
3
+ "version": "0.0.1-alpha.8",
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",
@@ -37,5 +37,5 @@
37
37
  "bugs": {
38
38
  "url": "https://github.com/viewfly/viewfly.git/issues"
39
39
  },
40
- "gitHead": "b4aa728ce10f6aaca2bb2e242c7b8b8f8befbfec"
40
+ "gitHead": "462b51e8623348002ba7c6ba2caf7df131534506"
41
41
  }