@tarojs/runtime 3.5.0-canary.0 → 3.5.0-canary.1

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.
@@ -1,4 +1,4 @@
1
- import { isFunction, isUndefined, isObject, warn, isArray, toCamelCase, ensure, toDashed, isString, EMPTY_OBJ, internalComponents, controlledComponent, defaultReconciler, noop } from '@tarojs/shared';
1
+ import { isFunction, isUndefined, isObject, warn, isArray, toCamelCase, noop, ensure, toDashed, isString, EMPTY_OBJ, internalComponents, controlledComponent, defaultReconciler } from '@tarojs/shared';
2
2
  import { injectable, inject, ContainerModule, optional, multiInject, Container } from 'inversify';
3
3
 
4
4
  /*! *****************************************************************************
@@ -70,10 +70,9 @@ if (process.env.TARO_ENV === 'h5') {
70
70
  };
71
71
  // Load global or shim versions of Map, Set, and WeakMap
72
72
  var functionPrototype = Object.getPrototypeOf(Function);
73
- var usePolyfill = isObject(process) && process.env && process.env["REFLECT_METADATA_USE_MAP_POLYFILL"] === "true";
74
73
  var _Map = Map;
75
74
  var _Set = Set;
76
- var _WeakMap = !usePolyfill && isFunction(WeakMap) ? WeakMap : CreateWeakMapPolyfill();
75
+ var _WeakMap = isFunction(WeakMap) ? WeakMap : CreateWeakMapPolyfill();
77
76
  // [[Metadata]] internal slot
78
77
  // https://rbuckton.github.io/reflect-metadata/#ordinary-object-internal-methods-and-internal-slots
79
78
  var Metadata = new _WeakMap();
@@ -1260,7 +1259,8 @@ function shortcutAttr(key) {
1260
1259
  default:
1261
1260
  return key;
1262
1261
  }
1263
- }
1262
+ }
1263
+ const customWrapperCache = new Map();
1264
1264
 
1265
1265
  const SID_TARO_ELEMENT = '0';
1266
1266
  const SID_TARO_ELEMENT_FACTORY = '1';
@@ -1358,6 +1358,7 @@ let TaroEventTarget = class TaroEventTarget {
1358
1358
  }
1359
1359
  addEventListener(type, handler, options) {
1360
1360
  var _a, _b;
1361
+ type = type.toLowerCase();
1361
1362
  (_b = (_a = this.hooks).onAddEvent) === null || _b === void 0 ? void 0 : _b.call(_a, type, handler, options, this);
1362
1363
  if (type === 'regionchange') {
1363
1364
  // map 组件的 regionchange 事件非常特殊,详情:https://github.com/NervJS/taro/issues/5766
@@ -1365,8 +1366,6 @@ let TaroEventTarget = class TaroEventTarget {
1365
1366
  this.addEventListener('end', handler, options);
1366
1367
  return;
1367
1368
  }
1368
- type = type.toLowerCase();
1369
- const handlers = this.__handlers[type];
1370
1369
  let isCapture = Boolean(options);
1371
1370
  let isOnce = false;
1372
1371
  if (isObject(options)) {
@@ -1382,6 +1381,16 @@ let TaroEventTarget = class TaroEventTarget {
1382
1381
  return;
1383
1382
  }
1384
1383
  process.env.NODE_ENV !== 'production' && warn(isCapture, 'Taro 暂未实现 event 的 capture 特性。');
1384
+ // 某些框架,如 PReact 有委托的机制,handler 始终是同一个函数
1385
+ // 这会导致多层停止冒泡失败:view -> view(handler.stop = false) -> view(handler.stop = true)
1386
+ // 这样解决:view -> view(handlerA.stop = false) -> view(handlerB.stop = false)
1387
+ // 因此每次绑定事件都新建一个函数,如果带来了性能问题,可以把这段逻辑抽取到 PReact 插件中。
1388
+ const oldHandler = handler;
1389
+ handler = function () {
1390
+ oldHandler.apply(this, arguments); // this 指向 Element
1391
+ };
1392
+ handler.oldHandler = oldHandler;
1393
+ const handlers = this.__handlers[type];
1385
1394
  if (isArray(handlers)) {
1386
1395
  handlers.push(handler);
1387
1396
  }
@@ -1398,7 +1407,10 @@ let TaroEventTarget = class TaroEventTarget {
1398
1407
  if (!isArray(handlers)) {
1399
1408
  return;
1400
1409
  }
1401
- const index = handlers.indexOf(handler);
1410
+ const index = handlers.findIndex(item => {
1411
+ if (item === handler || item.oldHandler === handler)
1412
+ return true;
1413
+ });
1402
1414
  process.env.NODE_ENV !== 'production' && warn(index === -1, `事件: '${type}' 没有注册在 DOM 中,因此不会被移除。`);
1403
1415
  handlers.splice(index, 1);
1404
1416
  }
@@ -1430,10 +1442,13 @@ function hydrate(node) {
1430
1442
  }
1431
1443
  const data = {
1432
1444
  ["nn" /* NodeName */]: nodeName,
1433
- uid: node.uid
1445
+ sid: node.sid
1434
1446
  };
1435
1447
  const { props } = node;
1436
1448
  const SPECIAL_NODES = node.hooks.getSpecialNodes();
1449
+ if (node.uid !== node.sid) {
1450
+ data.uid = node.uid;
1451
+ }
1437
1452
  if (!node.isAnyEventBinded() && SPECIAL_NODES.indexOf(nodeName) > -1) {
1438
1453
  data["nn" /* NodeName */] = `static-${nodeName}`;
1439
1454
  if (nodeName === VIEW && !isHasExtractProp(node)) {
@@ -1472,8 +1487,158 @@ function hydrate(node) {
1472
1487
  return data;
1473
1488
  }
1474
1489
 
1475
- const eventSource = new Map();
1490
+ class EventSource extends Map {
1491
+ removeNode(child) {
1492
+ const { sid, uid } = child;
1493
+ this.delete(sid);
1494
+ if (uid !== sid && uid)
1495
+ this.delete(uid);
1496
+ }
1497
+ removeNodeTree(child) {
1498
+ this.removeNode(child);
1499
+ const { childNodes } = child;
1500
+ childNodes.forEach(node => this.removeNodeTree(node));
1501
+ }
1502
+ }
1503
+ const eventSource = new EventSource();
1476
1504
 
1505
+ const observers = [];
1506
+ /**
1507
+ * The MutationObserver provides the ability
1508
+ * to watch for changes being made to the DOM tree.
1509
+ * It will invoke a specified callback function
1510
+ * when DOM changes occur.
1511
+ * @see https://dom.spec.whatwg.org/#mutationobserver
1512
+ * @see https://developer.mozilla.org/en-US/docs/Web/API/MutationObserver
1513
+ */
1514
+ class MutationObserverImpl {
1515
+ constructor(callback) {
1516
+ this.records = [];
1517
+ this.callback = callback;
1518
+ }
1519
+ /**
1520
+ * Configures the MutationObserver
1521
+ * to begin receiving notifications
1522
+ * through its callback function
1523
+ * when DOM changes matching the given options occur.
1524
+ *
1525
+ * Options matching is to be implemented.
1526
+ */
1527
+ observe(target, options) {
1528
+ this.disconnect();
1529
+ this.target = target;
1530
+ this.options = options || {};
1531
+ observers.push(this);
1532
+ }
1533
+ /**
1534
+ * Stop the MutationObserver instance
1535
+ * from receiving further notifications
1536
+ * until and unless observe() is called again.
1537
+ */
1538
+ disconnect() {
1539
+ this.target = null;
1540
+ const index = observers.indexOf(this);
1541
+ if (index >= 0) {
1542
+ observers.splice(index, 1);
1543
+ }
1544
+ }
1545
+ /**
1546
+ * Removes all pending notifications
1547
+ * from the MutationObserver's notification queue
1548
+ * and returns them in a new Array of MutationRecord objects.
1549
+ */
1550
+ takeRecords() {
1551
+ return this.records.splice(0, this.records.length);
1552
+ }
1553
+ }
1554
+ /** Match two TaroNodes by sid. */
1555
+ const sidMatches = (observerTarget, target) => {
1556
+ return !!observerTarget && observerTarget.sid === (target === null || target === void 0 ? void 0 : target.sid);
1557
+ };
1558
+ const isConcerned = (record, options) => {
1559
+ const { characterData, characterDataOldValue, attributes, attributeOldValue, childList } = options;
1560
+ switch (record.type) {
1561
+ case "characterData" /* CHARACTER_DATA */:
1562
+ if (characterData) {
1563
+ if (!characterDataOldValue)
1564
+ record.oldValue = null;
1565
+ return true;
1566
+ }
1567
+ return false;
1568
+ case "attributes" /* ATTRIBUTES */:
1569
+ if (attributes) {
1570
+ if (!attributeOldValue)
1571
+ record.oldValue = null;
1572
+ return true;
1573
+ }
1574
+ return false;
1575
+ case "childList" /* CHILD_LIST */:
1576
+ if (childList) {
1577
+ return true;
1578
+ }
1579
+ return false;
1580
+ }
1581
+ };
1582
+ let pendingMuatations = false;
1583
+ function logMutation(observer, record) {
1584
+ observer.records.push(record);
1585
+ if (!pendingMuatations) {
1586
+ pendingMuatations = true;
1587
+ Promise
1588
+ .resolve()
1589
+ .then(() => {
1590
+ pendingMuatations = false;
1591
+ observers.forEach(observer => {
1592
+ return observer.callback(observer.takeRecords());
1593
+ });
1594
+ });
1595
+ }
1596
+ }
1597
+ function recordMutation(record) {
1598
+ observers.forEach(observer => {
1599
+ const { options } = observer;
1600
+ for (let t = record.target; t; t = t.parentNode) {
1601
+ if (sidMatches(observer.target, t) && isConcerned(record, options)) {
1602
+ logMutation(observer, record);
1603
+ break;
1604
+ }
1605
+ if (!options.subtree)
1606
+ break;
1607
+ }
1608
+ });
1609
+ }
1610
+
1611
+ class MutationObserver {
1612
+ constructor(callback) {
1613
+ if (ENABLE_MUTATION_OBSERVER) {
1614
+ this.core = new MutationObserverImpl(callback);
1615
+ }
1616
+ else {
1617
+ if (process.env.NODE_ENV !== 'production') {
1618
+ console.warn('[Taro Warning] 若要使用 MutationObserver,请在 Taro 编译配置中设置 \'mini.enableMutationObserver: true\'');
1619
+ }
1620
+ this.core = {
1621
+ observe: noop,
1622
+ disconnect: noop,
1623
+ takeRecords: noop
1624
+ };
1625
+ }
1626
+ }
1627
+ observe(...args) {
1628
+ this.core.observe(...args);
1629
+ }
1630
+ disconnect() {
1631
+ this.core.disconnect();
1632
+ }
1633
+ takeRecords() {
1634
+ return this.core.takeRecords();
1635
+ }
1636
+ static record(record) {
1637
+ recordMutation(record);
1638
+ }
1639
+ }
1640
+
1641
+ const CHILDNODES = "cn" /* Childnodes */;
1477
1642
  const nodeId = incrementId();
1478
1643
  let TaroNode = class TaroNode extends TaroEventTarget {
1479
1644
  constructor() {
@@ -1484,20 +1649,34 @@ let TaroNode = class TaroNode extends TaroEventTarget {
1484
1649
  this.hydrate = (node) => () => hydrate(node);
1485
1650
  const impl = getNodeImpl();
1486
1651
  impl.bind(this);
1487
- this.uid = `_n_${nodeId()}`;
1488
- eventSource.set(this.uid, this);
1652
+ this.uid = `_n_${nodeId()}`; // dom 节点 id,开发者可修改
1653
+ this.sid = this.uid; // dom 节点全局唯一 id,不可被修改
1654
+ eventSource.set(this.sid, this);
1489
1655
  }
1490
1656
  /**
1491
1657
  * like jQuery's $.empty()
1492
1658
  */
1493
1659
  _empty() {
1494
- while (this.childNodes.length > 0) {
1495
- const child = this.childNodes[0];
1660
+ while (this.firstChild) {
1661
+ // Data Structure
1662
+ const child = this.firstChild;
1496
1663
  child.parentNode = null;
1497
- eventSource.delete(child.uid);
1498
1664
  this.childNodes.shift();
1665
+ // eventSource
1666
+ eventSource.removeNodeTree(child);
1499
1667
  }
1500
1668
  }
1669
+ updateChildNodes(isClean) {
1670
+ const cleanChildNodes = () => [];
1671
+ const rerenderChildNodes = () => {
1672
+ const childNodes = this.childNodes.filter(node => !isComment(node));
1673
+ return childNodes.map(hydrate);
1674
+ };
1675
+ this.enqueueUpdate({
1676
+ path: `${this._path}.${CHILDNODES}`,
1677
+ value: isClean ? cleanChildNodes : rerenderChildNodes
1678
+ });
1679
+ }
1501
1680
  get _root() {
1502
1681
  var _a;
1503
1682
  return ((_a = this.parentNode) === null || _a === void 0 ? void 0 : _a._root) || null;
@@ -1514,7 +1693,7 @@ let TaroNode = class TaroNode extends TaroEventTarget {
1514
1693
  const list = parentNode.childNodes.filter(node => !isComment(node));
1515
1694
  const indexOfNode = list.indexOf(this);
1516
1695
  const index = this.hooks.getPathIndex(indexOfNode);
1517
- return `${parentNode._path}.${"cn" /* Childnodes */}.${index}`;
1696
+ return `${parentNode._path}.${CHILDNODES}.${index}`;
1518
1697
  }
1519
1698
  return '';
1520
1699
  }
@@ -1545,18 +1724,32 @@ let TaroNode = class TaroNode extends TaroEventTarget {
1545
1724
  * @TODO 等待完整 innerHTML 实现
1546
1725
  */
1547
1726
  set textContent(text) {
1727
+ const document = this._getElement(ElementNames.Document)();
1728
+ const newText = document.createTextNode(text);
1729
+ // @Todo: appendChild 会多触发一次
1730
+ MutationObserver.record({
1731
+ type: "childList" /* CHILD_LIST */,
1732
+ target: this,
1733
+ removedNodes: this.childNodes.slice(),
1734
+ addedNodes: text === '' ? [] : [newText]
1735
+ });
1548
1736
  this._empty();
1549
1737
  if (text === '') {
1550
- this.enqueueUpdate({
1551
- path: `${this._path}.${"cn" /* Childnodes */}`,
1552
- value: () => []
1553
- });
1738
+ this.updateChildNodes(true);
1554
1739
  }
1555
1740
  else {
1556
- const document = this._getElement(ElementNames.Document)();
1557
- this.appendChild(document.createTextNode(text));
1741
+ this.appendChild(newText);
1742
+ this.updateChildNodes();
1558
1743
  }
1559
1744
  }
1745
+ /**
1746
+ * @doc https://developer.mozilla.org/zh-CN/docs/Web/API/Node/insertBefore
1747
+ * @scenario
1748
+ * [A,B,C]
1749
+ * 1. insert D before C, D has no parent
1750
+ * 2. insert D before C, D has the same parent of C
1751
+ * 3. insert D before C, D has the different parent of C
1752
+ */
1560
1753
  insertBefore(newChild, refChild, isReplace) {
1561
1754
  if (newChild.nodeName === DOCUMENT_FRAGMENT) {
1562
1755
  newChild.childNodes.reduceRight((previousValue, currentValue) => {
@@ -1565,72 +1758,114 @@ let TaroNode = class TaroNode extends TaroEventTarget {
1565
1758
  }, refChild);
1566
1759
  return newChild;
1567
1760
  }
1568
- newChild.remove();
1761
+ // Parent release newChild
1762
+ // - cleanRef: false (No need to clean eventSource, because newChild is about to be inserted)
1763
+ // - update: true (Need to update parent.childNodes, because parent.childNodes is reordered)
1764
+ newChild.remove({ cleanRef: false });
1765
+ // Data structure
1569
1766
  newChild.parentNode = this;
1570
- let payload;
1571
1767
  if (refChild) {
1768
+ // insertBefore & replaceChild
1572
1769
  const index = this.findIndex(refChild);
1573
1770
  this.childNodes.splice(index, 0, newChild);
1574
- if (isReplace) {
1575
- payload = {
1576
- path: newChild._path,
1577
- value: this.hydrate(newChild)
1578
- };
1579
- }
1580
- else {
1581
- payload = {
1582
- path: `${this._path}.${"cn" /* Childnodes */}`,
1583
- value: () => {
1584
- const childNodes = this.childNodes.filter(node => !isComment(node));
1585
- return childNodes.map(hydrate);
1586
- }
1587
- };
1588
- }
1589
1771
  }
1590
1772
  else {
1773
+ // appendChild
1591
1774
  this.childNodes.push(newChild);
1592
- payload = {
1775
+ }
1776
+ // Serialization
1777
+ if (!refChild || isReplace) {
1778
+ // appendChild & replaceChild
1779
+ this.enqueueUpdate({
1593
1780
  path: newChild._path,
1594
1781
  value: this.hydrate(newChild)
1595
- };
1782
+ });
1596
1783
  }
1597
- this.enqueueUpdate(payload);
1598
- if (!eventSource.has(newChild.uid)) {
1599
- eventSource.set(newChild.uid, newChild);
1784
+ else {
1785
+ // insertBefore
1786
+ this.updateChildNodes();
1600
1787
  }
1788
+ MutationObserver.record({
1789
+ type: "childList" /* CHILD_LIST */,
1790
+ target: this,
1791
+ addedNodes: [newChild],
1792
+ removedNodes: isReplace
1793
+ ? [refChild] /** replaceChild */
1794
+ : [],
1795
+ nextSibling: isReplace
1796
+ ? refChild.nextSibling /** replaceChild */
1797
+ : (refChild || null),
1798
+ previousSibling: newChild.previousSibling
1799
+ });
1601
1800
  return newChild;
1602
1801
  }
1603
- appendChild(child) {
1604
- this.insertBefore(child);
1802
+ /**
1803
+ * @doc https://developer.mozilla.org/zh-CN/docs/Web/API/Node/appendChild
1804
+ * @scenario
1805
+ * [A,B,C]
1806
+ * 1. append C, C has no parent
1807
+ * 2. append C, C has the same parent of B
1808
+ * 3. append C, C has the different parent of B
1809
+ */
1810
+ appendChild(newChild) {
1811
+ return this.insertBefore(newChild);
1605
1812
  }
1813
+ /**
1814
+ * @doc https://developer.mozilla.org/zh-CN/docs/Web/API/Node/replaceChild
1815
+ * @scenario
1816
+ * [A,B,C]
1817
+ * 1. replace B with C, C has no parent
1818
+ * 2. replace B with C, C has no parent, C has the same parent of B
1819
+ * 3. replace B with C, C has no parent, C has the different parent of B
1820
+ */
1606
1821
  replaceChild(newChild, oldChild) {
1607
- if (oldChild.parentNode === this) {
1608
- this.insertBefore(newChild, oldChild, true);
1609
- oldChild.remove(true);
1610
- return oldChild;
1611
- }
1822
+ if (oldChild.parentNode !== this)
1823
+ return;
1824
+ // Insert the newChild
1825
+ this.insertBefore(newChild, oldChild, true);
1826
+ // Destroy the oldChild
1827
+ // - cleanRef: true (Need to clean eventSource, because the oldChild was detached from the DOM tree)
1828
+ // - update: false (No need to update parent.childNodes, because replace will not cause the parent.childNodes being reordered)
1829
+ oldChild.remove({ doUpdate: false });
1830
+ return oldChild;
1612
1831
  }
1613
- removeChild(child, isReplace) {
1614
- const index = this.findIndex(child);
1615
- this.childNodes.splice(index, 1);
1616
- if (!isReplace) {
1617
- this.enqueueUpdate({
1618
- path: `${this._path}.${"cn" /* Childnodes */}`,
1619
- value: () => {
1620
- const childNodes = this.childNodes.filter(node => !isComment(node));
1621
- return childNodes.map(hydrate);
1622
- }
1832
+ /**
1833
+ * @doc https://developer.mozilla.org/zh-CN/docs/Web/API/Node/removeChild
1834
+ * @scenario
1835
+ * [A,B,C]
1836
+ * 1. remove A or B
1837
+ * 2. remove C
1838
+ */
1839
+ removeChild(child, options = {}) {
1840
+ const { cleanRef, doUpdate } = options;
1841
+ if (cleanRef !== false && doUpdate !== false) {
1842
+ // appendChild/replaceChild/insertBefore 不应该触发
1843
+ // @Todo: 但其实如果 newChild 的父节点是另一颗子树的节点,应该是要触发的
1844
+ MutationObserver.record({
1845
+ type: "childList" /* CHILD_LIST */,
1846
+ target: this,
1847
+ removedNodes: [child],
1848
+ nextSibling: child.nextSibling,
1849
+ previousSibling: child.previousSibling
1623
1850
  });
1624
1851
  }
1852
+ // Data Structure
1853
+ const index = this.findIndex(child);
1854
+ this.childNodes.splice(index, 1);
1625
1855
  child.parentNode = null;
1626
- eventSource.delete(child.uid);
1627
- // @TODO: eventSource memory overflow
1628
- // child._empty()
1856
+ // Set eventSource
1857
+ if (cleanRef !== false) {
1858
+ eventSource.removeNodeTree(child);
1859
+ }
1860
+ // Serialization
1861
+ if (doUpdate !== false) {
1862
+ this.updateChildNodes();
1863
+ }
1629
1864
  return child;
1630
1865
  }
1631
- remove(isReplace) {
1866
+ remove(options) {
1632
1867
  var _a;
1633
- (_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this, isReplace);
1868
+ (_a = this.parentNode) === null || _a === void 0 ? void 0 : _a.removeChild(this, options);
1634
1869
  }
1635
1870
  hasChildNodes() {
1636
1871
  return this.childNodes.length > 0;
@@ -1656,6 +1891,11 @@ let TaroText = class TaroText extends TaroNode {
1656
1891
  this.nodeName = '#text';
1657
1892
  }
1658
1893
  set textContent(text) {
1894
+ MutationObserver.record({
1895
+ target: this,
1896
+ type: "characterData" /* CHARACTER_DATA */,
1897
+ oldValue: this._value
1898
+ });
1659
1899
  this._value = text;
1660
1900
  this.enqueueUpdate({
1661
1901
  path: `${this._path}.${"v" /* Text */}`,
@@ -1869,6 +2109,7 @@ combine('box', ['DecorationBreak', 'Shadow', 'Sizing', 'Snap'], true);
1869
2109
 
1870
2110
  function setStyle(newVal, styleKey) {
1871
2111
  const old = this[styleKey];
2112
+ const oldCssTxt = this.cssText;
1872
2113
  if (newVal) {
1873
2114
  this._usedStyleProp.add(styleKey);
1874
2115
  }
@@ -1879,6 +2120,16 @@ function setStyle(newVal, styleKey) {
1879
2120
  path: `${this._element._path}.${"st" /* Style */}`,
1880
2121
  value: this.cssText
1881
2122
  });
2123
+ // @Todo:
2124
+ // el.style.cssText = 'x: y;m: n'(Bug: 触发两次)
2125
+ // el.style.cssText = 'x: y'(正常)
2126
+ // el.style.x = y(正常)
2127
+ MutationObserver.record({
2128
+ type: "attributes" /* ATTRIBUTES */,
2129
+ target: this._element,
2130
+ attributeName: 'style',
2131
+ oldValue: oldCssTxt
2132
+ });
1882
2133
  }
1883
2134
  }
1884
2135
  function initStyle(ctor) {
@@ -1918,15 +2169,15 @@ class Style {
1918
2169
  });
1919
2170
  }
1920
2171
  get cssText() {
1921
- let text = '';
2172
+ const texts = [];
1922
2173
  this._usedStyleProp.forEach(key => {
1923
2174
  const val = this[key];
1924
2175
  if (!val)
1925
2176
  return;
1926
2177
  const styleName = isCssVariable(key) ? key : toDashed(key);
1927
- text += `${styleName}: ${val};`;
2178
+ texts.push(`${styleName}: ${val};`);
1928
2179
  });
1929
- return text;
2180
+ return texts.join(' ');
1930
2181
  }
1931
2182
  set cssText(str) {
1932
2183
  if (str == null) {
@@ -2033,7 +2284,7 @@ class ClassList extends Set {
2033
2284
  this.el = el;
2034
2285
  }
2035
2286
  get value() {
2036
- return [...this].join(' ');
2287
+ return [...this].filter(v => v !== '').join(' ');
2037
2288
  }
2038
2289
  add(s) {
2039
2290
  super.add(s);
@@ -2160,12 +2411,24 @@ let TaroElement = class TaroElement extends TaroNode {
2160
2411
  var _a, _b;
2161
2412
  process.env.NODE_ENV !== 'production' && warn(isString(value) && value.length > PROPERTY_THRESHOLD, `元素 ${this.nodeName} 的 属性 ${qualifiedName} 的值数据量过大,可能会影响渲染性能。考虑降低图片转为 base64 的阈值或在 CSS 中使用 base64。`);
2162
2413
  const isPureView = this.nodeName === VIEW && !isHasExtractProp(this) && !this.isAnyEventBinded();
2414
+ if (qualifiedName !== STYLE) {
2415
+ MutationObserver.record({
2416
+ target: this,
2417
+ type: "attributes" /* ATTRIBUTES */,
2418
+ attributeName: qualifiedName,
2419
+ oldValue: this.getAttribute(qualifiedName)
2420
+ });
2421
+ }
2163
2422
  switch (qualifiedName) {
2164
2423
  case STYLE:
2165
2424
  this.style.cssText = value;
2166
2425
  break;
2167
2426
  case ID:
2168
- eventSource.delete(this.uid);
2427
+ if (this.uid !== this.sid) {
2428
+ // eventSource[sid] 永远保留,直到组件卸载
2429
+ // eventSource[uid] 可变
2430
+ eventSource.delete(this.uid);
2431
+ }
2169
2432
  value = String(value);
2170
2433
  this.props[qualifiedName] = this.uid = value;
2171
2434
  eventSource.set(value, this);
@@ -2183,7 +2446,7 @@ let TaroElement = class TaroElement extends TaroNode {
2183
2446
  qualifiedName = shortcutAttr(qualifiedName);
2184
2447
  const payload = {
2185
2448
  path: `${this._path}.${toCamelCase(qualifiedName)}`,
2186
- value
2449
+ value: isFunction(value) ? () => value : value
2187
2450
  };
2188
2451
  (_b = (_a = this.hooks).modifySetAttrPayload) === null || _b === void 0 ? void 0 : _b.call(_a, this, qualifiedName, payload);
2189
2452
  this.enqueueUpdate(payload);
@@ -2208,6 +2471,12 @@ let TaroElement = class TaroElement extends TaroNode {
2208
2471
  removeAttribute(qualifiedName) {
2209
2472
  var _a, _b, _c, _d;
2210
2473
  const isStaticView = this.nodeName === VIEW && isHasExtractProp(this) && !this.isAnyEventBinded();
2474
+ MutationObserver.record({
2475
+ target: this,
2476
+ type: "attributes" /* ATTRIBUTES */,
2477
+ attributeName: qualifiedName,
2478
+ oldValue: this.getAttribute(qualifiedName)
2479
+ });
2211
2480
  if (qualifiedName === STYLE) {
2212
2481
  this.style.cssText = '';
2213
2482
  }
@@ -2348,24 +2617,33 @@ class Performance {
2348
2617
  }
2349
2618
  const perf = new Performance();
2350
2619
 
2351
- function findCustomWrapper(ctx, dataPathArr) {
2352
- let currentData = ctx.__data__ || ctx.data || ctx._data;
2353
- let wrapper;
2354
- let index;
2355
- dataPathArr.some((item, i) => {
2356
- const key = item.replace(/^\[(.+)\]$/, '$1');
2620
+ function findCustomWrapper(root, dataPathArr) {
2621
+ // ['root', 'cn', '[0]'] remove 'root' => ['cn', '[0]']
2622
+ const list = dataPathArr.slice(1);
2623
+ let currentData = root;
2624
+ let customWrapper;
2625
+ let splitedPath = '';
2626
+ list.some((item, i) => {
2627
+ const key = item
2628
+ // '[0]' => '0'
2629
+ .replace(/^\[(.+)\]$/, '$1')
2630
+ // 'cn' => 'childNodes'
2631
+ .replace(/\bcn\b/g, 'childNodes');
2357
2632
  currentData = currentData[key];
2358
2633
  if (isUndefined(currentData))
2359
2634
  return true;
2360
- if (currentData.nn === CUSTOM_WRAPPER) {
2361
- wrapper = currentData;
2362
- index = i;
2635
+ if (currentData.nodeName === CUSTOM_WRAPPER) {
2636
+ const res = customWrapperCache.get(currentData.sid);
2637
+ if (res) {
2638
+ customWrapper = res;
2639
+ splitedPath = dataPathArr.slice(i + 2).join('.');
2640
+ }
2363
2641
  }
2364
2642
  });
2365
- if (wrapper) {
2643
+ if (customWrapper) {
2366
2644
  return {
2367
- wrapper,
2368
- index: index
2645
+ customWrapper,
2646
+ splitedPath
2369
2647
  };
2370
2648
  }
2371
2649
  }
@@ -2433,17 +2711,12 @@ let TaroRootElement = class TaroRootElement extends TaroElement {
2433
2711
  // 更新渲染,区分 CustomWrapper 与页面级别的 setData
2434
2712
  for (const p in data) {
2435
2713
  const dataPathArr = p.split('.');
2436
- const found = findCustomWrapper(ctx, dataPathArr);
2714
+ const found = findCustomWrapper(this, dataPathArr);
2437
2715
  if (found) {
2438
2716
  // 此项数据使用 CustomWrapper 去更新
2439
- const { wrapper, index } = found;
2440
- const customWrapperId = `#${wrapper.uid}`;
2441
- const customWrapper = ctx.selectComponent(customWrapperId);
2442
- if (customWrapper) {
2443
- const splitedPath = dataPathArr.slice(index + 1).join('.');
2444
- // 合并同一个 customWrapper 的相关更新到一次 setData 中
2445
- customWrapperMap.set(customWrapper, Object.assign(Object.assign({}, (customWrapperMap.get(customWrapper) || {})), { [`i.${splitedPath}`]: data[p] }));
2446
- }
2717
+ const { customWrapper, splitedPath } = found;
2718
+ // 合并同一个 customWrapper 的相关更新到一次 setData 中
2719
+ customWrapperMap.set(customWrapper, Object.assign(Object.assign({}, (customWrapperMap.get(customWrapper) || {})), { [`i.${splitedPath}`]: data[p] }));
2447
2720
  }
2448
2721
  else {
2449
2722
  // 此项数据使用页面去更新
@@ -2597,8 +2870,9 @@ function eventHandler(event) {
2597
2870
  const hooks = getHooks();
2598
2871
  (_a = hooks.modifyMpEvent) === null || _a === void 0 ? void 0 : _a.call(hooks, event);
2599
2872
  event.currentTarget || (event.currentTarget = event.target);
2600
- const nid = ((_b = event.currentTarget) === null || _b === void 0 ? void 0 : _b.id) || ((_c = event.detail) === null || _c === void 0 ? void 0 : _c.id) || '';
2601
- const node = getDocument().getElementById(nid);
2873
+ const currentTarget = event.currentTarget;
2874
+ const id = ((_b = currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget.dataset) === null || _b === void 0 ? void 0 : _b.sid /** sid */) || (currentTarget === null || currentTarget === void 0 ? void 0 : currentTarget.id) /** uid */ || ((_c = event.detail) === null || _c === void 0 ? void 0 : _c.id) || '';
2875
+ const node = getDocument().getElementById(id);
2602
2876
  if (node) {
2603
2877
  const dispatch = () => {
2604
2878
  var _a;
@@ -3016,8 +3290,12 @@ class StyleTagParser {
3016
3290
  // console.log('res this.styles: ', this.styles)
3017
3291
  }
3018
3292
  parseSelector(src) {
3019
- // todo: 属性选择器里可以带空格:[a = "b"],这里的 split(' ') 需要作兼容
3020
- const list = src.trim().replace(/ *([>~+]) */g, ' $1').replace(/ +/g, ' ').split(' ');
3293
+ const list = src
3294
+ .trim()
3295
+ .replace(/ *([>~+]) */g, ' $1')
3296
+ .replace(/ +/g, ' ')
3297
+ .replace(/\[\s*([^[\]=\s]+)\s*=\s*([^[\]=\s]+)\s*\]/g, '[$1=$2]')
3298
+ .split(' ');
3021
3299
  const selectors = list.map(item => {
3022
3300
  const firstChar = item.charAt(0);
3023
3301
  const selector = {
@@ -3523,7 +3801,7 @@ function bindInnerHTML(ctx, getDoc) {
3523
3801
  configurable: true,
3524
3802
  enumerable: true,
3525
3803
  set(html) {
3526
- setInnerHTML.call(ctx, ctx, html, getDoc);
3804
+ setInnerHTML.call(this, this, html, getDoc);
3527
3805
  },
3528
3806
  get() {
3529
3807
  return '';
@@ -4030,11 +4308,11 @@ if (process.env.TARO_ENV && process.env.TARO_ENV !== 'h5') {
4030
4308
  if (!(DATE in window$1)) {
4031
4309
  window$1.Date = Date;
4032
4310
  }
4033
- window$1.setTimeout = function (cb, delay) {
4034
- setTimeout(cb, delay);
4311
+ window$1.setTimeout = function (...args) {
4312
+ return setTimeout(...args);
4035
4313
  };
4036
- window$1.clearTimeout = function (seed) {
4037
- clearTimeout(seed);
4314
+ window$1.clearTimeout = function (...args) {
4315
+ return clearTimeout(...args);
4038
4316
  };
4039
4317
  document$1.defaultView = window$1;
4040
4318
  }
@@ -4171,11 +4449,8 @@ function stringify(obj) {
4171
4449
  return path === '' ? path : '?' + path;
4172
4450
  }
4173
4451
  function getPath(id, options) {
4174
- let path = id;
4175
- if (process.env.TARO_ENV !== 'h5') {
4176
- path = id + stringify(options);
4177
- }
4178
- return path;
4452
+ const idx = id.indexOf('?');
4453
+ return `${idx > -1 ? id.substring(0, idx) : id}${stringify(process.env.TARO_ENV === 'h5' ? { stamp: (options === null || options === void 0 ? void 0 : options.stamp) || '' } : options)}`;
4179
4454
  }
4180
4455
  function getOnReadyEventKey(path) {
4181
4456
  return path + '.' + ON_READY;
@@ -4208,7 +4483,7 @@ function createPageConfig(component, pageName, data, pageConfig) {
4208
4483
  let loadResolver;
4209
4484
  let hasLoaded;
4210
4485
  const config = {
4211
- [ONLOAD](options, cb) {
4486
+ [ONLOAD](options = {}, cb) {
4212
4487
  hasLoaded = new Promise(resolve => { loadResolver = resolve; });
4213
4488
  perf.start(PAGE_INIT);
4214
4489
  Current.page = this;
@@ -4217,6 +4492,9 @@ function createPageConfig(component, pageName, data, pageConfig) {
4217
4492
  // this.$taroPath 是页面唯一标识,不可变,因此页面参数 options 也不可变
4218
4493
  this.$taroPath = getPath(id, options);
4219
4494
  const $taroPath = this.$taroPath;
4495
+ if (process.env.TARO_ENV === 'h5') {
4496
+ config.path = this.$taroPath;
4497
+ }
4220
4498
  // this.$taroParams 作为暴露给开发者的页面参数对象,可以被随意修改
4221
4499
  if (this.$taroParams == null) {
4222
4500
  this.$taroParams = Object.assign({}, options);
@@ -4252,6 +4530,7 @@ function createPageConfig(component, pageName, data, pageConfig) {
4252
4530
  instances.delete($taroPath);
4253
4531
  if (pageElement) {
4254
4532
  pageElement.ctx = null;
4533
+ pageElement = null;
4255
4534
  }
4256
4535
  if (prepareMountList.length) {
4257
4536
  prepareMountList.forEach(fn => fn());
@@ -4266,13 +4545,13 @@ function createPageConfig(component, pageName, data, pageConfig) {
4266
4545
  raf(() => eventCenter.trigger(getOnReadyEventKey(id)));
4267
4546
  this.onReady.called = true;
4268
4547
  },
4269
- [ONSHOW]() {
4548
+ [ONSHOW](options = {}) {
4270
4549
  hasLoaded.then(() => {
4271
4550
  // 设置 Current 的 page 和 router
4272
4551
  Current.page = this;
4273
4552
  setCurrentRouter(this);
4274
4553
  // 触发生命周期
4275
- safeExecute(this.$taroPath, ON_SHOW);
4554
+ safeExecute(this.$taroPath, ON_SHOW, options);
4276
4555
  // 通过事件触发子组件的生命周期
4277
4556
  raf(() => eventCenter.trigger(getOnShowEventKey(id)));
4278
4557
  });
@@ -4321,9 +4600,6 @@ function createPageConfig(component, pageName, data, pageConfig) {
4321
4600
  if (!isUndefined(data)) {
4322
4601
  config.data = data;
4323
4602
  }
4324
- if (process.env.TARO_ENV === 'h5') {
4325
- config.path = id;
4326
- }
4327
4603
  (_c = hooks.modifyPageObject) === null || _c === void 0 ? void 0 : _c.call(hooks, config);
4328
4604
  return config;
4329
4605
  }
@@ -4368,8 +4644,26 @@ function createComponentConfig(component, componentName, data) {
4368
4644
  return config;
4369
4645
  }
4370
4646
  function createRecursiveComponentConfig(componentName) {
4371
- return {
4372
- properties: {
4647
+ const isCustomWrapper = componentName === CUSTOM_WRAPPER;
4648
+ const lifeCycles = isCustomWrapper
4649
+ ? {
4650
+ attached() {
4651
+ var _a;
4652
+ const componentId = (_a = this.data.i) === null || _a === void 0 ? void 0 : _a.sid;
4653
+ if (isString(componentId)) {
4654
+ customWrapperCache.set(componentId, this);
4655
+ }
4656
+ },
4657
+ detached() {
4658
+ var _a;
4659
+ const componentId = (_a = this.data.i) === null || _a === void 0 ? void 0 : _a.sid;
4660
+ if (isString(componentId)) {
4661
+ customWrapperCache.delete(componentId);
4662
+ }
4663
+ }
4664
+ }
4665
+ : EMPTY_OBJ;
4666
+ return Object.assign({ properties: {
4373
4667
  i: {
4374
4668
  type: Object,
4375
4669
  value: {
@@ -4380,15 +4674,12 @@ function createRecursiveComponentConfig(componentName) {
4380
4674
  type: String,
4381
4675
  value: ''
4382
4676
  }
4383
- },
4384
- options: {
4677
+ }, options: {
4385
4678
  addGlobalClass: true,
4386
- virtualHost: componentName !== CUSTOM_WRAPPER
4387
- },
4388
- methods: {
4679
+ virtualHost: !isCustomWrapper
4680
+ }, methods: {
4389
4681
  eh: eventHandler
4390
- }
4391
- };
4682
+ } }, lifeCycles);
4392
4683
  }
4393
4684
 
4394
4685
  function removeLeadingSlash(path) {
@@ -4429,5 +4720,5 @@ const nextTick = (cb, ctx) => {
4429
4720
  }
4430
4721
  };
4431
4722
 
4432
- export { Current, ElementNames, Events, FormElement, SERVICE_IDENTIFIER, SVGElement, Style, TaroElement, TaroEvent, TaroNode, TaroRootElement, TaroText, addLeadingSlash, caf as cancelAnimationFrame, container, createComponentConfig, createDocument, createEvent, createPageConfig, createRecursiveComponentConfig, document$1 as document, eventCenter, eventHandler, getComputedStyle, getCurrentInstance, getPageInstance, hydrate, incrementId, injectPageInstance, navigator, nextTick, now, options, processPluginHooks, raf as requestAnimationFrame, safeExecute, stringify, window$1 as window };
4723
+ export { Current, ElementNames, Events, FormElement, MutationObserver, SERVICE_IDENTIFIER, SVGElement, Style, TaroElement, TaroEvent, TaroNode, TaroRootElement, TaroText, addLeadingSlash, caf as cancelAnimationFrame, container, createComponentConfig, createDocument, createEvent, createPageConfig, createRecursiveComponentConfig, document$1 as document, eventCenter, eventHandler, eventSource, getComputedStyle, getCurrentInstance, getPageInstance, hydrate, incrementId, injectPageInstance, navigator, nextTick, now, options, processPluginHooks, raf as requestAnimationFrame, safeExecute, stringify, window$1 as window };
4433
4724
  //# sourceMappingURL=runtime.esm.js.map