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

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