@viewfly/core 0.0.1-alpha.1 → 0.0.1-alpha.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundles/index.js CHANGED
@@ -4,6 +4,15 @@ require('reflect-metadata');
4
4
  var di = require('@tanbo/di');
5
5
  var stream = require('@tanbo/stream');
6
6
 
7
+ function makeError(name) {
8
+ return function viewflyError(message) {
9
+ const error = new Error(message);
10
+ error.name = `[ViewflyError: ${name}]`;
11
+ error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
12
+ return error;
13
+ };
14
+ }
15
+
7
16
  class NativeRenderer {
8
17
  }
9
18
 
@@ -35,163 +44,30 @@ function __metadata(metadataKey, metadataValue) {
35
44
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(metadataKey, metadataValue);
36
45
  }
37
46
 
38
- function Fragment(props) {
39
- return () => new JSXFragment(props);
40
- }
41
- class JSXFragment {
42
- constructor(props) {
43
- this.props = props;
44
- }
45
- }
46
- function jsx(setup, config) {
47
- if (typeof setup === 'string') {
48
- return new JSXElement(setup, config);
49
- }
50
- return function (context) {
51
- return new Component(context, setup, config);
52
- };
53
- }
54
- function jsxs(setup, config) {
55
- if (typeof setup === 'string') {
56
- return new JSXElement(setup, config);
57
- }
58
- return function (context) {
59
- return new Component(context, setup, config);
60
- };
61
- }
62
- class JSXText {
63
- constructor(text) {
64
- this.text = text;
65
- }
66
- }
67
- function flatChildren(jsxNodes) {
68
- const children = [];
69
- for (const node of jsxNodes) {
70
- if (node instanceof JSXElement || typeof node === 'function') {
71
- children.push(node);
72
- }
73
- else if (typeof node === 'string' && node.length) {
74
- children.push(new JSXText(node));
75
- }
76
- else if (Array.isArray(node)) {
77
- children.push(...flatChildren(node));
78
- }
79
- else if (node !== null && typeof node !== 'undefined') {
80
- children.push(new JSXText(String(node)));
81
- }
82
- }
83
- return children;
84
- }
85
- class Props {
86
- constructor(props) {
87
- this.attrs = new Map();
88
- this.styles = new Map();
89
- this.classes = new Set();
90
- this.listeners = {};
91
- this.children = [];
92
- if (!props) {
93
- return;
94
- }
95
- Object.keys(props).forEach(key => {
96
- if (key === 'children') {
97
- if (props.children !== null && typeof props.children !== 'undefined') {
98
- if (Array.isArray(props.children)) {
99
- this.children = flatChildren(props.children);
100
- }
101
- else {
102
- this.children = flatChildren([props.children]);
103
- }
104
- }
105
- return;
106
- }
107
- if (key === 'class') {
108
- this.classes = new Set(Props.classToArray(props[key]));
109
- return;
110
- }
111
- if (key === 'style') {
112
- const style = props.style || '';
113
- if (typeof style === 'string') {
114
- style.split(';').map(s => s.split(':')).forEach(v => {
115
- if (!v[0] || !v[1]) {
116
- return;
117
- }
118
- this.styles.set(v[0].trim(), v[1].trim());
119
- });
120
- }
121
- else if (typeof style === 'object') {
122
- Object.keys(style).forEach(key => {
123
- this.styles.set(key, style[key]);
124
- });
125
- }
126
- return;
127
- }
128
- if (/^on[A-Z]/.test(key)) {
129
- const listener = props[key];
130
- if (typeof listener === 'function') {
131
- this.listeners[key.replace(/^on/, '').toLowerCase()] = listener;
132
- }
133
- else {
134
- this.attrs.set(key, listener);
135
- }
136
- return;
137
- }
138
- this.attrs.set(key, props[key]);
139
- });
140
- }
141
- static classToArray(config) {
142
- const classes = [];
143
- if (!config) {
144
- return classes;
145
- }
146
- if (typeof config === 'string') {
147
- const items = config.match(/\S+/g);
148
- return items || classes;
149
- }
150
- else if (Array.isArray(config)) {
151
- for (const i of config) {
152
- classes.push(...Props.classToArray(i));
153
- }
154
- }
155
- else if (typeof config === 'object') {
156
- if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
157
- classes.push(config.toString());
158
- return classes;
159
- }
160
- for (const key in config) {
161
- if ({}.hasOwnProperty.call(config, key) && config[key]) {
162
- classes.push(key);
163
- }
164
- }
165
- }
166
- return classes;
167
- }
168
- }
169
- class JSXElement {
170
- constructor(name, config) {
171
- this.name = name;
172
- this.config = config;
173
- this.props = new Props(config);
174
- }
175
- }
176
-
177
- function makeError(name) {
178
- return function viewflyError(message) {
179
- const error = new Error(message);
180
- error.name = `[ViewflyError: ${name}]`;
181
- error.stack = error.stack.replace(/\n.*?(?=\n)/, '');
182
- return error;
183
- };
184
- }
185
-
186
- const componentStack = [];
47
+ const componentSetupStack = [];
48
+ const componentRendingStack = [];
49
+ const derivedStack = [];
187
50
  const componentErrorFn = makeError('component');
188
- function getComponentContext(need = true) {
189
- const current = componentStack[componentStack.length - 1];
51
+ function getSetupContext(need = true) {
52
+ const current = componentSetupStack[componentSetupStack.length - 1];
190
53
  if (!current && need) {
54
+ // 防止因外部捕获异常引引起的缓存未清理的问题
55
+ componentRendingStack.pop();
191
56
  throw componentErrorFn('cannot be called outside the component!');
192
57
  }
193
58
  return current;
194
59
  }
60
+ function getRendingContext() {
61
+ return componentRendingStack[componentRendingStack.length - 1];
62
+ }
63
+ function getDerivedContext() {
64
+ return derivedStack[derivedStack.length - 1];
65
+ }
66
+ class JSXComponent {
67
+ constructor(createInstance) {
68
+ this.createInstance = createInstance;
69
+ }
70
+ }
195
71
  /**
196
72
  * Viewfly 组件管理类,用于管理组件的生命周期,上下文等
197
73
  */
@@ -202,10 +78,12 @@ class Component extends di.ReflectiveInjector {
202
78
  get changed() {
203
79
  return this._changed;
204
80
  }
205
- constructor(context, setup, config) {
81
+ constructor(context, setup, props, key) {
206
82
  super(context, []);
207
83
  this.setup = setup;
208
- this.config = config;
84
+ this.props = props;
85
+ this.key = key;
86
+ this.$$typeOf = this.setup;
209
87
  this.destroyCallbacks = [];
210
88
  this.mountCallbacks = [];
211
89
  this.propsChangedCallbacks = [];
@@ -215,9 +93,11 @@ class Component extends di.ReflectiveInjector {
215
93
  this.updatedDestroyCallbacks = [];
216
94
  this.propsChangedDestroyCallbacks = [];
217
95
  this.isFirstRending = true;
218
- this.props = new Props(config);
219
96
  this.parentComponent = this.parentInjector;
220
97
  }
98
+ is(target) {
99
+ return target.$$typeOf === this.$$typeOf;
100
+ }
221
101
  addProvide(providers) {
222
102
  providers = Array.isArray(providers) ? providers : [providers];
223
103
  providers.forEach(p => {
@@ -225,27 +105,42 @@ class Component extends di.ReflectiveInjector {
225
105
  });
226
106
  }
227
107
  init() {
228
- componentStack.push(this);
229
108
  const self = this;
230
- const props = new Proxy({}, {
109
+ const props = new Proxy(this.props, {
231
110
  get(_, key) {
232
- if (self.config) {
233
- return self.config[key];
111
+ if (self.props) {
112
+ return self.props[key];
234
113
  }
235
114
  },
236
115
  set() {
116
+ // 防止因外部捕获异常引引起的缓存未清理的问题
117
+ if (isSetup) {
118
+ componentSetupStack.pop();
119
+ }
120
+ if (isRending) {
121
+ componentRendingStack.pop();
122
+ }
237
123
  throw componentErrorFn('component props is readonly!');
238
124
  }
239
125
  });
126
+ componentSetupStack.push(this);
127
+ let isSetup = true;
240
128
  const render = this.setup(props);
129
+ isSetup = false;
130
+ componentSetupStack.pop();
131
+ componentRendingStack.push(this);
132
+ let isRending = true;
241
133
  const template = render();
242
- componentStack.pop();
134
+ isRending = false;
135
+ componentRendingStack.pop();
243
136
  return {
244
137
  template,
245
138
  render: () => {
246
- componentStack.push(this);
139
+ componentRendingStack.push(this);
140
+ isRending = true;
247
141
  const template = render();
248
- componentStack.pop();
142
+ isRending = false;
143
+ componentRendingStack.pop();
249
144
  return template;
250
145
  }
251
146
  };
@@ -271,8 +166,8 @@ class Component extends di.ReflectiveInjector {
271
166
  }
272
167
  }
273
168
  invokePropsChangedHooks(newProps) {
274
- const oldProps = this.config;
275
- this.config = newProps;
169
+ const oldProps = this.props;
170
+ this.props = newProps;
276
171
  this.propsChangedDestroyCallbacks.forEach(fn => {
277
172
  fn();
278
173
  });
@@ -334,7 +229,7 @@ class Component extends di.ReflectiveInjector {
334
229
  * ```
335
230
  */
336
231
  function onMount(callback) {
337
- const component = getComponentContext();
232
+ const component = getSetupContext();
338
233
  component.mountCallbacks.push(callback);
339
234
  }
340
235
  /**
@@ -353,7 +248,7 @@ function onMount(callback) {
353
248
  * ```
354
249
  */
355
250
  function onUpdated(callback) {
356
- const component = getComponentContext();
251
+ const component = getSetupContext();
357
252
  component.updatedCallbacks.push(callback);
358
253
  return () => {
359
254
  const index = component.updatedCallbacks.indexOf(callback);
@@ -382,7 +277,7 @@ function onUpdated(callback) {
382
277
  * ```
383
278
  */
384
279
  function onPropsChanged(callback) {
385
- const component = getComponentContext();
280
+ const component = getSetupContext();
386
281
  component.propsChangedCallbacks.push(callback);
387
282
  return () => {
388
283
  const index = component.propsChangedCallbacks.indexOf(callback);
@@ -396,31 +291,34 @@ function onPropsChanged(callback) {
396
291
  * @param callback
397
292
  */
398
293
  function onDestroy(callback) {
399
- const component = getComponentContext();
294
+ const component = getSetupContext();
400
295
  component.destroyCallbacks.push(callback);
401
296
  }
402
297
  class Ref {
403
- // private prevValue: T | null = null
404
- constructor(callback, component) {
298
+ constructor(callback) {
405
299
  this.callback = callback;
406
- this.component = component;
407
- this.unListenFn = null;
408
- component.destroyCallbacks.push(() => {
409
- this.unListen();
410
- });
300
+ this.unBindMap = new WeakMap();
301
+ this.targetCaches = new Set();
302
+ }
303
+ bind(value) {
304
+ if (typeof value !== 'object' || value === null) {
305
+ return;
306
+ }
307
+ if (this.targetCaches.has(value)) {
308
+ return;
309
+ }
310
+ const unBindFn = this.callback(value);
311
+ if (typeof unBindFn === 'function') {
312
+ this.unBindMap.set(value, unBindFn);
313
+ }
314
+ this.targetCaches.add(value);
411
315
  }
412
- update(value) {
413
- // if (value === this.prevValue) {
414
- // return
415
- // }
416
- // this.prevValue = value
417
- this.unListen();
418
- this.unListenFn = this.callback(value) || null;
419
- }
420
- unListen() {
421
- if (typeof this.unListenFn === 'function') {
422
- this.unListenFn();
423
- this.unListenFn = null;
316
+ unBind(value) {
317
+ this.targetCaches.delete(value);
318
+ const unBindFn = this.unBindMap.get(value);
319
+ this.unBindMap.delete(value);
320
+ if (typeof unBindFn === 'function') {
321
+ unBindFn();
424
322
  }
425
323
  }
426
324
  }
@@ -446,8 +344,7 @@ class Ref {
446
344
  * ```
447
345
  */
448
346
  function useRef(callback) {
449
- const component = getComponentContext();
450
- return new Ref(callback, component);
347
+ return new Ref(callback);
451
348
  }
452
349
  const depsKey = Symbol('deps');
453
350
  /**
@@ -475,8 +372,12 @@ const depsKey = Symbol('deps');
475
372
  */
476
373
  function useSignal(state) {
477
374
  const usedComponents = new Set();
478
- function stateManager() {
479
- const component = getComponentContext(false);
375
+ function signal() {
376
+ const component = getRendingContext();
377
+ const derivedContext = getDerivedContext();
378
+ if (derivedContext) {
379
+ derivedContext.push(signal);
380
+ }
480
381
  if (component && !usedComponents.has(component)) {
481
382
  usedComponents.add(component);
482
383
  component.destroyCallbacks.push(() => {
@@ -485,44 +386,67 @@ function useSignal(state) {
485
386
  }
486
387
  return state;
487
388
  }
488
- stateManager.set = function (newState) {
489
- if (typeof newState === 'function') {
490
- newState = newState(state);
491
- }
492
- else if (newState === state) {
389
+ signal.set = function (newState) {
390
+ if (newState === state) {
493
391
  return;
494
392
  }
495
393
  state = newState;
496
394
  for (const component of usedComponents) {
497
395
  component.markAsDirtied();
498
396
  }
499
- for (const fn of stateManager[depsKey]) {
397
+ for (const fn of signal[depsKey]) {
500
398
  fn();
501
399
  }
502
400
  };
503
- stateManager[depsKey] = new Set();
504
- return stateManager;
401
+ signal[depsKey] = new Set();
402
+ return signal;
505
403
  }
506
404
  /**
507
- * 监听状态变化,当任意一个状态发生变更时,触发回调。
508
- * useEffect 会返回一个取消监听的函数,调用此函数,可以取消监听。
509
- * 当在组件中调用时,组件销毁时会自动取消监听。
510
- * @param deps 依赖的状态 Signal,可以是一个 Signal,只可以一个数包含 Signal 的数组
511
- * @param effect 状态变更后的回调函数
405
+ * 使用派生值,Viewfly 会收集回调函数内同步执行时访问的 Signal,
406
+ * 并在你获取 useDerived 函数返回的 Signal 的值时,自动计算最新的值。
407
+ *
408
+ * @param callback
409
+ * @param isContinue 可选的停止函数,在每次值更新后调用,当返回值为 false 时,将不再监听依赖的变化
512
410
  */
411
+ function useDerived(callback, isContinue) {
412
+ const deps = [];
413
+ derivedStack.push(deps);
414
+ const data = callback();
415
+ derivedStack.pop();
416
+ const signal = useSignal(data);
417
+ if (deps.length) {
418
+ const unListen = useEffect(deps, () => {
419
+ const data = callback();
420
+ signal.set(data);
421
+ if (typeof isContinue === 'function' && !isContinue(data)) {
422
+ unListen();
423
+ }
424
+ });
425
+ }
426
+ return signal;
427
+ }
428
+ /* eslint-enable max-len*/
513
429
  function useEffect(deps, effect) {
430
+ if (typeof deps === 'function' &&
431
+ typeof deps.set === 'undefined' &&
432
+ typeof deps[depsKey] === 'undefined') {
433
+ deps = useDerived(deps);
434
+ }
514
435
  const signals = Array.isArray(deps) ? deps : [deps];
436
+ let oldValues = signals.map(s => s());
515
437
  let prevCleanup;
516
438
  function effectCallback() {
517
439
  if (typeof prevCleanup === 'function') {
518
440
  prevCleanup();
519
441
  }
520
- prevCleanup = effect();
442
+ const newValues = signals.map(s => s());
443
+ prevCleanup = Array.isArray(deps) ? effect(newValues, oldValues) : effect(newValues[0], oldValues[0]);
444
+ oldValues = newValues;
521
445
  }
522
446
  for (const dep of signals) {
523
447
  dep[depsKey].add(effectCallback);
524
448
  }
525
- const component = getComponentContext(false);
449
+ const component = getSetupContext(false);
526
450
  let isClean = false;
527
451
  const destroyFn = () => {
528
452
  if (isClean) {
@@ -547,7 +471,7 @@ function useEffect(deps, effect) {
547
471
  * @param provider
548
472
  */
549
473
  function provide(provider) {
550
- const component = getComponentContext();
474
+ const component = getSetupContext();
551
475
  component.addProvide(provider);
552
476
  return component;
553
477
  }
@@ -555,16 +479,57 @@ function provide(provider) {
555
479
  * 通过组件上下文获取 IoC 容器内数据的勾子方法
556
480
  */
557
481
  function inject(token, notFoundValue, flags) {
558
- const component = getComponentContext();
482
+ const component = getSetupContext();
559
483
  return component.parentInjector.get(token, notFoundValue, flags);
560
484
  }
561
485
 
486
+ const jsxErrorFn = makeError('JSX');
487
+ const Fragment = function Fragment() {
488
+ throw jsxErrorFn('Fragment does not support calling.');
489
+ };
490
+ function jsx(setup, config, key) {
491
+ if (typeof setup === 'string') {
492
+ return new JSXElement(setup, config, key);
493
+ }
494
+ return new JSXComponent(function (context) {
495
+ return new Component(context, setup, config, key);
496
+ });
497
+ }
498
+ function jsxs(setup, config, key) {
499
+ if (typeof setup === 'string') {
500
+ return new JSXElement(setup, config, key);
501
+ }
502
+ return new JSXComponent(function (context) {
503
+ return new Component(context, setup, config, key);
504
+ });
505
+ }
506
+ class JSXText {
507
+ constructor(text) {
508
+ this.text = text;
509
+ this.$$typeOf = '#text';
510
+ }
511
+ is(target) {
512
+ return target.$$typeOf === this.$$typeOf;
513
+ }
514
+ }
515
+ class JSXElement {
516
+ constructor(name, props, key) {
517
+ this.name = name;
518
+ this.props = props;
519
+ this.key = key;
520
+ this.$$typeOf = this.name;
521
+ }
522
+ is(target) {
523
+ return target.$$typeOf === this.$$typeOf;
524
+ }
525
+ }
526
+
562
527
  /**
563
528
  * Viewfly 根组件,用于实现组件状态更新事件通知
564
529
  */
565
530
  class RootComponent extends Component {
566
- constructor(factory) {
567
- super(new di.NullInjector(), factory, new Props(null));
531
+ constructor(factory, parentInjector = new di.NullInjector()) {
532
+ super(parentInjector, factory, {});
568
533
  this.changeEmitter = new stream.Subject();
569
534
  }
570
535
  markAsChanged() {
@@ -574,142 +539,68 @@ class RootComponent extends Component {
574
539
  }
575
540
 
576
541
  const refKey = 'ref';
577
- function getObjectChanges(target, source) {
542
+ function getObjectChanges(newProps, oldProps) {
578
543
  const changes = {
579
544
  remove: [],
580
- add: []
545
+ add: [],
546
+ replace: []
581
547
  };
582
- // if (!target) {
583
- // if (source) {
584
- // Object.keys(source).forEach(key => {
585
- // changes.remove.push([key, source[key]])
586
- // })
587
- // }
588
- // return changes
589
- // }
590
- //
591
- // if (!source) {
592
- // Object.keys(target).forEach(key => {
593
- // changes.add.push([key, target[key]])
594
- // })
595
- // return changes
596
- // }
597
- Object.keys(target).forEach(key => {
598
- const leftValue = target[key];
599
- if (!Reflect.has(source, key)) {
600
- changes.add.push([key, leftValue]);
601
- return;
548
+ if (!newProps) {
549
+ if (oldProps) {
550
+ Object.keys(oldProps).forEach(key => {
551
+ changes.remove.push([key, oldProps[key]]);
552
+ });
602
553
  }
603
- const rightValue = source[key];
604
- if (leftValue === rightValue) {
554
+ return changes;
555
+ }
556
+ if (!oldProps) {
557
+ Object.keys(newProps).forEach(key => {
558
+ changes.add.push([key, newProps[key]]);
559
+ });
560
+ return changes;
561
+ }
562
+ Object.keys(newProps).forEach(key => {
563
+ const leftValue = newProps[key];
564
+ const rightValue = oldProps[key];
565
+ if (Reflect.has(oldProps, key)) {
566
+ if (leftValue !== rightValue) {
567
+ changes.replace.push([key, leftValue, rightValue]);
568
+ }
605
569
  return;
606
570
  }
607
571
  changes.add.push([key, leftValue]);
608
- changes.remove.push([key, rightValue]);
609
572
  });
610
- Object.keys(source).forEach(key => {
611
- if (!Reflect.has(target, key)) {
612
- changes.remove.push([key, source[key]]);
573
+ Object.keys(oldProps).forEach(key => {
574
+ if (!Reflect.has(newProps, key)) {
575
+ changes.remove.push([key, oldProps[key]]);
613
576
  }
614
577
  });
615
578
  return changes;
616
579
  }
617
- function getMapChanges(target, source) {
618
- const changes = {
619
- remove: [],
620
- set: []
621
- };
622
- // if (!target) {
623
- // if (source) {
624
- // source.forEach((value, key) => {
625
- // changes.remove.push([key, value])
626
- // })
627
- // }
628
- // return changes
629
- // }
630
- // if (!source) {
631
- // target.forEach((value, key) => {
632
- // changes.set.push([key, value])
633
- // })
634
- // return changes
635
- // }
636
- target.forEach((value, key) => {
637
- const rightValue = source.get(key);
638
- if (value === rightValue) {
639
- return;
580
+ function classToString(config) {
581
+ if (!config) {
582
+ return '';
583
+ }
584
+ if (typeof config === 'string') {
585
+ return config;
586
+ }
587
+ else if (Array.isArray(config)) {
588
+ return config.map(i => {
589
+ return classToString(i);
590
+ }).join(' ');
591
+ }
592
+ else if (typeof config === 'object') {
593
+ if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
594
+ return config.toString();
640
595
  }
641
- changes.set.push([key, value]);
642
- });
643
- source.forEach((value, key) => {
644
- if (key === refKey && value instanceof Ref) {
645
- const newValue = target.get(key);
646
- if (value !== newValue) {
647
- changes.remove.push([key, value]);
596
+ const classes = [];
597
+ for (const key in config) {
598
+ if ({}.hasOwnProperty.call(config, key) && config[key]) {
599
+ classes.push(key);
648
600
  }
649
- return;
650
- }
651
- if (!target.has(key)) {
652
- changes.remove.push([key, value]);
653
- }
654
- });
655
- return changes;
656
- }
657
- function getSetChanges(target, source) {
658
- const changes = {
659
- add: [],
660
- remove: []
661
- };
662
- // if (!target) {
663
- // if (source) {
664
- // source.forEach(i => {
665
- // changes.remove.push(i)
666
- // })
667
- // }
668
- // return changes
669
- // }
670
- //
671
- // if (!source) {
672
- // target.forEach(i => {
673
- // changes.add.push(i)
674
- // })
675
- // return changes
676
- // }
677
- target.forEach(i => {
678
- if (!source.has(i)) {
679
- changes.add.push(i);
680
601
  }
681
- });
682
- source.forEach(i => {
683
- if (!target.has(i)) {
684
- changes.remove.push(i);
685
- }
686
- });
687
- return changes;
688
- }
689
- const compareText = '0'.repeat(8);
690
- function getNodeChanges(newVNode, oldVNode) {
691
- const newProps = newVNode.props;
692
- const oldProps = oldVNode.props;
693
- const styleChanges = getMapChanges(newProps.styles, oldProps.styles);
694
- const attrChanges = getMapChanges(newProps.attrs, oldProps.attrs);
695
- const classesChanges = getSetChanges(newProps.classes, oldProps.classes);
696
- const listenerChanges = getObjectChanges(newProps.listeners, oldProps.listeners);
697
- return {
698
- styleChanges,
699
- attrChanges,
700
- classesChanges,
701
- listenerChanges,
702
- isChanged: [
703
- attrChanges.set.length,
704
- attrChanges.remove.length,
705
- styleChanges.set.length,
706
- styleChanges.remove.length,
707
- classesChanges.add.length,
708
- classesChanges.remove.length,
709
- listenerChanges.add.length,
710
- listenerChanges.remove.length
711
- ].join('') !== compareText
712
- };
602
+ return classes.join(' ');
603
+ }
713
604
  }
714
605
 
715
606
  class RootComponentRef {
@@ -731,10 +622,10 @@ exports.Renderer = class Renderer {
731
622
  }
732
623
  render() {
733
624
  const { component, host } = this.rootComponentRef;
734
- const chain = new Atom(component, null);
735
- const children = this.buildView(chain);
736
- children.forEach(child => {
737
- this.nativeRenderer.appendChild(host, child);
625
+ const atom = new Atom(component, null);
626
+ this.buildView(atom, {
627
+ isParent: true,
628
+ host
738
629
  });
739
630
  }
740
631
  refresh() {
@@ -752,6 +643,37 @@ exports.Renderer = class Renderer {
752
643
  const atom = this.componentAtomCaches.get(component).atom.child;
753
644
  this.reconcileElement(atom, context);
754
645
  }
646
+ else {
647
+ const prevSibling = this.getPrevSibling(component);
648
+ if (prevSibling) {
649
+ context.isParent = false;
650
+ context.host = prevSibling;
651
+ }
652
+ }
653
+ }
654
+ getPrevSibling(component) {
655
+ let atom = this.componentAtomCaches.get(component).atom.child;
656
+ const childAtoms = [];
657
+ while (atom) {
658
+ childAtoms.push(atom);
659
+ atom = atom.sibling;
660
+ }
661
+ const components = [];
662
+ while (childAtoms.length) {
663
+ const last = childAtoms.pop();
664
+ if (last.jsxNode instanceof Component) {
665
+ components.push(last.jsxNode);
666
+ continue;
667
+ }
668
+ return last.nativeNode;
669
+ }
670
+ for (const component of components) {
671
+ const nativeNode = this.getPrevSibling(component);
672
+ if (nativeNode) {
673
+ return nativeNode;
674
+ }
675
+ }
676
+ return null;
755
677
  }
756
678
  reconcileElement(atom, context) {
757
679
  while (atom) {
@@ -781,76 +703,150 @@ exports.Renderer = class Renderer {
781
703
  else {
782
704
  atom.child = null;
783
705
  }
784
- this.diff(atom.child, diffAtom, context);
706
+ this.diff(atom.child, diffAtom, context, 0, 0);
785
707
  component.rendered();
786
708
  }
787
- diff(start, diffAtom, context) {
709
+ diff(newAtom, oldAtom, context, expectIndex, index) {
788
710
  const oldChildren = [];
789
- while (diffAtom) {
790
- oldChildren.push(diffAtom);
791
- diffAtom = diffAtom.sibling;
711
+ while (oldAtom) {
712
+ oldChildren.push({
713
+ index,
714
+ atom: oldAtom
715
+ });
716
+ oldAtom = oldAtom.sibling;
717
+ index++;
792
718
  }
793
719
  const commits = [];
794
- const addReuseCommit = (start, reusedAtom) => {
795
- commits.push(() => {
796
- const isComponent = start.jsxNode instanceof Component;
797
- if (!isComponent) {
798
- const host = context.host;
799
- if (context.isParent) {
800
- this.nativeRenderer.prependChild(host, start.nativeNode);
720
+ const changeCommits = {
721
+ reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
722
+ commits.push(() => {
723
+ const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
724
+ if (add.length || remove.length || replace.length) {
725
+ reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
801
726
  }
802
- else {
803
- this.nativeRenderer.insertAfter(start.nativeNode, host);
727
+ const newProps = start.jsxNode.props;
728
+ start.jsxNode = reusedAtom.jsxNode;
729
+ start.jsxNode.props = newProps;
730
+ const { render } = this.componentAtomCaches.get(start.jsxNode);
731
+ const template = render();
732
+ if (template) {
733
+ this.linkTemplate(template, start.jsxNode, start);
734
+ }
735
+ this.componentAtomCaches.set(start.jsxNode, {
736
+ render,
737
+ atom: start
738
+ });
739
+ if (start.child) {
740
+ this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
741
+ }
742
+ else if (reusedAtom.child) {
743
+ let atom = reusedAtom.child;
744
+ while (atom) {
745
+ this.cleanView(atom, false);
746
+ atom = atom.sibling;
747
+ }
804
748
  }
805
- context.host = start.nativeNode;
806
- context.isParent = false;
807
- }
808
- if (start.child) {
809
- const childContext = start.jsxNode instanceof JSXElement ? {
810
- host: start.nativeNode,
811
- isParent: true
812
- } : context;
813
- this.diff(start.child, reusedAtom.child, childContext);
814
- }
815
- else if (reusedAtom.child) {
816
- this.cleanView(reusedAtom.child, false);
817
- }
818
- if (isComponent) {
819
749
  start.jsxNode.rendered();
820
- }
821
- });
822
- };
823
- const addCreateCommit = (start) => {
824
- commits.push(() => {
825
- const children = this.createViewByAtom(start);
826
- children.forEach(child => {
827
- if (context.isParent) {
828
- this.nativeRenderer.prependChild(context.host, child);
750
+ });
751
+ },
752
+ reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
753
+ commits.push((offset) => {
754
+ newAtom.nativeNode = oldAtom.nativeNode;
755
+ const host = context.host;
756
+ if (expectIndex !== oldIndex - offset) {
757
+ if (context.isParent) {
758
+ this.nativeRenderer.prependChild(host, newAtom.nativeNode);
759
+ }
760
+ else {
761
+ this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
762
+ }
763
+ }
764
+ context.host = newAtom.nativeNode;
765
+ context.isParent = false;
766
+ const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
767
+ if (newAtom.child) {
768
+ this.diff(newAtom.child, oldAtom.child, {
769
+ host: newAtom.nativeNode,
770
+ isParent: true
771
+ }, 0, 0);
829
772
  }
830
- else {
831
- this.nativeRenderer.insertAfter(child, context.host);
773
+ else if (oldAtom.child) {
774
+ let atom = oldAtom.child;
775
+ while (atom) {
776
+ this.cleanView(atom, false);
777
+ atom = atom.sibling;
778
+ }
779
+ }
780
+ applyRefs();
781
+ });
782
+ },
783
+ reuseText: (newAtom, oldAtom) => {
784
+ commits.push(() => {
785
+ const nativeNode = oldAtom.nativeNode;
786
+ if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
787
+ this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
832
788
  }
833
- context.host = child;
789
+ newAtom.nativeNode = nativeNode;
790
+ context.host = nativeNode;
834
791
  context.isParent = false;
835
792
  });
836
- });
837
- };
838
- while (start && !start.nativeNode) {
839
- const reusedAtom = this.reuseAndUpdate(start, oldChildren);
840
- if (reusedAtom) {
841
- addReuseCommit(start, reusedAtom);
793
+ },
794
+ create: (start) => {
795
+ commits.push(() => {
796
+ this.buildView(start, context);
797
+ });
842
798
  }
843
- else {
844
- addCreateCommit(start);
799
+ };
800
+ while (newAtom && !newAtom.nativeNode) {
801
+ this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
802
+ newAtom = newAtom.sibling;
803
+ expectIndex++;
804
+ }
805
+ for (const item of oldChildren) {
806
+ this.cleanView(item.atom, false);
807
+ }
808
+ let j = 0;
809
+ let offset = 0;
810
+ const len = oldChildren.length;
811
+ for (let i = 0; i < commits.length; i++) {
812
+ const commit = commits[i];
813
+ while (j < len) {
814
+ const current = oldChildren[j];
815
+ if (current.index <= i) {
816
+ offset++;
817
+ j++;
818
+ continue;
819
+ }
820
+ break;
845
821
  }
846
- start = start.sibling;
822
+ commit(offset);
847
823
  }
848
- for (const atom of oldChildren) {
849
- this.cleanView(atom, false);
850
- }
851
- for (const commit of commits) {
852
- commit();
824
+ }
825
+ createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
826
+ for (let i = 0; i < oldChildren.length; i++) {
827
+ const { atom: diffAtom, index: diffIndex } = oldChildren[i];
828
+ const key = newAtom.jsxNode.key;
829
+ const diffKey = diffAtom.jsxNode.key;
830
+ if (key !== undefined && diffKey !== undefined) {
831
+ if (diffKey !== key) {
832
+ continue;
833
+ }
834
+ }
835
+ if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
836
+ if (newAtom.jsxNode instanceof JSXElement) {
837
+ changeCommits.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
838
+ }
839
+ else if (newAtom.jsxNode instanceof JSXText) {
840
+ changeCommits.reuseText(newAtom, diffAtom);
841
+ }
842
+ else {
843
+ changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
844
+ }
845
+ oldChildren.splice(i, 1);
846
+ return;
847
+ }
853
848
  }
849
+ changeCommits.create(newAtom);
854
850
  }
855
851
  cleanView(atom, isClean) {
856
852
  if (atom.nativeNode) {
@@ -859,10 +855,8 @@ exports.Renderer = class Renderer {
859
855
  isClean = true;
860
856
  }
861
857
  if (atom.jsxNode instanceof JSXElement) {
862
- const ref = atom.jsxNode.props.attrs.get(refKey);
863
- if (ref instanceof Ref) {
864
- ref.unListen();
865
- }
858
+ const ref = atom.jsxNode.props[refKey];
859
+ this.applyRefs(ref, atom.nativeNode, false);
866
860
  }
867
861
  }
868
862
  let child = atom.child;
@@ -874,167 +868,77 @@ exports.Renderer = class Renderer {
874
868
  atom.jsxNode.destroy();
875
869
  }
876
870
  }
877
- reuseAndUpdate(start, oldChildren) {
878
- for (let i = 0; i < oldChildren.length; i++) {
879
- const diffAtom = oldChildren[i];
880
- if (start.jsxNode instanceof JSXElement) {
881
- if (diffAtom.jsxNode instanceof JSXElement && start.jsxNode.name === diffAtom.jsxNode.name) {
882
- const nativeNode = diffAtom.nativeNode;
883
- start.nativeNode = nativeNode;
884
- this.updateNativeNodeProperties(start.jsxNode, diffAtom.jsxNode, nativeNode);
885
- oldChildren.splice(i, 1);
886
- return diffAtom;
887
- }
888
- }
889
- else if (start.jsxNode instanceof JSXText) {
890
- if (diffAtom.jsxNode instanceof JSXText) {
891
- const nativeNode = diffAtom.nativeNode;
892
- if (start.jsxNode.text !== diffAtom.jsxNode.text) {
893
- this.nativeRenderer.syncTextContent(nativeNode, start.jsxNode.text);
894
- }
895
- start.nativeNode = nativeNode;
896
- oldChildren.splice(i, 1);
897
- return diffAtom;
898
- }
899
- }
900
- else if (diffAtom.jsxNode instanceof Component) {
901
- if (start.jsxNode.setup === diffAtom.jsxNode.setup) {
902
- const { isChanged } = getNodeChanges(start.jsxNode, diffAtom.jsxNode);
903
- if (isChanged) {
904
- diffAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.config);
905
- }
906
- start.jsxNode = diffAtom.jsxNode;
907
- const { render } = this.componentAtomCaches.get(start.jsxNode);
908
- const template = render();
909
- if (template) {
910
- this.linkTemplate(template, start.jsxNode, start);
911
- }
912
- this.componentAtomCaches.set(start.jsxNode, {
913
- render,
914
- atom: start
915
- });
916
- oldChildren.splice(i, 1);
917
- return diffAtom;
918
- }
871
+ buildView(atom, context) {
872
+ if (atom.jsxNode instanceof Component) {
873
+ this.componentRender(atom.jsxNode, atom);
874
+ let child = atom.child;
875
+ while (child) {
876
+ this.buildView(child, context);
877
+ child = child.sibling;
919
878
  }
879
+ atom.jsxNode.rendered();
920
880
  }
921
- return null;
922
- }
923
- createViewByAtom(atom) {
924
- if (atom.jsxNode instanceof JSXElement) {
925
- const nativeNode = this.createElement(atom.jsxNode);
926
- atom.nativeNode = nativeNode;
927
- if (atom.child) {
928
- const children = this.buildView(atom.child);
929
- for (const child of children) {
930
- this.nativeRenderer.appendChild(nativeNode, child);
931
- }
881
+ else {
882
+ let nativeNode;
883
+ let applyRefs = null;
884
+ if (atom.jsxNode instanceof JSXElement) {
885
+ const { nativeNode: n, applyRefs: a } = this.createElement(atom.jsxNode);
886
+ nativeNode = n;
887
+ applyRefs = a;
888
+ }
889
+ else {
890
+ nativeNode = this.createTextNode(atom.jsxNode);
932
891
  }
933
- return [nativeNode];
934
- }
935
- else if (atom.jsxNode instanceof JSXText) {
936
- const nativeNode = this.createTextNode(atom.jsxNode);
937
892
  atom.nativeNode = nativeNode;
938
- return [nativeNode];
939
- }
940
- const { template, render } = atom.jsxNode.init();
941
- this.componentAtomCaches.set(atom.jsxNode, {
942
- atom,
943
- render
944
- });
945
- if (template) {
946
- this.linkTemplate(template, atom.jsxNode, atom);
947
- }
948
- if (atom.child) {
949
- return this.buildView(atom.child);
950
- }
951
- return [];
952
- }
953
- buildView(chain) {
954
- const context = [];
955
- const children = [];
956
- function getContext() {
957
- return context[context.length - 1];
958
- }
959
- let atom = chain;
960
- const stopAtom = chain.parent;
961
- wrap: while (atom) {
962
- if (atom.jsxNode instanceof Component) {
963
- this.componentRender(atom.jsxNode, atom);
964
- if (atom.child) {
965
- atom = atom.child;
966
- continue;
967
- }
968
- atom.jsxNode.rendered();
893
+ if (context.isParent) {
894
+ this.nativeRenderer.prependChild(context.host, nativeNode);
969
895
  }
970
896
  else {
971
- const host = getContext();
972
- const nativeNode = atom.jsxNode instanceof JSXElement ? this.createElement(atom.jsxNode) : this.createTextNode(atom.jsxNode);
973
- atom.nativeNode = nativeNode;
974
- if (host) {
975
- this.nativeRenderer.appendChild(host, nativeNode);
976
- }
977
- else {
978
- children.push(nativeNode);
979
- }
980
- if (atom.child) {
981
- context.push(nativeNode);
982
- atom = atom.child;
983
- continue;
984
- }
897
+ this.nativeRenderer.insertAfter(nativeNode, context.host);
985
898
  }
986
- while (atom) {
987
- if (atom.sibling) {
988
- atom = atom.sibling;
989
- break;
990
- }
991
- atom = atom.parent;
992
- const isComponent = (atom === null || atom === void 0 ? void 0 : atom.jsxNode) instanceof Component;
993
- if (isComponent) {
994
- atom.jsxNode.rendered();
995
- }
996
- if (atom === stopAtom) {
997
- break wrap;
998
- }
999
- if (isComponent) {
1000
- continue;
899
+ if (atom.jsxNode instanceof JSXElement) {
900
+ const childContext = {
901
+ isParent: true,
902
+ host: nativeNode
903
+ };
904
+ let child = atom.child;
905
+ while (child) {
906
+ this.buildView(child, childContext);
907
+ child = child.sibling;
1001
908
  }
1002
- context.pop();
909
+ }
910
+ context.host = nativeNode;
911
+ context.isParent = false;
912
+ if (applyRefs) {
913
+ applyRefs();
1003
914
  }
1004
915
  }
1005
- return children;
1006
916
  }
1007
- componentRender(component, parent) {
917
+ componentRender(component, from) {
1008
918
  const { template, render } = component.init();
1009
919
  if (template) {
1010
- this.linkTemplate(template, component, parent);
920
+ this.linkTemplate(template, component, from);
1011
921
  }
1012
922
  this.componentAtomCaches.set(component, {
1013
923
  render,
1014
- atom: parent
924
+ atom: from
1015
925
  });
1016
- return parent;
926
+ return from;
1017
927
  }
1018
928
  createChainByComponentFactory(context, factory, parent) {
1019
- const component = factory(context);
929
+ const component = factory.createInstance(context);
1020
930
  if (component.setup === Fragment) {
1021
931
  return this.createChainByChildren(component, component.props.children, parent);
1022
932
  }
1023
933
  return new Atom(component, parent);
1024
934
  }
1025
- createChain(context, template, parent) {
1026
- if (template instanceof JSXElement) {
1027
- return this.createChainByJSXElement(context, template, parent);
1028
- }
1029
- if (template instanceof JSXText) {
1030
- return this.createChainByJSXText(template, parent);
1031
- }
1032
- return this.createChainByComponentFactory(context, template, parent);
1033
- }
1034
935
  createChainByJSXElement(context, element, parent) {
1035
936
  const atom = new Atom(element, parent);
1036
- const children = this.createChainByChildren(context, element.props.children, atom);
1037
- this.link(atom, children);
937
+ if (Reflect.has(element.props, 'children')) {
938
+ const jsxChildren = element.props.children;
939
+ const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
940
+ this.link(atom, children);
941
+ }
1038
942
  return atom;
1039
943
  }
1040
944
  createChainByJSXText(node, parent) {
@@ -1043,19 +947,39 @@ exports.Renderer = class Renderer {
1043
947
  createChainByChildren(context, children, parent) {
1044
948
  const atoms = [];
1045
949
  for (const item of children) {
1046
- const child = this.createChain(context, item, parent);
1047
- if (Array.isArray(child)) {
1048
- atoms.push(...child);
950
+ if (item instanceof JSXElement) {
951
+ atoms.push(this.createChainByJSXElement(context, item, parent));
952
+ continue;
1049
953
  }
1050
- else {
1051
- atoms.push(child);
954
+ if (item instanceof JSXComponent) {
955
+ const childAtom = this.createChainByComponentFactory(context, item, parent);
956
+ if (Array.isArray(childAtom)) {
957
+ atoms.push(...childAtom);
958
+ }
959
+ else {
960
+ atoms.push(childAtom);
961
+ }
962
+ continue;
963
+ }
964
+ if (typeof item === 'string' && item.length) {
965
+ atoms.push(this.createChainByJSXText(new JSXText(item), parent));
966
+ continue;
967
+ }
968
+ if (Array.isArray(item)) {
969
+ atoms.push(...this.createChainByChildren(context, item, parent));
970
+ continue;
971
+ }
972
+ if (item !== null && typeof item !== 'undefined') {
973
+ atoms.push(this.createChainByJSXText(new JSXText(String(item)), parent));
1052
974
  }
1053
975
  }
1054
976
  return atoms;
1055
977
  }
1056
978
  linkTemplate(template, component, parent) {
1057
979
  if (template) {
1058
- const child = this.createChain(component, template, parent);
980
+ const child = template instanceof JSXElement ?
981
+ this.createChainByJSXElement(component, template, parent) :
982
+ this.createChainByComponentFactory(component, template, parent);
1059
983
  this.link(parent, Array.isArray(child) ? child : [child]);
1060
984
  }
1061
985
  }
@@ -1069,56 +993,153 @@ exports.Renderer = class Renderer {
1069
993
  createElement(vNode) {
1070
994
  const nativeNode = this.nativeRenderer.createElement(vNode.name);
1071
995
  const props = vNode.props;
1072
- if (props) {
1073
- props.attrs.forEach((value, key) => {
1074
- if (key === refKey && value instanceof Ref) {
1075
- value.update(nativeNode);
1076
- return;
996
+ let bindingRefs;
997
+ const keys = Object.keys(props);
998
+ for (const key of keys) {
999
+ if (key === 'children') {
1000
+ continue;
1001
+ }
1002
+ if (key === 'class') {
1003
+ this.nativeRenderer.setClass(nativeNode, classToString(props[key]));
1004
+ continue;
1005
+ }
1006
+ if (key === 'style') {
1007
+ const style = props.style;
1008
+ Object.keys(style).forEach(key => {
1009
+ this.nativeRenderer.setStyle(nativeNode, key, style[key]);
1010
+ });
1011
+ continue;
1012
+ }
1013
+ if (/^on[A-Z]/.test(key)) {
1014
+ const listener = props[key];
1015
+ if (typeof listener === 'function') {
1016
+ this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), listener);
1077
1017
  }
1078
- this.nativeRenderer.setProperty(nativeNode, key, value);
1079
- });
1080
- props.styles.forEach((value, key) => {
1081
- this.nativeRenderer.setStyle(nativeNode, key, value);
1082
- });
1083
- props.classes.forEach(k => this.nativeRenderer.addClass(nativeNode, k));
1084
- Object.keys(props.listeners).forEach(type => {
1085
- this.nativeRenderer.listen(nativeNode, type, props.listeners[type]);
1086
- });
1018
+ continue;
1019
+ }
1020
+ if (key === refKey) {
1021
+ bindingRefs = props[key];
1022
+ continue;
1023
+ }
1024
+ this.nativeRenderer.setProperty(nativeNode, key, props[key]);
1087
1025
  }
1088
- return nativeNode;
1026
+ return {
1027
+ nativeNode,
1028
+ applyRefs: () => {
1029
+ this.applyRefs(bindingRefs, nativeNode, true);
1030
+ }
1031
+ };
1089
1032
  }
1090
1033
  createTextNode(child) {
1091
1034
  return this.nativeRenderer.createTextNode(child.text);
1092
1035
  }
1093
1036
  updateNativeNodeProperties(newVNode, oldVNode, nativeNode) {
1094
- const { styleChanges, attrChanges, classesChanges, listenerChanges, isChanged } = getNodeChanges(newVNode, oldVNode);
1095
- if (!isChanged) {
1096
- return;
1097
- }
1098
- styleChanges.remove.forEach(i => this.nativeRenderer.removeStyle(nativeNode, i[0]));
1099
- styleChanges.set.forEach(i => this.nativeRenderer.setStyle(nativeNode, i[0], i[1]));
1100
- attrChanges.remove.forEach(([key, value]) => {
1101
- if (key === refKey && value instanceof Ref) {
1102
- value.unListen();
1103
- return;
1037
+ const changes = getObjectChanges(newVNode.props, oldVNode.props);
1038
+ let unBindRefs;
1039
+ let bindRefs;
1040
+ for (const [key, value] of changes.remove) {
1041
+ if (key === 'children') {
1042
+ continue;
1043
+ }
1044
+ if (key === 'class') {
1045
+ this.nativeRenderer.setClass(nativeNode, '');
1046
+ continue;
1047
+ }
1048
+ if (key === 'style') {
1049
+ Object.keys(value).forEach(styleName => {
1050
+ this.nativeRenderer.removeStyle(nativeNode, styleName);
1051
+ });
1052
+ continue;
1053
+ }
1054
+ if (/^on[A-Z]/.test(key)) {
1055
+ if (typeof value === 'function') {
1056
+ this.nativeRenderer.unListen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
1057
+ }
1058
+ continue;
1059
+ }
1060
+ if (key === refKey) {
1061
+ unBindRefs = value;
1062
+ continue;
1104
1063
  }
1105
1064
  this.nativeRenderer.removeProperty(nativeNode, key);
1106
- });
1107
- attrChanges.set.forEach(([key, value]) => {
1108
- if (key === refKey && value instanceof Ref) {
1109
- value.update(nativeNode);
1110
- return;
1065
+ }
1066
+ for (const [key, newValue, oldValue] of changes.replace) {
1067
+ if (key === 'children') {
1068
+ continue;
1069
+ }
1070
+ if (key === 'class') {
1071
+ const oldClassName = classToString(oldValue);
1072
+ const newClassName = classToString(newValue);
1073
+ if (oldClassName !== newClassName) {
1074
+ this.nativeRenderer.setClass(nativeNode, newClassName);
1075
+ }
1076
+ continue;
1077
+ }
1078
+ if (key === 'style') {
1079
+ const styleChanges = getObjectChanges(newValue, oldValue);
1080
+ for (const [styleName] of styleChanges.remove) {
1081
+ this.nativeRenderer.removeStyle(nativeNode, styleName);
1082
+ }
1083
+ for (const [styleName, styleValue] of [...styleChanges.add, ...styleChanges.replace]) {
1084
+ this.nativeRenderer.setStyle(nativeNode, styleName, styleValue);
1085
+ }
1086
+ continue;
1087
+ }
1088
+ if (/^on[A-Z]/.test(key)) {
1089
+ const listenType = key.replace(/^on/, '').toLowerCase();
1090
+ if (typeof oldValue === 'function') {
1091
+ this.nativeRenderer.unListen(nativeNode, listenType, oldValue);
1092
+ }
1093
+ if (typeof newValue === 'function') {
1094
+ this.nativeRenderer.listen(nativeNode, listenType, newValue);
1095
+ }
1096
+ continue;
1097
+ }
1098
+ if (key === refKey) {
1099
+ unBindRefs = oldValue;
1100
+ bindRefs = newValue;
1101
+ continue;
1102
+ }
1103
+ this.nativeRenderer.setProperty(nativeNode, key, newValue);
1104
+ }
1105
+ for (const [key, value] of changes.add) {
1106
+ if (key === 'children') {
1107
+ continue;
1108
+ }
1109
+ if (key === 'class') {
1110
+ this.nativeRenderer.setClass(nativeNode, classToString(value));
1111
+ continue;
1112
+ }
1113
+ if (key === 'style') {
1114
+ Object.keys(value).forEach(styleName => {
1115
+ this.nativeRenderer.setStyle(nativeNode, styleName, value[styleName]);
1116
+ });
1117
+ continue;
1118
+ }
1119
+ if (/^on[A-Z]/.test(key)) {
1120
+ if (typeof value === 'function') {
1121
+ this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
1122
+ }
1123
+ continue;
1124
+ }
1125
+ if (key === refKey) {
1126
+ bindRefs = value;
1127
+ continue;
1111
1128
  }
1112
1129
  this.nativeRenderer.setProperty(nativeNode, key, value);
1113
- });
1114
- classesChanges.remove.forEach(i => this.nativeRenderer.removeClass(nativeNode, i));
1115
- classesChanges.add.forEach(i => this.nativeRenderer.addClass(nativeNode, i));
1116
- listenerChanges.remove.forEach(i => {
1117
- this.nativeRenderer.unListen(nativeNode, i[0], i[1]);
1118
- });
1119
- listenerChanges.add.forEach(i => {
1120
- this.nativeRenderer.listen(nativeNode, i[0], i[1]);
1121
- });
1130
+ }
1131
+ return () => {
1132
+ this.applyRefs(unBindRefs, nativeNode, false);
1133
+ this.applyRefs(bindRefs, nativeNode, true);
1134
+ };
1135
+ }
1136
+ applyRefs(refs, nativeNode, binding) {
1137
+ refs = Array.isArray(refs) ? refs : [refs];
1138
+ for (const item of refs) {
1139
+ if (item instanceof Ref) {
1140
+ binding ? item.bind(nativeNode) : item.unBind(nativeNode);
1141
+ }
1142
+ }
1122
1143
  }
1123
1144
  };
1124
1145
  exports.Renderer = __decorate([
@@ -1160,7 +1181,7 @@ class Viewfly extends di.ReflectiveInjector {
1160
1181
  /**
1161
1182
  * 启动 Viewfly
1162
1183
  */
1163
- start() {
1184
+ run() {
1164
1185
  const renderer = this.get(exports.Renderer);
1165
1186
  renderer.render();
1166
1187
  if (this.config.autoUpdate === false) {
@@ -1185,17 +1206,16 @@ class Viewfly extends di.ReflectiveInjector {
1185
1206
  return () => {
1186
1207
  return this.destroyed ? null : rootNode;
1187
1208
  };
1188
- });
1209
+ }, this.config.context);
1189
1210
  }
1190
1211
  }
1191
1212
 
1192
1213
  exports.Component = Component;
1193
1214
  exports.Fragment = Fragment;
1215
+ exports.JSXComponent = JSXComponent;
1194
1216
  exports.JSXElement = JSXElement;
1195
- exports.JSXFragment = JSXFragment;
1196
1217
  exports.JSXText = JSXText;
1197
1218
  exports.NativeRenderer = NativeRenderer;
1198
- exports.Props = Props;
1199
1219
  exports.Ref = Ref;
1200
1220
  exports.RootComponent = RootComponent;
1201
1221
  exports.RootComponentRef = RootComponentRef;
@@ -1203,11 +1223,13 @@ exports.Viewfly = Viewfly;
1203
1223
  exports.inject = inject;
1204
1224
  exports.jsx = jsx;
1205
1225
  exports.jsxs = jsxs;
1226
+ exports.makeError = makeError;
1206
1227
  exports.onDestroy = onDestroy;
1207
1228
  exports.onMount = onMount;
1208
1229
  exports.onPropsChanged = onPropsChanged;
1209
1230
  exports.onUpdated = onUpdated;
1210
1231
  exports.provide = provide;
1232
+ exports.useDerived = useDerived;
1211
1233
  exports.useEffect = useEffect;
1212
1234
  exports.useRef = useRef;
1213
1235
  exports.useSignal = useSignal;