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