@viewfly/core 1.0.0-alpha.2 → 1.0.0-alpha.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bundles/index.js CHANGED
@@ -163,6 +163,40 @@ exports.InjectFlags = void 0;
163
163
  class Injector {
164
164
  }
165
165
 
166
+ /**
167
+ * 构造函数参数装饰器,用于改变注入 token
168
+ */
169
+ const Inject = function InjectDecorator(token) {
170
+ if (this instanceof Inject) {
171
+ this.token = token;
172
+ }
173
+ else {
174
+ return makeParamDecorator(Inject, new Inject(token));
175
+ }
176
+ };
177
+ const Self = function SelfDecorator() {
178
+ if (!(this instanceof Self)) {
179
+ return makeParamDecorator(Self, new Self());
180
+ }
181
+ };
182
+ const SkipSelf = function SkipSelfDecorator() {
183
+ if (!(this instanceof SkipSelf)) {
184
+ return makeParamDecorator(SkipSelf, new SkipSelf());
185
+ }
186
+ };
187
+ const Optional = function OptionalDecorator() {
188
+ if (!(this instanceof Optional)) {
189
+ return makeParamDecorator(Optional, new Optional());
190
+ }
191
+ };
192
+ const Prop = function PropDecorator(token, notFoundValue, flags) {
193
+ if (!(this instanceof Prop)) {
194
+ return makePropertyDecorator(Prop, token, function (instance, propertyName, token, injector) {
195
+ instance[propertyName] = injector.get(token instanceof ForwardRef ? token.getRef() : token, notFoundValue, flags);
196
+ });
197
+ }
198
+ };
199
+
166
200
  function stringify(token) {
167
201
  if (typeof token === 'string') {
168
202
  return token;
@@ -202,12 +236,12 @@ const THROW_IF_NOT_FOUND = {
202
236
  const nullInjectorErrorFn = (token) => {
203
237
  return makeError('NullInjector')(`No provide for \`${stringify(token)}\`!`);
204
238
  };
205
- class NullInjector extends Injector {
239
+ class NullInjector {
206
240
  constructor() {
207
- super(...arguments);
208
241
  this.parentInjector = null;
209
242
  }
210
- get(token, flag, notFoundValue = THROW_IF_NOT_FOUND) {
243
+ /* eslint-disable-next-line */
244
+ get(token, notFoundValue = THROW_IF_NOT_FOUND, _) {
211
245
  if (notFoundValue === THROW_IF_NOT_FOUND) {
212
246
  throw nullInjectorErrorFn(token);
213
247
  }
@@ -215,40 +249,6 @@ class NullInjector extends Injector {
215
249
  }
216
250
  }
217
251
 
218
- /**
219
- * 构造函数参数装饰器,用于改变注入 token
220
- */
221
- const Inject = function InjectDecorator(token) {
222
- if (this instanceof Inject) {
223
- this.token = token;
224
- }
225
- else {
226
- return makeParamDecorator(Inject, new Inject(token));
227
- }
228
- };
229
- const Self = function SelfDecorator() {
230
- if (!(this instanceof Self)) {
231
- return makeParamDecorator(Self, new Self());
232
- }
233
- };
234
- const SkipSelf = function SkipSelfDecorator() {
235
- if (!(this instanceof SkipSelf)) {
236
- return makeParamDecorator(SkipSelf, new SkipSelf());
237
- }
238
- };
239
- const Optional = function OptionalDecorator() {
240
- if (!(this instanceof Optional)) {
241
- return makeParamDecorator(Optional, new Optional());
242
- }
243
- };
244
- const Prop = function PropDecorator(token, flags, notFoundValue = THROW_IF_NOT_FOUND) {
245
- if (!(this instanceof Prop)) {
246
- return makePropertyDecorator(Prop, token, function (instance, propertyName, token, injector) {
247
- instance[propertyName] = injector.get(token instanceof ForwardRef ? token.getRef() : token, flags, notFoundValue);
248
- });
249
- }
250
- };
251
-
252
252
  /**
253
253
  * 标准化 provide,并返回统一数据结构
254
254
  * @param provider
@@ -412,11 +412,9 @@ const provideScopeError = (token) => {
412
412
  /**
413
413
  * 反射注入器
414
414
  */
415
- class ReflectiveInjector extends Injector {
415
+ class ReflectiveInjector {
416
416
  constructor(parentInjector, staticProviders, scope) {
417
- super();
418
417
  this.parentInjector = parentInjector;
419
- this.staticProviders = staticProviders;
420
418
  this.scope = scope;
421
419
  this.recordValues = new Map();
422
420
  this.normalizedProviders = staticProviders.map(provide => {
@@ -426,15 +424,15 @@ class ReflectiveInjector extends Injector {
426
424
  /**
427
425
  * 用于获取当前注入器上下文内的实例、对象或数据
428
426
  * @param token 访问 token
429
- * @param flags 查询规则
430
427
  * @param notFoundValue 如未查找到的返回值
428
+ * @param flags 查询规则
431
429
  */
432
- get(token, flags = exports.InjectFlags.Default, notFoundValue = THROW_IF_NOT_FOUND) {
430
+ get(token, notFoundValue = THROW_IF_NOT_FOUND, flags) {
433
431
  var _a;
434
432
  flags = flags || exports.InjectFlags.Default;
435
433
  if (flags === exports.InjectFlags.SkipSelf) {
436
434
  if (this.parentInjector) {
437
- return this.parentInjector.get(token, exports.InjectFlags.Default, notFoundValue);
435
+ return this.parentInjector.get(token, notFoundValue);
438
436
  }
439
437
  if (notFoundValue !== THROW_IF_NOT_FOUND) {
440
438
  return notFoundValue;
@@ -478,9 +476,12 @@ class ReflectiveInjector extends Injector {
478
476
  return notFoundValue;
479
477
  }
480
478
  if (this.parentInjector) {
481
- return this.parentInjector.get(token, flags === exports.InjectFlags.Optional ? exports.InjectFlags.Optional : exports.InjectFlags.Default, notFoundValue);
479
+ return this.parentInjector.get(token, notFoundValue, flags === exports.InjectFlags.Optional ? exports.InjectFlags.Optional : exports.InjectFlags.Default);
482
480
  }
483
481
  if (notFoundValue === THROW_IF_NOT_FOUND) {
482
+ // if (flags === InjectFlags.Optional) {
483
+ // return null as U
484
+ // }
484
485
  throw reflectiveInjectorErrorFn(token);
485
486
  }
486
487
  return notFoundValue;
@@ -510,11 +511,11 @@ class ReflectiveInjector extends Injector {
510
511
  const tryValue = {};
511
512
  const injectToken = dep.injectKey instanceof ForwardRef ? dep.injectKey.getRef() : dep.injectKey;
512
513
  if (dep.visibility instanceof Self) {
513
- reflectiveValue = this.get(injectToken, exports.InjectFlags.Self, tryValue);
514
+ reflectiveValue = this.get(injectToken, tryValue, exports.InjectFlags.Self);
514
515
  }
515
516
  else if (dep.visibility instanceof SkipSelf) {
516
517
  if (this.parentInjector) {
517
- reflectiveValue = this.parentInjector.get(injectToken, exports.InjectFlags.Default, tryValue);
518
+ reflectiveValue = this.parentInjector.get(injectToken, tryValue);
518
519
  }
519
520
  else {
520
521
  if (dep.optional) {
@@ -527,7 +528,7 @@ class ReflectiveInjector extends Injector {
527
528
  }
528
529
  }
529
530
  else {
530
- reflectiveValue = this.get(injectToken, exports.InjectFlags.Default, tryValue);
531
+ reflectiveValue = this.get(injectToken, tryValue);
531
532
  }
532
533
  if (reflectiveValue === tryValue) {
533
534
  if (dep.optional) {
@@ -589,13 +590,13 @@ function getArrayChanges(left, right) {
589
590
  return changes;
590
591
  }
591
592
  function classToString(config) {
592
- if (!config) {
593
- return '';
594
- }
595
593
  if (typeof config === 'string') {
596
594
  return config;
597
595
  }
598
- else if (Array.isArray(config)) {
596
+ if (!config) {
597
+ return '';
598
+ }
599
+ if (Array.isArray(config)) {
599
600
  const classes = [];
600
601
  for (const i of config) {
601
602
  const v = classToString(i);
@@ -605,7 +606,7 @@ function classToString(config) {
605
606
  }
606
607
  return classes.join(' ');
607
608
  }
608
- else if (typeof config === 'object') {
609
+ if (typeof config === 'object') {
609
610
  if (config.toString !== Object.prototype.toString && !config.toString.toString().includes('[native code]')) {
610
611
  return config.toString();
611
612
  }
@@ -632,6 +633,9 @@ function styleToObject(style) {
632
633
  });
633
634
  return obj;
634
635
  }
636
+ const TextAtomType = Symbol('Text');
637
+ const ElementAtomType = Symbol('Element');
638
+ const ComponentAtomType = Symbol('Component');
635
639
 
636
640
  const componentSetupStack = [];
637
641
  const signalDepsStack = [];
@@ -738,30 +742,29 @@ class Component extends ReflectiveInjector {
738
742
  portalHost: this.instance.$portalHost
739
743
  };
740
744
  }
741
- update(newProps, forceUpdate = false) {
745
+ update(newProps) {
742
746
  const oldProps = this.props;
743
- const { add, remove, replace } = getObjectChanges(newProps, this.props);
744
- if (add.length || remove.length || replace.length) {
745
- this.invokePropsChangedHooks(newProps);
746
- }
747
- else if (!this.dirty) {
748
- return this.template;
749
- }
750
- const newRefs = toRefs(newProps.ref);
751
- if (this.refs) {
752
- for (const oldRef of this.refs) {
753
- if (!newRefs.includes(oldRef)) {
754
- oldRef.unBind(this.instance);
747
+ if (newProps !== oldProps) {
748
+ const { add, remove, replace } = getObjectChanges(newProps, oldProps);
749
+ if (add.length || remove.length || replace.length) {
750
+ this.invokePropsChangedHooks(newProps);
751
+ }
752
+ const newRefs = toRefs(newProps.ref);
753
+ if (this.refs) {
754
+ for (const oldRef of this.refs) {
755
+ if (!newRefs.includes(oldRef)) {
756
+ oldRef.unBind(this.instance);
757
+ }
755
758
  }
756
759
  }
760
+ for (const newRef of newRefs) {
761
+ newRef.bind(this.instance);
762
+ }
763
+ if (newRefs.length) {
764
+ this.refs = newRefs;
765
+ }
757
766
  }
758
- for (const newRef of newRefs) {
759
- newRef.bind(this.instance);
760
- }
761
- if (newRefs.length) {
762
- this.refs = newRefs;
763
- }
764
- if (!forceUpdate && typeof this.instance.$useMemo === 'function') {
767
+ if (typeof this.instance.$useMemo === 'function') {
765
768
  if (this.instance.$useMemo(newProps, oldProps)) {
766
769
  return this.template;
767
770
  }
@@ -845,7 +848,12 @@ class Component extends ReflectiveInjector {
845
848
  }
846
849
  }
847
850
  if (unmountedCallbacks.length) {
848
- this.unmountedCallbacks = unmountedCallbacks;
851
+ if (this.unmountedCallbacks) {
852
+ this.unmountedCallbacks.push(...unmountedCallbacks);
853
+ }
854
+ else {
855
+ this.unmountedCallbacks = unmountedCallbacks;
856
+ }
849
857
  }
850
858
  this.mountCallbacks = null;
851
859
  }
@@ -1234,9 +1242,9 @@ function withAnnotation(annotation, componentSetup) {
1234
1242
  /**
1235
1243
  * 通过组件上下文获取 IoC 容器内数据的勾子方法
1236
1244
  */
1237
- function inject(token, flags = exports.InjectFlags.Default, notFoundValue = THROW_IF_NOT_FOUND) {
1245
+ function inject(token, notFoundValue = THROW_IF_NOT_FOUND, flags) {
1238
1246
  const component = getSetupContext();
1239
- return component.get(token, flags, notFoundValue);
1247
+ return component.get(token, notFoundValue, flags);
1240
1248
  }
1241
1249
  /**
1242
1250
  * 获取当前组件实例
@@ -1274,14 +1282,17 @@ function withMemo(canUseMemo, render) {
1274
1282
  };
1275
1283
  }
1276
1284
 
1277
- const listenerReg = /^on(?=[A-Z])/;
1285
+ const componentViewCache = new WeakMap();
1286
+ const listenerReg = /^on[A-Z]/;
1278
1287
  function createRenderer(component, nativeRenderer) {
1279
1288
  let isInit = true;
1280
1289
  return function render(host) {
1281
1290
  if (isInit) {
1282
1291
  isInit = false;
1283
1292
  const atom = {
1284
- type: 'component',
1293
+ type: ComponentAtomType,
1294
+ index: 0,
1295
+ nodeType: component.type,
1285
1296
  jsxNode: component,
1286
1297
  sibling: null,
1287
1298
  child: null,
@@ -1301,41 +1312,28 @@ function createRenderer(component, nativeRenderer) {
1301
1312
  }
1302
1313
  function buildView(nativeRenderer, parentComponent, atom, context) {
1303
1314
  const { jsxNode, type } = atom;
1304
- if (type === 'component') {
1315
+ if (type === ComponentAtomType) {
1305
1316
  const component = new Component(parentComponent, jsxNode.type, jsxNode.props, jsxNode.key);
1306
1317
  atom.jsxNode = component;
1307
1318
  componentRender(nativeRenderer, component, atom, context);
1308
1319
  }
1320
+ else if (type === ElementAtomType) {
1321
+ createElement(nativeRenderer, atom, parentComponent, context);
1322
+ }
1309
1323
  else {
1310
- let nativeNode;
1311
- let applyRefs = null;
1312
- if (type === 'element') {
1313
- const { nativeNode: n, applyRefs: a } = createElement(nativeRenderer, jsxNode, atom.isSvg);
1314
- nativeNode = n;
1315
- applyRefs = a;
1316
- }
1317
- else {
1318
- nativeNode = createTextNode(nativeRenderer, jsxNode, atom.isSvg);
1319
- }
1320
- atom.nativeNode = nativeNode;
1321
- insertNode(nativeRenderer, atom, context);
1322
- if (type === 'element') {
1323
- const childContext = {
1324
- isParent: true,
1325
- host: nativeNode,
1326
- rootHost: context.rootHost
1327
- };
1328
- let child = atom.child;
1329
- while (child) {
1330
- buildView(nativeRenderer, parentComponent, child, childContext);
1331
- child = child.sibling;
1332
- }
1333
- }
1334
- context.host = nativeNode;
1335
- context.isParent = false;
1336
- if (applyRefs) {
1337
- applyRefs();
1338
- }
1324
+ createTextNode(nativeRenderer, atom, context);
1325
+ }
1326
+ }
1327
+ function buildElementChildren(atom, nativeRenderer, parentComponent, context) {
1328
+ const childContext = {
1329
+ isParent: true,
1330
+ host: atom.nativeNode,
1331
+ rootHost: context.rootHost
1332
+ };
1333
+ let child = atom.child;
1334
+ while (child) {
1335
+ buildView(nativeRenderer, parentComponent, child, childContext);
1336
+ child = child.sibling;
1339
1337
  }
1340
1338
  }
1341
1339
  function updateView(nativeRenderer, component) {
@@ -1351,115 +1349,77 @@ function updateView(nativeRenderer, component) {
1351
1349
  }
1352
1350
  }
1353
1351
  function applyChanges(nativeRenderer, component) {
1354
- const { atom, host, isParent, rootHost } = component.$$view;
1352
+ const { atom, host, isParent, rootHost } = componentViewCache.get(component);
1355
1353
  const diffAtom = atom.child;
1356
- const template = component.update(component.props, true);
1354
+ const template = component.update(component.props);
1357
1355
  atom.child = createChildChain(template, atom.isSvg);
1358
1356
  const context = {
1359
1357
  host,
1360
1358
  isParent,
1361
1359
  rootHost
1362
1360
  };
1363
- diff(nativeRenderer, component, atom.child, diffAtom, context, 0, 0);
1361
+ diff(nativeRenderer, component, atom.child, diffAtom, context, false);
1364
1362
  const next = atom.sibling;
1365
1363
  if (next && next.jsxNode instanceof Component) {
1366
- next.jsxNode.$$view.host = context.host;
1367
- next.jsxNode.$$view.isParent = context.isParent;
1368
- }
1369
- }
1370
- function diff(nativeRenderer, parentComponent, newAtom, oldAtom, context, expectIndex, index) {
1371
- let prevDiffAtom = null;
1372
- let firstDiffAtomIndexed = null;
1373
- if (oldAtom) {
1374
- prevDiffAtom = {
1375
- index,
1376
- atom: oldAtom,
1377
- prev: null
1378
- };
1379
- index++;
1380
- firstDiffAtomIndexed = prevDiffAtom;
1381
- oldAtom = oldAtom.sibling;
1382
- while (oldAtom) {
1383
- const diffAtom = {
1384
- index,
1385
- atom: oldAtom,
1386
- prev: prevDiffAtom
1387
- };
1388
- prevDiffAtom.next = diffAtom;
1389
- prevDiffAtom = diffAtom;
1390
- oldAtom = oldAtom.sibling;
1391
- index++;
1392
- }
1364
+ const view = componentViewCache.get(next.jsxNode);
1365
+ view.host = context.host;
1366
+ view.isParent = context.isParent;
1393
1367
  }
1368
+ }
1369
+ function diff(nativeRenderer, parentComponent, newAtom, oldAtom, context, needMove) {
1394
1370
  const commits = [];
1395
1371
  function changeOffset() {
1396
1372
  offset++;
1397
1373
  }
1398
1374
  while (newAtom) {
1399
- firstDiffAtomIndexed = createChanges(newAtom, expectIndex, firstDiffAtomIndexed, nativeRenderer, commits, context, parentComponent, changeOffset);
1375
+ oldAtom = createChanges(newAtom, oldAtom, nativeRenderer, commits, context, parentComponent, changeOffset);
1400
1376
  newAtom = newAtom.sibling;
1401
- expectIndex++;
1402
1377
  }
1403
- let dirtyDiffAtom = firstDiffAtomIndexed;
1378
+ let dirtyDiffAtom = oldAtom;
1404
1379
  while (dirtyDiffAtom) {
1405
- cleanView(nativeRenderer, dirtyDiffAtom.atom, false);
1406
- dirtyDiffAtom = dirtyDiffAtom.next;
1380
+ cleanView(nativeRenderer, dirtyDiffAtom, true);
1381
+ dirtyDiffAtom = dirtyDiffAtom.sibling;
1407
1382
  }
1408
1383
  let offset = 0;
1409
1384
  for (let i = 0; i < commits.length; i++) {
1410
1385
  const commit = commits[i];
1411
- while (firstDiffAtomIndexed) {
1412
- if (firstDiffAtomIndexed.index <= i) {
1386
+ while (oldAtom) {
1387
+ if (oldAtom.index <= i) {
1413
1388
  offset--;
1414
- firstDiffAtomIndexed = firstDiffAtomIndexed.next;
1389
+ oldAtom = oldAtom.sibling;
1415
1390
  continue;
1416
1391
  }
1417
1392
  break;
1418
1393
  }
1419
- commit(offset);
1394
+ commit(offset, needMove);
1420
1395
  }
1421
1396
  }
1422
- function createChanges(newAtom, expectIndex, diffAtomIndexed, nativeRenderer, commits, context, parentComponent, effect) {
1423
- const startDiffAtom = diffAtomIndexed;
1424
- const { jsxNode: newJsxNode, type } = newAtom;
1425
- const key = newJsxNode.key;
1426
- while (diffAtomIndexed) {
1427
- const { atom: diffAtom, index: diffIndex } = diffAtomIndexed;
1428
- if (type === diffAtom.type) {
1397
+ function createChanges(newAtom, oldAtom, nativeRenderer, commits, context, parentComponent, effect) {
1398
+ const startDiffAtom = oldAtom;
1399
+ let prev = null;
1400
+ while (oldAtom) {
1401
+ const newAtomType = newAtom.type;
1402
+ if (oldAtom.type === newAtomType && oldAtom.nodeType === newAtom.nodeType && oldAtom.key === newAtom.key) {
1429
1403
  let commit;
1430
- if (type === 'text') {
1431
- commit = updateText(newAtom, diffAtom, nativeRenderer, context);
1404
+ if (newAtomType === TextAtomType) {
1405
+ commit = updateText(newAtom, oldAtom, nativeRenderer, context);
1406
+ }
1407
+ else if (newAtomType === ComponentAtomType) {
1408
+ commit = updateComponent(newAtom, oldAtom, nativeRenderer, context);
1432
1409
  }
1433
1410
  else {
1434
- const { key: diffKey, type: diffType } = diffAtom.jsxNode;
1435
- if (diffKey !== key || newJsxNode.type !== diffType) {
1436
- diffAtomIndexed = diffAtomIndexed.next;
1437
- continue;
1438
- }
1439
- if (type === 'component') {
1440
- commit = updateComponent(newAtom, diffAtom, expectIndex, diffIndex, nativeRenderer, context);
1441
- }
1442
- else {
1443
- commit = updateElement(newAtom, diffAtom, expectIndex, diffIndex, nativeRenderer, context, parentComponent);
1444
- }
1411
+ commit = updateElement(newAtom, oldAtom, nativeRenderer, context, parentComponent);
1445
1412
  }
1446
1413
  commits.push(commit);
1447
- const next = diffAtomIndexed.next;
1448
- const prev = diffAtomIndexed.prev;
1414
+ const next = oldAtom.sibling;
1449
1415
  if (!prev) {
1450
- diffAtomIndexed = next;
1451
- if (diffAtomIndexed) {
1452
- diffAtomIndexed.prev = null;
1453
- }
1454
- return diffAtomIndexed;
1455
- }
1456
- prev.next = next;
1457
- if (next) {
1458
- next.prev = prev;
1416
+ return next;
1459
1417
  }
1418
+ prev.sibling = next;
1460
1419
  return startDiffAtom;
1461
1420
  }
1462
- diffAtomIndexed = diffAtomIndexed.next;
1421
+ prev = oldAtom;
1422
+ oldAtom = oldAtom.sibling;
1463
1423
  }
1464
1424
  commits.push(createNewView(newAtom, nativeRenderer, context, parentComponent, effect));
1465
1425
  return startDiffAtom;
@@ -1471,77 +1431,60 @@ function createNewView(start, nativeRenderer, context, parentComponent, effect)
1471
1431
  };
1472
1432
  }
1473
1433
  function updateText(newAtom, oldAtom, nativeRenderer, context) {
1474
- return function () {
1434
+ return function (offset, needMove) {
1475
1435
  const nativeNode = oldAtom.nativeNode;
1476
- if (newAtom.jsxNode !== oldAtom.jsxNode) {
1477
- nativeRenderer.syncTextContent(nativeNode, newAtom.jsxNode, newAtom.isSvg);
1478
- }
1479
1436
  newAtom.nativeNode = nativeNode;
1437
+ if (needMove || newAtom.index - offset !== oldAtom.index) {
1438
+ insertNode(nativeRenderer, newAtom, context);
1439
+ }
1480
1440
  context.host = nativeNode;
1481
1441
  context.isParent = false;
1482
1442
  };
1483
1443
  }
1484
- function updateElement(newAtom, oldAtom, expectIndex, oldIndex, nativeRenderer, context, parentComponent) {
1485
- return function (offset) {
1444
+ function updateElement(newAtom, oldAtom, nativeRenderer, context, parentComponent) {
1445
+ return function (offset, needMove) {
1486
1446
  newAtom.nativeNode = oldAtom.nativeNode;
1487
- if (expectIndex - offset !== oldIndex) {
1447
+ if (needMove || newAtom.index - offset !== oldAtom.index) {
1488
1448
  insertNode(nativeRenderer, newAtom, context);
1489
1449
  }
1490
1450
  context.host = newAtom.nativeNode;
1491
1451
  context.isParent = false;
1492
- const applyRefs = updateNativeNodeProperties(nativeRenderer, newAtom.jsxNode, oldAtom.jsxNode, newAtom.nativeNode, newAtom.isSvg);
1493
- if (newAtom.child) {
1494
- diff(nativeRenderer, parentComponent, newAtom.child, oldAtom.child, {
1495
- host: newAtom.nativeNode,
1496
- isParent: true,
1497
- rootHost: context.rootHost
1498
- }, 0, 0);
1499
- }
1500
- else if (oldAtom.child) {
1501
- let atom = oldAtom.child;
1502
- nativeRenderer.cleanChildren(oldAtom.nativeNode, oldAtom.isSvg);
1503
- while (atom) {
1504
- cleanView(nativeRenderer, atom, true);
1505
- atom = atom.sibling;
1506
- }
1507
- }
1508
- applyRefs();
1452
+ updateNativeNodeProperties(nativeRenderer, newAtom, oldAtom, parentComponent, context);
1509
1453
  };
1510
1454
  }
1511
- function updateComponent(newAtom, reusedAtom, expectIndex, oldIndex, nativeRenderer, context) {
1512
- return function (offset) {
1455
+ function updateComponent(newAtom, reusedAtom, nativeRenderer, context) {
1456
+ return function (offset, needMove) {
1513
1457
  const component = reusedAtom.jsxNode;
1514
1458
  const newProps = newAtom.jsxNode.props;
1515
1459
  const oldTemplate = component.template;
1516
1460
  const newTemplate = component.update(newProps);
1517
1461
  const portalHost = component.instance.$portalHost;
1518
1462
  context = portalHost ? { isParent: true, host: portalHost, rootHost: portalHost } : context;
1519
- component.$$view = Object.assign({ atom: newAtom }, context);
1463
+ componentViewCache.set(component, Object.assign({ atom: newAtom }, context));
1520
1464
  newAtom.jsxNode = component;
1521
1465
  if (newTemplate === oldTemplate) {
1522
- reuseComponentView(nativeRenderer, newAtom, reusedAtom, context, expectIndex - offset !== oldIndex);
1523
- updateView(nativeRenderer, component);
1466
+ newAtom.child = reusedAtom.child;
1467
+ reuseComponentView(nativeRenderer, newAtom.child, context, needMove || newAtom.index - offset !== reusedAtom.index);
1468
+ component.rendered();
1524
1469
  return;
1525
1470
  }
1526
1471
  if (newTemplate) {
1527
1472
  newAtom.child = createChildChain(newTemplate, newAtom.isSvg);
1528
1473
  }
1529
1474
  if (newAtom.child) {
1530
- diff(nativeRenderer, component, newAtom.child, reusedAtom.child, context, expectIndex, oldIndex + offset);
1475
+ diff(nativeRenderer, component, newAtom.child, reusedAtom.child, context, needMove || newAtom.index - offset !== reusedAtom.index);
1531
1476
  }
1532
1477
  else if (reusedAtom.child) {
1533
1478
  let atom = reusedAtom.child;
1534
1479
  while (atom) {
1535
- cleanView(nativeRenderer, atom, false);
1480
+ cleanView(nativeRenderer, atom, true);
1536
1481
  atom = atom.sibling;
1537
1482
  }
1538
1483
  }
1539
1484
  component.rendered();
1540
1485
  };
1541
1486
  }
1542
- function reuseComponentView(nativeRenderer, newAtom, reusedAtom, context, moveView) {
1543
- let child = reusedAtom.child;
1544
- newAtom.child = child;
1487
+ function reuseComponentView(nativeRenderer, child, context, moveView) {
1545
1488
  const updateContext = (atom) => {
1546
1489
  if (atom.jsxNode instanceof Component) {
1547
1490
  let child = atom.child;
@@ -1563,13 +1506,21 @@ function reuseComponentView(nativeRenderer, newAtom, reusedAtom, context, moveVi
1563
1506
  child = child.sibling;
1564
1507
  }
1565
1508
  }
1509
+ function cleanElementChildren(atom, nativeRenderer) {
1510
+ let child = atom.child;
1511
+ nativeRenderer.cleanChildren(atom.nativeNode, atom.isSvg);
1512
+ while (child) {
1513
+ cleanView(nativeRenderer, child, false);
1514
+ child = child.sibling;
1515
+ }
1516
+ }
1566
1517
  function cleanView(nativeRenderer, atom, needClean) {
1567
1518
  if (atom.nativeNode) {
1568
- if (!needClean) {
1519
+ if (needClean) {
1569
1520
  nativeRenderer.remove(atom.nativeNode, atom.isSvg);
1570
- needClean = true;
1521
+ needClean = false;
1571
1522
  }
1572
- if (atom.type === 'element') {
1523
+ if (atom.type === ElementAtomType) {
1573
1524
  const ref = atom.jsxNode.props[refKey];
1574
1525
  applyRefs(ref, atom.nativeNode, false);
1575
1526
  }
@@ -1577,7 +1528,7 @@ function cleanView(nativeRenderer, atom, needClean) {
1577
1528
  let child = atom.child;
1578
1529
  while (child) {
1579
1530
  if (child.jsxNode instanceof Component && child.jsxNode.instance.$portalHost) {
1580
- needClean = false;
1531
+ needClean = true;
1581
1532
  }
1582
1533
  cleanView(nativeRenderer, child, needClean);
1583
1534
  child = child.sibling;
@@ -1590,7 +1541,7 @@ function componentRender(nativeRenderer, component, from, context) {
1590
1541
  const { template, portalHost } = component.render();
1591
1542
  from.child = createChildChain(template, from.isSvg);
1592
1543
  context = portalHost ? { isParent: true, host: portalHost, rootHost: portalHost } : context;
1593
- component.$$view = Object.assign({ atom: from }, context);
1544
+ componentViewCache.set(component, Object.assign({ atom: from }, context));
1594
1545
  let child = from.child;
1595
1546
  while (child) {
1596
1547
  buildView(nativeRenderer, component, child, context);
@@ -1598,49 +1549,26 @@ function componentRender(nativeRenderer, component, from, context) {
1598
1549
  }
1599
1550
  component.rendered();
1600
1551
  }
1601
- function createChainByJSXComponent(jsxNode, prevAtom, isSvg) {
1602
- const atom = {
1603
- type: 'component',
1604
- jsxNode,
1605
- sibling: null,
1606
- child: null,
1607
- nativeNode: null,
1608
- isSvg
1609
- };
1610
- prevAtom.sibling = atom;
1611
- return atom;
1612
- }
1613
- function createChainByJSXText(jsxNode, prevAtom, isSvg) {
1552
+ function createChainByJSXNode(type, jsxNode, nodeType, prevAtom, isSvg, key) {
1614
1553
  const atom = {
1615
- type: 'text',
1554
+ type,
1555
+ index: prevAtom.index + 1,
1616
1556
  jsxNode,
1617
1557
  sibling: null,
1618
1558
  child: null,
1619
1559
  nativeNode: null,
1620
- isSvg
1560
+ isSvg,
1561
+ nodeType,
1562
+ key
1621
1563
  };
1622
1564
  prevAtom.sibling = atom;
1623
1565
  return atom;
1624
1566
  }
1625
- function createChainByJSXElement(element, prevAtom, isSvg) {
1626
- isSvg = isSvg || element.type === 'svg';
1627
- const atom = {
1628
- type: 'element',
1629
- jsxNode: element,
1630
- sibling: null,
1631
- child: null,
1632
- nativeNode: null,
1633
- isSvg
1634
- };
1635
- prevAtom.sibling = atom;
1636
- atom.child = createChildChain(element.props.children, isSvg);
1637
- return atom;
1638
- }
1639
1567
  function createChainByNode(jsxNode, prevAtom, isSvg) {
1640
1568
  const type = typeof jsxNode;
1641
1569
  if (jsxNode !== null && type !== 'undefined' && type !== 'boolean') {
1642
1570
  if (typeof jsxNode === 'string') {
1643
- return createChainByJSXText(jsxNode, prevAtom, isSvg);
1571
+ return createChainByJSXNode(TextAtomType, jsxNode, jsxNode, prevAtom, isSvg);
1644
1572
  }
1645
1573
  if (Array.isArray(jsxNode)) {
1646
1574
  return createChainByChildren(jsxNode, prevAtom, isSvg);
@@ -1648,13 +1576,14 @@ function createChainByNode(jsxNode, prevAtom, isSvg) {
1648
1576
  if (type === 'object') {
1649
1577
  const nodeType = typeof jsxNode.type;
1650
1578
  if (nodeType === 'string') {
1651
- return createChainByJSXElement(jsxNode, prevAtom, isSvg);
1579
+ return createChainByJSXNode(ElementAtomType, jsxNode, jsxNode.type, prevAtom, isSvg || jsxNode.type === 'svg', jsxNode.key);
1652
1580
  }
1653
1581
  else if (nodeType === 'function') {
1654
- return createChainByJSXComponent(jsxNode, prevAtom, isSvg);
1582
+ return createChainByJSXNode(ComponentAtomType, jsxNode, jsxNode.type, prevAtom, isSvg, jsxNode.key);
1655
1583
  }
1656
1584
  }
1657
- return createChainByJSXText(String(jsxNode), prevAtom, isSvg);
1585
+ const text = String(jsxNode);
1586
+ return createChainByJSXNode(TextAtomType, text, text, prevAtom, isSvg);
1658
1587
  }
1659
1588
  return prevAtom;
1660
1589
  }
@@ -1665,7 +1594,7 @@ function createChainByChildren(children, prevAtom, isSvg) {
1665
1594
  return prevAtom;
1666
1595
  }
1667
1596
  function createChildChain(template, isSvg) {
1668
- const beforeAtom = { sibling: null };
1597
+ const beforeAtom = { sibling: null, index: -1 };
1669
1598
  createChainByNode(template, beforeAtom, isSvg);
1670
1599
  return beforeAtom.sibling;
1671
1600
  }
@@ -1682,12 +1611,14 @@ function insertNode(nativeRenderer, atom, context) {
1682
1611
  nativeRenderer.insertAfter(atom.nativeNode, context.host, atom.isSvg);
1683
1612
  }
1684
1613
  }
1685
- function createElement(nativeRenderer, vNode, isSvg) {
1686
- const nativeNode = nativeRenderer.createElement(vNode.type, isSvg);
1687
- const props = vNode.props;
1614
+ function createElement(nativeRenderer, atom, parentComponent, context) {
1615
+ const { isSvg, jsxNode } = atom;
1616
+ const nativeNode = nativeRenderer.createElement(jsxNode.type, isSvg);
1617
+ const props = jsxNode.props;
1688
1618
  let bindingRefs;
1689
1619
  for (const key in props) {
1690
1620
  if (key === 'children') {
1621
+ atom.child = createChildChain(jsxNode.props.children, isSvg);
1691
1622
  continue;
1692
1623
  }
1693
1624
  if (key === 'class') {
@@ -1707,7 +1638,7 @@ function createElement(nativeRenderer, vNode, isSvg) {
1707
1638
  if (listenerReg.test(key)) {
1708
1639
  const listener = props[key];
1709
1640
  if (typeof listener === 'function') {
1710
- bindEvent(nativeRenderer, vNode, key, nativeNode, listener, isSvg);
1641
+ nativeRenderer.listen(nativeNode, key, listener, isSvg);
1711
1642
  }
1712
1643
  continue;
1713
1644
  }
@@ -1717,21 +1648,32 @@ function createElement(nativeRenderer, vNode, isSvg) {
1717
1648
  }
1718
1649
  nativeRenderer.setProperty(nativeNode, key, props[key], isSvg);
1719
1650
  }
1720
- return {
1721
- nativeNode,
1722
- applyRefs: () => {
1723
- applyRefs(bindingRefs, nativeNode, true);
1724
- }
1725
- };
1726
- }
1727
- function createTextNode(nativeRenderer, text, isSvg) {
1728
- return nativeRenderer.createTextNode(text, isSvg);
1729
- }
1730
- function updateNativeNodeProperties(nativeRenderer, newVNode, oldVNode, nativeNode, isSvg) {
1651
+ atom.nativeNode = nativeNode;
1652
+ insertNode(nativeRenderer, atom, context);
1653
+ buildElementChildren(atom, nativeRenderer, parentComponent, context);
1654
+ context.host = nativeNode;
1655
+ context.isParent = false;
1656
+ applyRefs(bindingRefs, nativeNode, true);
1657
+ }
1658
+ function createTextNode(nativeRenderer, atom, context) {
1659
+ const nativeNode = nativeRenderer.createTextNode(atom.jsxNode, atom.isSvg);
1660
+ atom.nativeNode = nativeNode;
1661
+ insertNode(nativeRenderer, atom, context);
1662
+ context.host = nativeNode;
1663
+ context.isParent = false;
1664
+ }
1665
+ function updateNativeNodeProperties(nativeRenderer, newAtom, oldAtom, parentComponent, context) {
1666
+ const newVNode = newAtom.jsxNode;
1667
+ const isSvg = newAtom.isSvg;
1668
+ const nativeNode = newAtom.nativeNode;
1669
+ const oldVNode = oldAtom.jsxNode;
1670
+ if (newVNode === oldVNode) {
1671
+ updateElementChildren(newAtom, oldAtom, nativeRenderer, parentComponent, context, isSvg);
1672
+ return;
1673
+ }
1731
1674
  const changes = getObjectChanges(newVNode.props, oldVNode.props);
1732
1675
  let unBindRefs;
1733
1676
  let bindRefs;
1734
- newVNode.on = oldVNode.on;
1735
1677
  for (const [key, value] of changes.remove) {
1736
1678
  if (key === 'children') {
1737
1679
  continue;
@@ -1748,10 +1690,7 @@ function updateNativeNodeProperties(nativeRenderer, newVNode, oldVNode, nativeNo
1748
1690
  }
1749
1691
  if (listenerReg.test(key)) {
1750
1692
  if (typeof value === 'function') {
1751
- const type = key.replace(listenerReg, '').toLowerCase();
1752
- const oldOn = oldVNode.on;
1753
- nativeRenderer.unListen(nativeNode, type, oldOn[type].delegate, isSvg);
1754
- Reflect.deleteProperty(oldOn, type);
1693
+ nativeRenderer.unListen(nativeNode, key, value, isSvg);
1755
1694
  }
1756
1695
  continue;
1757
1696
  }
@@ -1784,8 +1723,8 @@ function updateNativeNodeProperties(nativeRenderer, newVNode, oldVNode, nativeNo
1784
1723
  continue;
1785
1724
  }
1786
1725
  if (listenerReg.test(key)) {
1787
- const listenType = key.replace(listenerReg, '').toLowerCase();
1788
- newVNode.on[listenType].listenFn = newValue;
1726
+ nativeRenderer.unListen(nativeNode, key, oldValue, isSvg);
1727
+ nativeRenderer.listen(nativeNode, key, newValue, isSvg);
1789
1728
  continue;
1790
1729
  }
1791
1730
  if (key === refKey) {
@@ -1812,7 +1751,7 @@ function updateNativeNodeProperties(nativeRenderer, newVNode, oldVNode, nativeNo
1812
1751
  }
1813
1752
  if (listenerReg.test(key)) {
1814
1753
  if (typeof value === 'function') {
1815
- bindEvent(nativeRenderer, newVNode, key, nativeNode, value, isSvg);
1754
+ nativeRenderer.listen(nativeNode, key, value, isSvg);
1816
1755
  }
1817
1756
  continue;
1818
1757
  }
@@ -1822,10 +1761,36 @@ function updateNativeNodeProperties(nativeRenderer, newVNode, oldVNode, nativeNo
1822
1761
  }
1823
1762
  nativeRenderer.setProperty(nativeNode, key, value, isSvg);
1824
1763
  }
1825
- return () => {
1826
- applyRefs(unBindRefs, nativeNode, false);
1827
- applyRefs(bindRefs, nativeNode, true);
1828
- };
1764
+ updateElementChildren(newAtom, oldAtom, nativeRenderer, parentComponent, context, isSvg);
1765
+ applyRefs(unBindRefs, nativeNode, false);
1766
+ applyRefs(bindRefs, nativeNode, true);
1767
+ }
1768
+ function updateElementChildren(newAtom, oldAtom, nativeRenderer, parentComponent, context, isSvg) {
1769
+ /**
1770
+ * 不能仅依赖 children 是否相等的判断来确定是否要继续向下 diff
1771
+ * 如:
1772
+ * ```tsx
1773
+ * <Comp>
1774
+ * <div>
1775
+ * {props.children}
1776
+ * </div>
1777
+ * </Comp>
1778
+ * ```
1779
+ * 其中当 Comp 产生变化时,children 来自父组件,这时 children 是相等的,
1780
+ * 但,children 内可能有子组件也发生了变化,如果不继续 diff,那么,子组件
1781
+ * 的视图更新将不会发生
1782
+ */
1783
+ newAtom.child = createChildChain(newAtom.jsxNode.props.children, isSvg);
1784
+ if (!newAtom.child) {
1785
+ cleanElementChildren(oldAtom, nativeRenderer);
1786
+ }
1787
+ else {
1788
+ diff(nativeRenderer, parentComponent, newAtom.child, oldAtom.child, {
1789
+ host: newAtom.nativeNode,
1790
+ isParent: true,
1791
+ rootHost: context.rootHost
1792
+ }, false);
1793
+ }
1829
1794
  }
1830
1795
  function applyRefs(refs, nativeNode, binding) {
1831
1796
  if (refs) {
@@ -1837,21 +1802,6 @@ function applyRefs(refs, nativeNode, binding) {
1837
1802
  }
1838
1803
  }
1839
1804
  }
1840
- function bindEvent(nativeRenderer, vNode, key, nativeNode, listenFn, isSvg) {
1841
- let on = vNode.on;
1842
- if (!on) {
1843
- vNode.on = on = {};
1844
- }
1845
- const type = key.replace(listenerReg, '').toLowerCase();
1846
- const delegateObj = {
1847
- delegate(...args) {
1848
- return delegateObj.listenFn.apply(this, args);
1849
- },
1850
- listenFn
1851
- };
1852
- on[type] = delegateObj;
1853
- nativeRenderer.listen(nativeNode, type, delegateObj.delegate, isSvg);
1854
- }
1855
1805
 
1856
1806
  /**
1857
1807
  * Viewfly 根组件,用于实现组件状态更新事件通知