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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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,68 @@ 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
- }
653
- });
654
- return changes;
655
- }
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
600
  }
680
- });
681
- source.forEach(i => {
682
- if (!target.has(i)) {
683
- changes.remove.push(i);
684
- }
685
- });
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
- };
601
+ return classes.join(' ');
602
+ }
712
603
  }
713
604
 
714
605
  class RootComponentRef {
@@ -730,10 +621,10 @@ let Renderer = class Renderer {
730
621
  }
731
622
  render() {
732
623
  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);
624
+ const atom = new Atom(component, null);
625
+ this.buildView(atom, {
626
+ isParent: true,
627
+ host
737
628
  });
738
629
  }
739
630
  refresh() {
@@ -751,6 +642,37 @@ let Renderer = class Renderer {
751
642
  const atom = this.componentAtomCaches.get(component).atom.child;
752
643
  this.reconcileElement(atom, context);
753
644
  }
645
+ else {
646
+ const prevSibling = this.getPrevSibling(component);
647
+ if (prevSibling) {
648
+ context.isParent = false;
649
+ context.host = prevSibling;
650
+ }
651
+ }
652
+ }
653
+ getPrevSibling(component) {
654
+ let atom = this.componentAtomCaches.get(component).atom.child;
655
+ const childAtoms = [];
656
+ while (atom) {
657
+ childAtoms.push(atom);
658
+ atom = atom.sibling;
659
+ }
660
+ const components = [];
661
+ while (childAtoms.length) {
662
+ const last = childAtoms.pop();
663
+ if (last.jsxNode instanceof Component) {
664
+ components.push(last.jsxNode);
665
+ continue;
666
+ }
667
+ return last.nativeNode;
668
+ }
669
+ for (const component of components) {
670
+ const nativeNode = this.getPrevSibling(component);
671
+ if (nativeNode) {
672
+ return nativeNode;
673
+ }
674
+ }
675
+ return null;
754
676
  }
755
677
  reconcileElement(atom, context) {
756
678
  while (atom) {
@@ -780,76 +702,150 @@ let Renderer = class Renderer {
780
702
  else {
781
703
  atom.child = null;
782
704
  }
783
- this.diff(atom.child, diffAtom, context);
705
+ this.diff(atom.child, diffAtom, context, 0, 0);
784
706
  component.rendered();
785
707
  }
786
- diff(start, diffAtom, context) {
708
+ diff(newAtom, oldAtom, context, expectIndex, index) {
787
709
  const oldChildren = [];
788
- while (diffAtom) {
789
- oldChildren.push(diffAtom);
790
- diffAtom = diffAtom.sibling;
710
+ while (oldAtom) {
711
+ oldChildren.push({
712
+ index,
713
+ atom: oldAtom
714
+ });
715
+ oldAtom = oldAtom.sibling;
716
+ index++;
791
717
  }
792
718
  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);
719
+ const changeCommits = {
720
+ reuseComponent: (start, reusedAtom, expectIndex, diffIndex) => {
721
+ commits.push(() => {
722
+ const { add, remove, replace } = getObjectChanges(start.jsxNode.props, reusedAtom.jsxNode.props);
723
+ if (add.length || remove.length || replace.length) {
724
+ reusedAtom.jsxNode.invokePropsChangedHooks(start.jsxNode.props);
800
725
  }
801
- else {
802
- this.nativeRenderer.insertAfter(start.nativeNode, host);
726
+ const newProps = start.jsxNode.props;
727
+ start.jsxNode = reusedAtom.jsxNode;
728
+ start.jsxNode.props = newProps;
729
+ const { render } = this.componentAtomCaches.get(start.jsxNode);
730
+ const template = render();
731
+ if (template) {
732
+ this.linkTemplate(template, start.jsxNode, start);
733
+ }
734
+ this.componentAtomCaches.set(start.jsxNode, {
735
+ render,
736
+ atom: start
737
+ });
738
+ if (start.child) {
739
+ this.diff(start.child, reusedAtom.child, context, expectIndex, diffIndex);
740
+ }
741
+ else if (reusedAtom.child) {
742
+ let atom = reusedAtom.child;
743
+ while (atom) {
744
+ this.cleanView(atom, false);
745
+ atom = atom.sibling;
746
+ }
803
747
  }
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
748
  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);
749
+ });
750
+ },
751
+ reuseElement: (newAtom, oldAtom, expectIndex, oldIndex) => {
752
+ commits.push((offset) => {
753
+ newAtom.nativeNode = oldAtom.nativeNode;
754
+ const host = context.host;
755
+ if (expectIndex !== oldIndex - offset) {
756
+ if (context.isParent) {
757
+ this.nativeRenderer.prependChild(host, newAtom.nativeNode);
758
+ }
759
+ else {
760
+ this.nativeRenderer.insertAfter(newAtom.nativeNode, host);
761
+ }
762
+ }
763
+ context.host = newAtom.nativeNode;
764
+ context.isParent = false;
765
+ const applyRefs = this.updateNativeNodeProperties(newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode);
766
+ if (newAtom.child) {
767
+ this.diff(newAtom.child, oldAtom.child, {
768
+ host: newAtom.nativeNode,
769
+ isParent: true
770
+ }, 0, 0);
828
771
  }
829
- else {
830
- this.nativeRenderer.insertAfter(child, context.host);
772
+ else if (oldAtom.child) {
773
+ let atom = oldAtom.child;
774
+ while (atom) {
775
+ this.cleanView(atom, false);
776
+ atom = atom.sibling;
777
+ }
778
+ }
779
+ applyRefs();
780
+ });
781
+ },
782
+ reuseText: (newAtom, oldAtom) => {
783
+ commits.push(() => {
784
+ const nativeNode = oldAtom.nativeNode;
785
+ if (newAtom.jsxNode.text !== oldAtom.jsxNode.text) {
786
+ this.nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode.text);
831
787
  }
832
- context.host = child;
788
+ newAtom.nativeNode = nativeNode;
789
+ context.host = nativeNode;
833
790
  context.isParent = false;
834
791
  });
835
- });
836
- };
837
- while (start && !start.nativeNode) {
838
- const reusedAtom = this.reuseAndUpdate(start, oldChildren);
839
- if (reusedAtom) {
840
- addReuseCommit(start, reusedAtom);
792
+ },
793
+ create: (start) => {
794
+ commits.push(() => {
795
+ this.buildView(start, context);
796
+ });
841
797
  }
842
- else {
843
- addCreateCommit(start);
798
+ };
799
+ while (newAtom && !newAtom.nativeNode) {
800
+ this.createChanges(newAtom, expectIndex, oldChildren, changeCommits);
801
+ newAtom = newAtom.sibling;
802
+ expectIndex++;
803
+ }
804
+ for (const item of oldChildren) {
805
+ this.cleanView(item.atom, false);
806
+ }
807
+ let j = 0;
808
+ let offset = 0;
809
+ const len = oldChildren.length;
810
+ for (let i = 0; i < commits.length; i++) {
811
+ const commit = commits[i];
812
+ while (j < len) {
813
+ const current = oldChildren[j];
814
+ if (current.index <= i) {
815
+ offset++;
816
+ j++;
817
+ continue;
818
+ }
819
+ break;
844
820
  }
845
- start = start.sibling;
821
+ commit(offset);
846
822
  }
847
- for (const atom of oldChildren) {
848
- this.cleanView(atom, false);
849
- }
850
- for (const commit of commits) {
851
- commit();
823
+ }
824
+ createChanges(newAtom, expectIndex, oldChildren, changeCommits) {
825
+ for (let i = 0; i < oldChildren.length; i++) {
826
+ const { atom: diffAtom, index: diffIndex } = oldChildren[i];
827
+ const key = newAtom.jsxNode.key;
828
+ const diffKey = diffAtom.jsxNode.key;
829
+ if (key !== undefined && diffKey !== undefined) {
830
+ if (diffKey !== key) {
831
+ continue;
832
+ }
833
+ }
834
+ if (newAtom.jsxNode.is(diffAtom.jsxNode)) {
835
+ if (newAtom.jsxNode instanceof JSXElement) {
836
+ changeCommits.reuseElement(newAtom, diffAtom, expectIndex, diffIndex);
837
+ }
838
+ else if (newAtom.jsxNode instanceof JSXText) {
839
+ changeCommits.reuseText(newAtom, diffAtom);
840
+ }
841
+ else {
842
+ changeCommits.reuseComponent(newAtom, diffAtom, expectIndex, diffIndex);
843
+ }
844
+ oldChildren.splice(i, 1);
845
+ return;
846
+ }
852
847
  }
848
+ changeCommits.create(newAtom);
853
849
  }
854
850
  cleanView(atom, isClean) {
855
851
  if (atom.nativeNode) {
@@ -858,10 +854,8 @@ let Renderer = class Renderer {
858
854
  isClean = true;
859
855
  }
860
856
  if (atom.jsxNode instanceof JSXElement) {
861
- const ref = atom.jsxNode.props.attrs.get(refKey);
862
- if (ref instanceof Ref) {
863
- ref.unListen();
864
- }
857
+ const ref = atom.jsxNode.props[refKey];
858
+ this.applyRefs(ref, atom.nativeNode, false);
865
859
  }
866
860
  }
867
861
  let child = atom.child;
@@ -873,167 +867,77 @@ let Renderer = class Renderer {
873
867
  atom.jsxNode.destroy();
874
868
  }
875
869
  }
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
- }
870
+ buildView(atom, context) {
871
+ if (atom.jsxNode instanceof Component) {
872
+ this.componentRender(atom.jsxNode, atom);
873
+ let child = atom.child;
874
+ while (child) {
875
+ this.buildView(child, context);
876
+ child = child.sibling;
918
877
  }
878
+ atom.jsxNode.rendered();
919
879
  }
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
- }
880
+ else {
881
+ let nativeNode;
882
+ let applyRefs = null;
883
+ if (atom.jsxNode instanceof JSXElement) {
884
+ const { nativeNode: n, applyRefs: a } = this.createElement(atom.jsxNode);
885
+ nativeNode = n;
886
+ applyRefs = a;
887
+ }
888
+ else {
889
+ nativeNode = this.createTextNode(atom.jsxNode);
931
890
  }
932
- return [nativeNode];
933
- }
934
- else if (atom.jsxNode instanceof JSXText) {
935
- const nativeNode = this.createTextNode(atom.jsxNode);
936
891
  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();
892
+ if (context.isParent) {
893
+ this.nativeRenderer.prependChild(context.host, nativeNode);
968
894
  }
969
895
  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
- }
896
+ this.nativeRenderer.insertAfter(nativeNode, context.host);
984
897
  }
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;
898
+ if (atom.jsxNode instanceof JSXElement) {
899
+ const childContext = {
900
+ isParent: true,
901
+ host: nativeNode
902
+ };
903
+ let child = atom.child;
904
+ while (child) {
905
+ this.buildView(child, childContext);
906
+ child = child.sibling;
1000
907
  }
1001
- context.pop();
908
+ }
909
+ context.host = nativeNode;
910
+ context.isParent = false;
911
+ if (applyRefs) {
912
+ applyRefs();
1002
913
  }
1003
914
  }
1004
- return children;
1005
915
  }
1006
- componentRender(component, parent) {
916
+ componentRender(component, from) {
1007
917
  const { template, render } = component.init();
1008
918
  if (template) {
1009
- this.linkTemplate(template, component, parent);
919
+ this.linkTemplate(template, component, from);
1010
920
  }
1011
921
  this.componentAtomCaches.set(component, {
1012
922
  render,
1013
- atom: parent
923
+ atom: from
1014
924
  });
1015
- return parent;
925
+ return from;
1016
926
  }
1017
927
  createChainByComponentFactory(context, factory, parent) {
1018
- const component = factory(context);
928
+ const component = factory.createInstance(context);
1019
929
  if (component.setup === Fragment) {
1020
930
  return this.createChainByChildren(component, component.props.children, parent);
1021
931
  }
1022
932
  return new Atom(component, parent);
1023
933
  }
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
934
  createChainByJSXElement(context, element, parent) {
1034
935
  const atom = new Atom(element, parent);
1035
- const children = this.createChainByChildren(context, element.props.children, atom);
1036
- this.link(atom, children);
936
+ if (Reflect.has(element.props, 'children')) {
937
+ const jsxChildren = element.props.children;
938
+ const children = this.createChainByChildren(context, Array.isArray(jsxChildren) ? jsxChildren : [jsxChildren], atom);
939
+ this.link(atom, children);
940
+ }
1037
941
  return atom;
1038
942
  }
1039
943
  createChainByJSXText(node, parent) {
@@ -1042,19 +946,39 @@ let Renderer = class Renderer {
1042
946
  createChainByChildren(context, children, parent) {
1043
947
  const atoms = [];
1044
948
  for (const item of children) {
1045
- const child = this.createChain(context, item, parent);
1046
- if (Array.isArray(child)) {
1047
- atoms.push(...child);
949
+ if (item instanceof JSXElement) {
950
+ atoms.push(this.createChainByJSXElement(context, item, parent));
951
+ continue;
1048
952
  }
1049
- else {
1050
- atoms.push(child);
953
+ if (item instanceof JSXComponent) {
954
+ const childAtom = this.createChainByComponentFactory(context, item, parent);
955
+ if (Array.isArray(childAtom)) {
956
+ atoms.push(...childAtom);
957
+ }
958
+ else {
959
+ atoms.push(childAtom);
960
+ }
961
+ continue;
962
+ }
963
+ if (typeof item === 'string' && item.length) {
964
+ atoms.push(this.createChainByJSXText(new JSXText(item), parent));
965
+ continue;
966
+ }
967
+ if (Array.isArray(item)) {
968
+ atoms.push(...this.createChainByChildren(context, item, parent));
969
+ continue;
970
+ }
971
+ if (item !== null && typeof item !== 'undefined') {
972
+ atoms.push(this.createChainByJSXText(new JSXText(String(item)), parent));
1051
973
  }
1052
974
  }
1053
975
  return atoms;
1054
976
  }
1055
977
  linkTemplate(template, component, parent) {
1056
978
  if (template) {
1057
- const child = this.createChain(component, template, parent);
979
+ const child = template instanceof JSXElement ?
980
+ this.createChainByJSXElement(component, template, parent) :
981
+ this.createChainByComponentFactory(component, template, parent);
1058
982
  this.link(parent, Array.isArray(child) ? child : [child]);
1059
983
  }
1060
984
  }
@@ -1068,56 +992,153 @@ let Renderer = class Renderer {
1068
992
  createElement(vNode) {
1069
993
  const nativeNode = this.nativeRenderer.createElement(vNode.name);
1070
994
  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;
995
+ let bindingRefs;
996
+ const keys = Object.keys(props);
997
+ for (const key of keys) {
998
+ if (key === 'children') {
999
+ continue;
1000
+ }
1001
+ if (key === 'class') {
1002
+ this.nativeRenderer.setClass(nativeNode, classToString(props[key]));
1003
+ continue;
1004
+ }
1005
+ if (key === 'style') {
1006
+ const style = props.style;
1007
+ Object.keys(style).forEach(key => {
1008
+ this.nativeRenderer.setStyle(nativeNode, key, style[key]);
1009
+ });
1010
+ continue;
1011
+ }
1012
+ if (/^on[A-Z]/.test(key)) {
1013
+ const listener = props[key];
1014
+ if (typeof listener === 'function') {
1015
+ this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), listener);
1076
1016
  }
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
- });
1017
+ continue;
1018
+ }
1019
+ if (key === refKey) {
1020
+ bindingRefs = props[key];
1021
+ continue;
1022
+ }
1023
+ this.nativeRenderer.setProperty(nativeNode, key, props[key]);
1086
1024
  }
1087
- return nativeNode;
1025
+ return {
1026
+ nativeNode,
1027
+ applyRefs: () => {
1028
+ this.applyRefs(bindingRefs, nativeNode, true);
1029
+ }
1030
+ };
1088
1031
  }
1089
1032
  createTextNode(child) {
1090
1033
  return this.nativeRenderer.createTextNode(child.text);
1091
1034
  }
1092
1035
  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;
1036
+ const changes = getObjectChanges(newVNode.props, oldVNode.props);
1037
+ let unBindRefs;
1038
+ let bindRefs;
1039
+ for (const [key, value] of changes.remove) {
1040
+ if (key === 'children') {
1041
+ continue;
1042
+ }
1043
+ if (key === 'class') {
1044
+ this.nativeRenderer.setClass(nativeNode, '');
1045
+ continue;
1046
+ }
1047
+ if (key === 'style') {
1048
+ Object.keys(value).forEach(styleName => {
1049
+ this.nativeRenderer.removeStyle(nativeNode, styleName);
1050
+ });
1051
+ continue;
1052
+ }
1053
+ if (/^on[A-Z]/.test(key)) {
1054
+ if (typeof value === 'function') {
1055
+ this.nativeRenderer.unListen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
1056
+ }
1057
+ continue;
1058
+ }
1059
+ if (key === refKey) {
1060
+ unBindRefs = value;
1061
+ continue;
1103
1062
  }
1104
1063
  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;
1064
+ }
1065
+ for (const [key, newValue, oldValue] of changes.replace) {
1066
+ if (key === 'children') {
1067
+ continue;
1068
+ }
1069
+ if (key === 'class') {
1070
+ const oldClassName = classToString(oldValue);
1071
+ const newClassName = classToString(newValue);
1072
+ if (oldClassName !== newClassName) {
1073
+ this.nativeRenderer.setClass(nativeNode, newClassName);
1074
+ }
1075
+ continue;
1076
+ }
1077
+ if (key === 'style') {
1078
+ const styleChanges = getObjectChanges(newValue, oldValue);
1079
+ for (const [styleName] of styleChanges.remove) {
1080
+ this.nativeRenderer.removeStyle(nativeNode, styleName);
1081
+ }
1082
+ for (const [styleName, styleValue] of [...styleChanges.add, ...styleChanges.replace]) {
1083
+ this.nativeRenderer.setStyle(nativeNode, styleName, styleValue);
1084
+ }
1085
+ continue;
1086
+ }
1087
+ if (/^on[A-Z]/.test(key)) {
1088
+ const listenType = key.replace(/^on/, '').toLowerCase();
1089
+ if (typeof oldValue === 'function') {
1090
+ this.nativeRenderer.unListen(nativeNode, listenType, oldValue);
1091
+ }
1092
+ if (typeof newValue === 'function') {
1093
+ this.nativeRenderer.listen(nativeNode, listenType, newValue);
1094
+ }
1095
+ continue;
1096
+ }
1097
+ if (key === refKey) {
1098
+ unBindRefs = oldValue;
1099
+ bindRefs = newValue;
1100
+ continue;
1101
+ }
1102
+ this.nativeRenderer.setProperty(nativeNode, key, newValue);
1103
+ }
1104
+ for (const [key, value] of changes.add) {
1105
+ if (key === 'children') {
1106
+ continue;
1107
+ }
1108
+ if (key === 'class') {
1109
+ this.nativeRenderer.setClass(nativeNode, classToString(value));
1110
+ continue;
1111
+ }
1112
+ if (key === 'style') {
1113
+ Object.keys(value).forEach(styleName => {
1114
+ this.nativeRenderer.setStyle(nativeNode, styleName, value[styleName]);
1115
+ });
1116
+ continue;
1117
+ }
1118
+ if (/^on[A-Z]/.test(key)) {
1119
+ if (typeof value === 'function') {
1120
+ this.nativeRenderer.listen(nativeNode, key.replace(/^on/, '').toLowerCase(), value);
1121
+ }
1122
+ continue;
1123
+ }
1124
+ if (key === refKey) {
1125
+ bindRefs = value;
1126
+ continue;
1110
1127
  }
1111
1128
  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
- });
1129
+ }
1130
+ return () => {
1131
+ this.applyRefs(unBindRefs, nativeNode, false);
1132
+ this.applyRefs(bindRefs, nativeNode, true);
1133
+ };
1134
+ }
1135
+ applyRefs(refs, nativeNode, binding) {
1136
+ refs = Array.isArray(refs) ? refs : [refs];
1137
+ for (const item of refs) {
1138
+ if (item instanceof Ref) {
1139
+ binding ? item.bind(nativeNode) : item.unBind(nativeNode);
1140
+ }
1141
+ }
1121
1142
  }
1122
1143
  };
1123
1144
  Renderer = __decorate([
@@ -1159,7 +1180,7 @@ class Viewfly extends ReflectiveInjector {
1159
1180
  /**
1160
1181
  * 启动 Viewfly
1161
1182
  */
1162
- start() {
1183
+ run() {
1163
1184
  const renderer = this.get(Renderer);
1164
1185
  renderer.render();
1165
1186
  if (this.config.autoUpdate === false) {
@@ -1184,8 +1205,8 @@ class Viewfly extends ReflectiveInjector {
1184
1205
  return () => {
1185
1206
  return this.destroyed ? null : rootNode;
1186
1207
  };
1187
- });
1208
+ }, this.config.context);
1188
1209
  }
1189
1210
  }
1190
1211
 
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 };
1212
+ 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 };