@viewfly/core 0.0.3 → 0.0.5

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