@tarojs/router 3.3.14 → 3.3.18

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,5 @@
1
- import { container, SERVICE_IDENTIFIER, eventCenter, Current, stringify, createPageConfig, requestAnimationFrame } from '@tarojs/runtime';
1
+ import { Current, requestAnimationFrame, container, SERVICE_IDENTIFIER, eventCenter, createPageConfig, stringify } from '@tarojs/runtime';
2
+ import { initTabBarApis } from '@tarojs/taro';
2
3
 
3
4
  function _extends() {
4
5
  _extends = Object.assign || function (target) {
@@ -47,55 +48,126 @@ function setHistoryMode(mode, base = '/') {
47
48
  }
48
49
  function prependBasename(url = '') {
49
50
  return basename.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
50
- }
51
-
52
- const stacks = [];
51
+ }
52
+ const hasBasename = (path = '', prefix = '') => new RegExp('^' + prefix + '(\\/|\\?|#|$)', 'i').test(path);
53
+ const stripBasename = (path = '', prefix = '') => hasBasename(path, prefix) ? path.substr(prefix.length) : path;
53
54
 
54
- let routesAlias = {};
55
- function setRoutesAlias(alias) {
56
- routesAlias = alias;
55
+ class Stacks {
56
+ constructor() {
57
+ this.stacks = [];
58
+ this.backDelta = 0;
59
+ }
60
+ set delta(delta) {
61
+ if (delta > 0) {
62
+ this.backDelta = delta;
63
+ }
64
+ else {
65
+ --this.backDelta;
66
+ }
67
+ }
68
+ get length() {
69
+ return this.stacks.length;
70
+ }
71
+ get last() {
72
+ return this.stacks[this.length - 1];
73
+ }
74
+ get() {
75
+ return this.stacks;
76
+ }
77
+ getItem(index) {
78
+ return this.stacks[index];
79
+ }
80
+ getLastIndex(pathname, stateWith = 1) {
81
+ const list = [...this.stacks].reverse();
82
+ return list.findIndex((page, i) => { var _a; return i >= stateWith && ((_a = page.path) === null || _a === void 0 ? void 0 : _a.replace(/\?.*/g, '')) === pathname; });
83
+ }
84
+ getDelta(pathname) {
85
+ if (this.backDelta >= 1) {
86
+ return this.backDelta;
87
+ }
88
+ // NOTE: 此处为了修复浏览器后退多级页面,在大量重复路由状况下可能出现判断错误的情况 (增强判断能力只能考虑在 query 中新增参数来判断,暂时搁置)
89
+ return this.getLastIndex(pathname) || 1;
90
+ }
91
+ getPrevIndex(pathname, stateWith = 1) {
92
+ const lastIndex = this.getLastIndex(pathname, stateWith);
93
+ if (lastIndex < 0) {
94
+ return -1;
95
+ }
96
+ return this.length - 1 - lastIndex;
97
+ }
98
+ pop() {
99
+ return this.stacks.pop();
100
+ }
101
+ push(page) {
102
+ return this.stacks.push(page);
103
+ }
57
104
  }
105
+ const stacks = new Stacks();
106
+
58
107
  function addLeadingSlash(path) {
59
108
  if (path == null) {
60
109
  return '';
61
110
  }
62
111
  return path.charAt(0) === '/' ? path : '/' + path;
63
112
  }
64
- // 解决navigateBack调用delta>1时,路由栈异常问题
65
- // 比如:A->B->C,navigateBack({delta: 2}),此时路由栈中还存在B页面
66
- // 原因:主要是由于一次性退出多层级页面时,此action只会执行一次,此处进行手动处理
67
- let historyBackDelta = 1;
68
- function setHistoryBackDelta(delta) {
69
- historyBackDelta = delta;
70
- }
71
- // eslint-disable-next-line @typescript-eslint/ban-types
72
- const throttle = (fn, threshold) => {
73
- let lastTime = 0;
74
- return function () {
75
- const now = Date.now();
76
- if (now - lastTime > threshold) {
77
- fn.apply(this, arguments);
78
- lastTime = now;
113
+ class RoutesAlias {
114
+ constructor() {
115
+ this.conf = [];
116
+ this.getConfig = (url = '') => {
117
+ const customRoute = this.conf.filter((arr) => {
118
+ return arr.includes(url);
119
+ });
120
+ return customRoute[0];
121
+ };
122
+ this.getOrigin = (url = '') => {
123
+ var _a;
124
+ return ((_a = this.getConfig(url)) === null || _a === void 0 ? void 0 : _a[0]) || url;
125
+ };
126
+ this.getAlias = (url = '') => {
127
+ var _a;
128
+ return ((_a = this.getConfig(url)) === null || _a === void 0 ? void 0 : _a[1]) || url;
129
+ };
130
+ this.getAll = (url = '') => {
131
+ return this.conf.filter((arr) => {
132
+ return arr.includes(url);
133
+ }).reduce((p, a) => {
134
+ p.push(a[1]);
135
+ return p;
136
+ }, [url]);
137
+ };
138
+ }
139
+ set(customRoutes = {}) {
140
+ for (let key in customRoutes) {
141
+ const path = customRoutes[key];
142
+ key = addLeadingSlash(key);
143
+ if (typeof path === 'string') {
144
+ this.conf.push([key, addLeadingSlash(path)]);
145
+ }
146
+ else if ((path === null || path === void 0 ? void 0 : path.length) > 0) {
147
+ this.conf.push(...path.map(p => [key, addLeadingSlash(p)]));
148
+ }
79
149
  }
80
- };
81
- };
82
- const stripBasename = (path, basename) => path.startsWith(basename) ? path.replace(basename, '') : path;
83
- const isTabBar = (config) => {
84
- var _a, _b;
85
- const { customRoutes = {}, basename = '', pathname } = config.router;
86
- const routePath = stripBasename(pathname, basename);
87
- const pagePath = ((_a = Object.entries(customRoutes).find(([, target]) => target === routePath)) === null || _a === void 0 ? void 0 : _a[0]) || routePath;
88
- return !!pagePath && (((_b = config.tabBar) === null || _b === void 0 ? void 0 : _b.list) || []).some(t => t.pagePath === pagePath);
89
- };
150
+ }
151
+ }
152
+ const routesAlias = new RoutesAlias();
90
153
 
91
154
  function processNavigateUrl(option) {
155
+ var _a;
92
156
  const pathPieces = J(option.url);
93
157
  // 处理自定义路由
94
- Object.keys(routesAlias).forEach(key => {
95
- if (addLeadingSlash(key) === addLeadingSlash(pathPieces.pathname)) {
96
- pathPieces.pathname = routesAlias[key];
97
- }
98
- });
158
+ pathPieces.pathname = routesAlias.getAlias(addLeadingSlash(pathPieces.pathname));
159
+ // 处理相对路径
160
+ if ((_a = pathPieces === null || pathPieces === void 0 ? void 0 : pathPieces.pathname) === null || _a === void 0 ? void 0 : _a.includes('./')) {
161
+ const parts = history.location.pathname.split('/');
162
+ parts.pop();
163
+ pathPieces.pathname.split('/').forEach((item) => {
164
+ if (item === '.') {
165
+ return;
166
+ }
167
+ item === '..' ? parts.pop() : parts.push(item);
168
+ });
169
+ pathPieces.pathname = parts.join('/');
170
+ }
99
171
  // 处理 basename
100
172
  pathPieces.pathname = prependBasename(pathPieces.pathname);
101
173
  // hack fix history v5 bug: https://github.com/remix-run/history/issues/814
@@ -107,9 +179,10 @@ async function navigate(option, method) {
107
179
  return new Promise((resolve, reject) => {
108
180
  const { success, complete, fail } = option;
109
181
  const unListen = history.listen(() => {
110
- success === null || success === void 0 ? void 0 : success();
111
- complete === null || complete === void 0 ? void 0 : complete();
112
- resolve();
182
+ const res = { errMsg: `${method}: ok` };
183
+ success === null || success === void 0 ? void 0 : success(res);
184
+ complete === null || complete === void 0 ? void 0 : complete(res);
185
+ resolve(res);
113
186
  unListen();
114
187
  });
115
188
  try {
@@ -119,19 +192,24 @@ async function navigate(option, method) {
119
192
  if (method === 'navigateTo') {
120
193
  history.push(pathPieces, state);
121
194
  }
122
- else if (method === 'redirectTo') {
195
+ else if (method === 'redirectTo' || method === 'switchTab') {
196
+ history.replace(pathPieces, state);
197
+ }
198
+ else if (method === 'reLaunch') {
199
+ stacks.delta = stacks.length;
123
200
  history.replace(pathPieces, state);
124
201
  }
125
202
  }
126
203
  else if (method === 'navigateBack') {
127
- setHistoryBackDelta(option.delta);
204
+ stacks.delta = option.delta;
128
205
  history.go(-option.delta);
129
206
  }
130
207
  }
131
208
  catch (error) {
132
- fail === null || fail === void 0 ? void 0 : fail(error);
133
- complete === null || complete === void 0 ? void 0 : complete();
134
- reject(error);
209
+ const res = { errMsg: `${method}: fail ${error.message || error}` };
210
+ fail === null || fail === void 0 ? void 0 : fail(res);
211
+ complete === null || complete === void 0 ? void 0 : complete(res);
212
+ reject(res);
135
213
  }
136
214
  });
137
215
  }
@@ -148,15 +226,14 @@ function navigateBack(options = { delta: 1 }) {
148
226
  return navigate(options, 'navigateBack');
149
227
  }
150
228
  function switchTab(option) {
151
- // TODO: 清除掉所有的栈去目标页面
152
- return redirectTo(option);
229
+ return navigate(option, 'switchTab');
153
230
  }
154
231
  function reLaunch(option) {
155
- // TODO: 清除掉所有的栈去目标页面
156
- return redirectTo(option);
232
+ return navigate(option, 'reLaunch');
157
233
  }
158
234
  function getCurrentPages() {
159
- return stacks;
235
+ const pages = stacks.get();
236
+ return pages.map(e => (Object.assign(Object.assign({}, e), { route: e.path || '' })));
160
237
  }
161
238
 
162
239
  /**
@@ -802,55 +879,6 @@ var UniversalRouter = function () {
802
879
 
803
880
  UniversalRouter.pathToRegexp = pathToRegexp_1;
804
881
 
805
- function initTabbar(config) {
806
- if (config.tabBar == null) {
807
- return;
808
- }
809
- // TODO: 找到 tabbar 的类型
810
- const tabbar = document.createElement('taro-tabbar');
811
- const homePage = config.pages ? config.pages[0] : '';
812
- tabbar.conf = config.tabBar;
813
- tabbar.conf.homePage = history.location.pathname === '/' ? homePage : history.location.pathname;
814
- const routerConfig = config.router;
815
- tabbar.conf.mode = routerConfig && routerConfig.mode ? routerConfig.mode : 'hash';
816
- tabbar.conf.custom = !!routerConfig.customRoutes;
817
- if (routerConfig.customRoutes) {
818
- tabbar.conf.custom = true;
819
- tabbar.conf.customRoutes = routerConfig.customRoutes;
820
- }
821
- else {
822
- tabbar.conf.custom = false;
823
- tabbar.conf.customRoutes = {};
824
- }
825
- if (typeof routerConfig.basename !== 'undefined') {
826
- tabbar.conf.basename = routerConfig.basename;
827
- }
828
- const container = document.getElementById('container');
829
- // eslint-disable-next-line no-unused-expressions
830
- container === null || container === void 0 ? void 0 : container.appendChild(tabbar);
831
- }
832
-
833
- const routerConfig = Object.create(null);
834
- function init(config) {
835
- var _a;
836
- config.router.mode = config.router.mode || 'hash';
837
- setHistoryMode(config.router.mode, config.router.basename);
838
- Object.assign(routerConfig, config);
839
- (_a = document.getElementById('app')) === null || _a === void 0 ? void 0 : _a.remove();
840
- const container = document.createElement('div');
841
- container.classList.add('taro-tabbar__container');
842
- container.id = 'container';
843
- const panel = document.createElement('div');
844
- panel.classList.add('taro-tabbar__panel');
845
- const app = document.createElement('div');
846
- app.id = 'app';
847
- app.classList.add('taro_router');
848
- panel.appendChild(app);
849
- container.appendChild(panel);
850
- document.body.appendChild(container);
851
- initTabbar(config);
852
- }
853
-
854
882
  function createCommonjsModule(fn, module) {
855
883
  return module = { exports: {} }, fn(module, module.exports), module.exports;
856
884
  }
@@ -1402,19 +1430,6 @@ var queryString_5 = queryString.stringifyUrl;
1402
1430
  var queryString_6 = queryString.pick;
1403
1431
  var queryString_7 = queryString.exclude;
1404
1432
 
1405
- const qs = function (stamp = 0) {
1406
- const search = routerConfig.router.mode === 'hash'
1407
- ? location.hash.slice(routerConfig.router.pathname.length + 1)
1408
- : location.search;
1409
- const query = search
1410
- ? queryString.parse(search.substr(1))
1411
- : {};
1412
- if (stamp) {
1413
- query.stamp = stamp.toString();
1414
- }
1415
- return query;
1416
- };
1417
-
1418
1433
  let pageScrollFn;
1419
1434
  let pageDOM = window;
1420
1435
  function bindPageScroll(page, config) {
@@ -1439,24 +1454,22 @@ function bindPageScroll(page, config) {
1439
1454
  pageDOM.addEventListener('scroll', pageScrollFn, false);
1440
1455
  }
1441
1456
  window.addEventListener('DOMSubtreeModified', (e) => {
1442
- var _a;
1443
- // @ts-ignore
1444
- const className = (_a = e.target) === null || _a === void 0 ? void 0 : _a.className;
1445
- if (className && /taro-tabbar__/.test(className)) {
1457
+ const target = e.target;
1458
+ const className = target === null || target === void 0 ? void 0 : target.className;
1459
+ if (className && /taro_page/.test(className)) {
1446
1460
  pageDOM.removeEventListener('scroll', pageScrollFn);
1447
1461
  pageDOM = getScrollContainer();
1448
1462
  pageDOM.addEventListener('scroll', pageScrollFn, false);
1449
1463
  }
1450
1464
  }, false);
1451
1465
  function getScrollContainer() {
1452
- if (document.querySelector('.taro-tabbar__tabbar') === null) {
1453
- // 没设置tabbar
1454
- return window;
1455
- }
1456
- else {
1457
- // 有设置tabbar
1458
- return document.querySelector('.taro-tabbar__panel') || window;
1459
- }
1466
+ var _a;
1467
+ const id = (_a = Current.page) === null || _a === void 0 ? void 0 : _a.path;
1468
+ const el = (id
1469
+ ? document.getElementById(id)
1470
+ : document.querySelector('.taro_page') ||
1471
+ document.querySelector('.taro_router'));
1472
+ return el || window;
1460
1473
  }
1461
1474
  function getOffset() {
1462
1475
  if (pageDOM instanceof Window) {
@@ -1467,97 +1480,310 @@ function getOffset() {
1467
1480
  }
1468
1481
  }
1469
1482
 
1470
- /* eslint-disable dot-notation */
1471
- function hidePage(page) {
1472
- if (page != null) {
1473
- page.onHide();
1474
- const pageEl = document.getElementById(page.path);
1475
- if (pageEl) {
1476
- pageEl.style.display = 'none';
1477
- }
1483
+ /**
1484
+ * 插入页面动画需要的样式
1485
+ */
1486
+ function loadAnimateStyle(ms = 300) {
1487
+ const css = `
1488
+ .taro_router .taro_page {
1489
+ position: absolute;
1490
+ left: 0;
1491
+ top: 0;
1492
+ width: 100%;
1493
+ height: 100%;
1494
+ transform: translate3d(100%, 0, 0);
1495
+ transition: transform ${ms}ms;
1496
+ }
1497
+
1498
+ .taro_router .taro_page.taro_tabbar_page {
1499
+ transform: none;
1500
+ }
1501
+
1502
+ .taro_router .taro_page.taro_page_show {
1503
+ transform: translate3d(0, 0, 0);
1504
+ }`;
1505
+ const style = document.createElement('style');
1506
+ style.innerHTML = css;
1507
+ document.getElementsByTagName('head')[0].appendChild(style);
1508
+ }
1509
+
1510
+ // @ts-nocheck
1511
+ function initTabbar(config) {
1512
+ if (config.tabBar == null) {
1513
+ return;
1514
+ }
1515
+ // TODO: custom-tab-bar
1516
+ const tabbar = document.createElement('taro-tabbar');
1517
+ const homePage = config.entryPagePath || (config.pages ? config.pages[0] : '');
1518
+ tabbar.conf = config.tabBar;
1519
+ tabbar.conf.homePage = history.location.pathname === '/' ? homePage : history.location.pathname;
1520
+ const routerConfig = config.router;
1521
+ tabbar.conf.mode = routerConfig && routerConfig.mode ? routerConfig.mode : 'hash';
1522
+ if (routerConfig.customRoutes) {
1523
+ tabbar.conf.custom = true;
1524
+ tabbar.conf.customRoutes = routerConfig.customRoutes;
1525
+ }
1526
+ else {
1527
+ tabbar.conf.custom = false;
1528
+ tabbar.conf.customRoutes = {};
1529
+ }
1530
+ if (typeof routerConfig.basename !== 'undefined') {
1531
+ tabbar.conf.basename = routerConfig.basename;
1532
+ }
1533
+ const container = document.getElementById('container');
1534
+ container === null || container === void 0 ? void 0 : container.appendChild(tabbar);
1535
+ initTabBarApis(config);
1536
+ }
1537
+
1538
+ function setDisplay(el, type = '') {
1539
+ if (el) {
1540
+ el.style.display = type;
1478
1541
  }
1479
1542
  }
1480
- function showPage(page, pageConfig, stacksIndex = 0) {
1481
- if (page != null) {
1482
- page.onShow();
1483
- let pageEl = document.getElementById(page.path);
1484
- if (pageEl) {
1485
- pageEl.style.display = 'block';
1543
+ class PageHandler {
1544
+ constructor(config) {
1545
+ this.defaultAnimation = { duration: 300, delay: 50 };
1546
+ this.config = config;
1547
+ this.mount();
1548
+ }
1549
+ get appId() { return 'app'; }
1550
+ get router() { return this.config.router; }
1551
+ get routerMode() { return this.router.mode || 'hash'; }
1552
+ get customRoutes() { return this.router.customRoutes || {}; }
1553
+ get routes() { return this.config.routes; }
1554
+ get tabBarList() { var _a; return ((_a = this.config.tabBar) === null || _a === void 0 ? void 0 : _a.list) || []; }
1555
+ get PullDownRefresh() { return this.config.PullDownRefresh; }
1556
+ get animation() { var _a, _b; return (_b = (_a = this.config) === null || _a === void 0 ? void 0 : _a.animation) !== null && _b !== void 0 ? _b : this.defaultAnimation; }
1557
+ get animationDelay() {
1558
+ var _a;
1559
+ return typeof this.animation === 'object'
1560
+ ? this.animation.delay
1561
+ : this.animation
1562
+ ? (_a = this.defaultAnimation) === null || _a === void 0 ? void 0 : _a.delay
1563
+ : 0;
1564
+ }
1565
+ get animationDuration() {
1566
+ var _a;
1567
+ return typeof this.animation === 'object'
1568
+ ? this.animation.duration
1569
+ : this.animation
1570
+ ? (_a = this.defaultAnimation) === null || _a === void 0 ? void 0 : _a.duration
1571
+ : 0;
1572
+ }
1573
+ set pathname(p) { this.router.pathname = p; }
1574
+ get pathname() { return this.router.pathname; }
1575
+ get basename() { return this.router.basename || ''; }
1576
+ get pageConfig() {
1577
+ return this.routes.find(r => {
1578
+ var _a;
1579
+ const routePath = stripBasename(this.pathname, this.basename);
1580
+ const pagePath = addLeadingSlash(r.path);
1581
+ return pagePath === routePath || ((_a = routesAlias.getConfig(pagePath)) === null || _a === void 0 ? void 0 : _a.includes(routePath));
1582
+ });
1583
+ }
1584
+ get isTabBar() {
1585
+ var _a;
1586
+ const routePath = stripBasename(this.pathname, this.basename);
1587
+ const pagePath = ((_a = Object.entries(this.customRoutes).find(([, target]) => {
1588
+ if (typeof target === 'string') {
1589
+ return target === routePath;
1590
+ }
1591
+ else if ((target === null || target === void 0 ? void 0 : target.length) > 0) {
1592
+ return target.includes(routePath);
1593
+ }
1594
+ return false;
1595
+ })) === null || _a === void 0 ? void 0 : _a[0]) || routePath;
1596
+ return !!pagePath && this.tabBarList.some(t => t.pagePath === pagePath);
1597
+ }
1598
+ isSamePage(page) {
1599
+ const routePath = stripBasename(this.pathname, this.basename);
1600
+ const pagePath = stripBasename(page === null || page === void 0 ? void 0 : page.path, this.basename);
1601
+ return pagePath.startsWith(routePath + '?');
1602
+ }
1603
+ get search() {
1604
+ let search = '?';
1605
+ if (this.routerMode) {
1606
+ const idx = location.hash.indexOf('?');
1607
+ if (idx > -1) {
1608
+ search = location.hash.slice(idx);
1609
+ }
1486
1610
  }
1487
1611
  else {
1488
- page.onLoad(qs(stacksIndex));
1489
- pageEl = document.getElementById(page.path);
1490
- pageOnReady(pageEl, page, false);
1612
+ search = location.search;
1491
1613
  }
1492
- bindPageScroll(page, pageConfig || {});
1614
+ return search.substr(1);
1493
1615
  }
1494
- }
1495
- function unloadPage(page) {
1496
- if (page != null) {
1497
- stacks.pop();
1498
- page.onUnload();
1616
+ getQuery(stamp = 0) {
1617
+ const search = this.search;
1618
+ const query = search
1619
+ ? queryString.parse(search)
1620
+ : {};
1621
+ if (stamp) {
1622
+ query.stamp = stamp.toString();
1623
+ }
1624
+ return query;
1499
1625
  }
1500
- }
1501
- function pageOnReady(pageEl, page, onLoad = true) {
1502
- var _a;
1503
- if (pageEl && !(pageEl === null || pageEl === void 0 ? void 0 : pageEl['__isReady'])) {
1504
- const el = pageEl.firstElementChild;
1505
- // eslint-disable-next-line no-unused-expressions
1506
- (_a = el === null || el === void 0 ? void 0 : el['componentOnReady']) === null || _a === void 0 ? void 0 : _a.call(el).then(() => {
1507
- requestAnimationFrame(() => {
1508
- page.onReady();
1509
- pageEl['__isReady'] = true;
1626
+ mount() {
1627
+ var _a;
1628
+ setHistoryMode(this.routerMode, this.router.basename);
1629
+ (_a = document.getElementById('app')) === null || _a === void 0 ? void 0 : _a.remove();
1630
+ this.animation && loadAnimateStyle(this.animationDuration);
1631
+ const app = document.createElement('div');
1632
+ app.id = this.appId;
1633
+ app.classList.add('taro_router');
1634
+ if (this.tabBarList.length > 1) {
1635
+ const container = document.createElement('div');
1636
+ container.classList.add('taro-tabbar__container');
1637
+ container.id = 'container';
1638
+ const panel = document.createElement('div');
1639
+ panel.classList.add('taro-tabbar__panel');
1640
+ panel.appendChild(app);
1641
+ container.appendChild(panel);
1642
+ document.body.appendChild(container);
1643
+ initTabbar(this.config);
1644
+ }
1645
+ else {
1646
+ document.body.appendChild(app);
1647
+ }
1648
+ }
1649
+ onReady(page, onLoad = true) {
1650
+ var _a, _b;
1651
+ const pageEl = document.getElementById(page.path);
1652
+ if (pageEl && !(pageEl === null || pageEl === void 0 ? void 0 : pageEl['__isReady'])) {
1653
+ const el = pageEl.firstElementChild;
1654
+ (_b = (_a = el === null || el === void 0 ? void 0 : el['componentOnReady']) === null || _a === void 0 ? void 0 : _a.call(el)) === null || _b === void 0 ? void 0 : _b.then(() => {
1655
+ requestAnimationFrame(() => {
1656
+ page.onReady();
1657
+ pageEl['__isReady'] = true;
1658
+ });
1510
1659
  });
1511
- });
1512
- onLoad && (pageEl['__page'] = page);
1660
+ onLoad && (pageEl['__page'] = page);
1661
+ }
1513
1662
  }
1514
- }
1515
- function loadPage(page, pageConfig, stacksIndex = 0) {
1516
- if (page !== null) {
1663
+ load(page, pageConfig = {}, stacksIndex = 0) {
1664
+ if (!page)
1665
+ return;
1517
1666
  let pageEl = document.getElementById(page.path);
1518
1667
  if (pageEl) {
1519
- pageEl.style.display = 'block';
1668
+ setDisplay(pageEl);
1669
+ this.isTabBar && pageEl.classList.add('taro_tabbar_page');
1670
+ this.addAnimation(pageEl, stacksIndex === 0);
1520
1671
  }
1521
1672
  else {
1522
- page.onLoad(qs(stacksIndex), function () {
1673
+ page.onLoad(this.getQuery(stacksIndex), () => {
1523
1674
  pageEl = document.getElementById(page.path);
1524
- pageOnReady(pageEl, page);
1675
+ this.isTabBar && (pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.add('taro_tabbar_page'));
1676
+ this.addAnimation(pageEl, stacksIndex === 0);
1677
+ this.onReady(page, true);
1525
1678
  });
1526
1679
  }
1527
1680
  stacks.push(page);
1528
1681
  page.onShow();
1529
- bindPageScroll(page, pageConfig || {});
1682
+ bindPageScroll(page, pageConfig);
1530
1683
  }
1531
- }
1684
+ unload(page, delta = 1, top = false) {
1685
+ var _a;
1686
+ if (!page)
1687
+ return;
1688
+ stacks.delta = --delta;
1689
+ stacks.pop();
1690
+ if (this.animation && top) {
1691
+ if (this.unloadTimer) {
1692
+ clearTimeout(this.unloadTimer);
1693
+ (_a = this.lastUnloadPage) === null || _a === void 0 ? void 0 : _a.onUnload();
1694
+ this.unloadTimer = null;
1695
+ }
1696
+ this.lastUnloadPage = page;
1697
+ const pageEl = document.getElementById(page.path);
1698
+ pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.remove('taro_page_show');
1699
+ this.unloadTimer = setTimeout(() => {
1700
+ var _a;
1701
+ this.unloadTimer = null;
1702
+ (_a = this.lastUnloadPage) === null || _a === void 0 ? void 0 : _a.onUnload();
1703
+ }, this.animationDuration);
1704
+ }
1705
+ if (delta >= 1)
1706
+ this.unload(stacks.last, delta);
1707
+ }
1708
+ show(page, pageConfig = {}, stacksIndex = 0) {
1709
+ if (!page)
1710
+ return;
1711
+ page.onShow();
1712
+ let pageEl = document.getElementById(page.path);
1713
+ if (pageEl) {
1714
+ setDisplay(pageEl);
1715
+ this.addAnimation(pageEl, stacksIndex === 0);
1716
+ }
1717
+ else {
1718
+ page.onLoad(this.getQuery(stacksIndex), () => {
1719
+ pageEl = document.getElementById(page.path);
1720
+ this.addAnimation(pageEl, stacksIndex === 0);
1721
+ this.onReady(page, false);
1722
+ });
1723
+ }
1724
+ bindPageScroll(page, pageConfig);
1725
+ }
1726
+ hide(page) {
1727
+ if (!page)
1728
+ return;
1729
+ // NOTE: 修复多页并发问题,此处可能因为路由跳转过快,执行时页面可能还没有创建成功
1730
+ const pageEl = document.getElementById(page.path);
1731
+ if (pageEl) {
1732
+ if (this.hideTimer) {
1733
+ clearTimeout(this.hideTimer);
1734
+ this.hideTimer = null;
1735
+ setDisplay(this.lastHidePage, 'none');
1736
+ }
1737
+ this.lastHidePage = pageEl;
1738
+ this.hideTimer = setTimeout(() => {
1739
+ this.hideTimer = null;
1740
+ setDisplay(this.lastHidePage, 'none');
1741
+ }, this.animationDelay);
1742
+ page.onHide();
1743
+ }
1744
+ else {
1745
+ setTimeout(() => this.hide(page), 0);
1746
+ }
1747
+ }
1748
+ addAnimation(pageEl, first = false) {
1749
+ if (!pageEl)
1750
+ return;
1751
+ if (this.animation && !first) {
1752
+ setTimeout(() => {
1753
+ pageEl.classList.add('taro_page_show');
1754
+ }, this.animationDelay);
1755
+ }
1756
+ else {
1757
+ pageEl.classList.add('taro_page_show');
1758
+ }
1759
+ }
1760
+ }
1761
+
1762
+ /* eslint-disable dot-notation */
1532
1763
  function createRouter(app, config, framework) {
1533
1764
  var _a;
1534
- init(config);
1535
- const routes = [];
1536
- const alias = (_a = config.router.customRoutes) !== null && _a !== void 0 ? _a : {};
1765
+ const handler = new PageHandler(config);
1537
1766
  const runtimeHooks = container.get(SERVICE_IDENTIFIER.Hooks);
1538
- setRoutesAlias(alias);
1539
- for (let i = 0; i < config.routes.length; i++) {
1540
- const route = config.routes[i];
1541
- const path = addLeadingSlash(route.path);
1542
- routes.push({
1543
- path: alias[path] || path,
1544
- action: route.load
1545
- });
1546
- }
1547
- const basename = config.router.basename;
1767
+ routesAlias.set(handler.router.customRoutes);
1768
+ const basename = handler.router.basename;
1769
+ const routes = handler.routes.map(route => ({
1770
+ path: routesAlias.getAll(addLeadingSlash(route.path)),
1771
+ action: route.load
1772
+ }));
1773
+ const entryPagePath = config.entryPagePath || ((_a = routes[0].path) === null || _a === void 0 ? void 0 : _a[0]);
1548
1774
  const router = new UniversalRouter(routes, { baseUrl: basename || '' });
1549
1775
  app.onLaunch();
1550
- const render = throttle(async ({ location, action }) => {
1776
+ const render = async ({ location, action }) => {
1551
1777
  var _a, _b, _c, _d;
1552
- routerConfig.router.pathname = location.pathname;
1778
+ handler.pathname = location.pathname;
1553
1779
  let element;
1554
1780
  try {
1555
- element = await router.resolve(config.router.forcePath || location.pathname);
1781
+ element = await router.resolve(handler.router.forcePath || handler.pathname);
1556
1782
  }
1557
1783
  catch (error) {
1558
1784
  if (error.status === 404) {
1559
1785
  (_a = app.onPageNotFound) === null || _a === void 0 ? void 0 : _a.call(app, {
1560
- path: location.pathname
1786
+ path: handler.pathname
1561
1787
  });
1562
1788
  }
1563
1789
  else {
@@ -1566,84 +1792,68 @@ function createRouter(app, config, framework) {
1566
1792
  }
1567
1793
  if (!element)
1568
1794
  return;
1569
- const pageConfig = config.routes.find(r => {
1570
- const path = addLeadingSlash(r.path);
1571
- return path === location.pathname || alias[path] === location.pathname;
1572
- });
1795
+ const pageConfig = handler.pageConfig;
1573
1796
  let enablePullDownRefresh = false;
1574
1797
  eventCenter.trigger('__taroRouterChange', {
1575
1798
  toLocation: {
1576
- path: location.pathname
1799
+ path: handler.pathname
1577
1800
  }
1578
1801
  });
1579
1802
  if (pageConfig) {
1580
1803
  document.title = (_b = pageConfig.navigationBarTitleText) !== null && _b !== void 0 ? _b : document.title;
1581
1804
  enablePullDownRefresh = pageConfig.enablePullDownRefresh;
1582
1805
  }
1806
+ const currentPage = Current.page;
1807
+ const pathname = handler.pathname;
1583
1808
  let shouldLoad = false;
1584
1809
  if (action === 'POP') {
1585
- unloadPage(Current.page);
1586
- let delta = historyBackDelta;
1587
- while (delta-- > 1) {
1588
- unloadPage(stacks.slice(-1)[0]);
1589
- }
1590
- // 最终必须重置为 1
1591
- setHistoryBackDelta(1);
1592
- const prevIndex = stacks.reduceRight((p, s, i) => {
1593
- if (p !== 0)
1594
- return p;
1595
- else if (s.path === location.pathname + stringify(qs(i)))
1596
- return i;
1597
- else
1598
- return 0;
1599
- }, 0);
1600
- const prev = stacks[prevIndex];
1601
- if (prev) {
1602
- showPage(prev, pageConfig, prevIndex);
1810
+ // NOTE: 浏览器事件退后多次时,该事件只会被触发一次
1811
+ const prevIndex = stacks.getPrevIndex(pathname);
1812
+ const delta = stacks.getDelta(pathname);
1813
+ handler.unload(currentPage, delta, prevIndex > -1);
1814
+ if (prevIndex > -1) {
1815
+ handler.show(stacks.getItem(prevIndex), pageConfig, prevIndex);
1603
1816
  }
1604
1817
  else {
1605
1818
  shouldLoad = true;
1606
1819
  }
1607
1820
  }
1608
- else if (action === 'PUSH') {
1609
- hidePage(Current.page);
1610
- shouldLoad = true;
1611
- }
1612
- else if (action === 'REPLACE') {
1613
- if (isTabBar(config)) {
1614
- hidePage(Current.page);
1615
- const pathname = stripBasename(config.router.pathname, basename);
1616
- const prevIndex = stacks.findIndex((r) => {
1617
- var _a;
1618
- return ((_a = r.path) === null || _a === void 0 ? void 0 : _a.replace(/\?.*/g, '')) === pathname;
1619
- });
1821
+ else {
1822
+ if (handler.isTabBar) {
1823
+ if (handler.isSamePage(currentPage))
1824
+ return;
1825
+ const prevIndex = stacks.getPrevIndex(pathname, 0);
1826
+ handler.hide(currentPage);
1620
1827
  if (prevIndex > -1) {
1621
- // tabbar 页且之前出现过,直接复用
1622
- const prev = stacks[prevIndex];
1623
- return showPage(prev, pageConfig, prevIndex);
1828
+ // NOTE: tabbar 页且之前出现过,直接复用
1829
+ return handler.show(stacks.getItem(prevIndex), pageConfig, prevIndex);
1624
1830
  }
1625
1831
  }
1626
- else {
1627
- unloadPage(Current.page);
1832
+ else if (action === 'REPLACE') {
1833
+ const delta = stacks.getDelta(pathname);
1834
+ // NOTE: 页面路由记录并不会清空,只是移除掉缓存的 stack 以及页面
1835
+ handler.unload(currentPage, delta);
1836
+ }
1837
+ else if (action === 'PUSH') {
1838
+ handler.hide(currentPage);
1628
1839
  }
1629
1840
  shouldLoad = true;
1630
1841
  }
1631
- if (shouldLoad) {
1842
+ if (shouldLoad || stacks.length < 1) {
1632
1843
  const el = (_c = element.default) !== null && _c !== void 0 ? _c : element;
1633
1844
  const loadConfig = Object.assign({}, pageConfig);
1845
+ const stacksIndex = stacks.length;
1634
1846
  delete loadConfig['path'];
1635
1847
  delete loadConfig['load'];
1636
- const pathname = stripBasename(config.router.pathname, basename);
1637
- const routerIndex = stacks.length;
1638
- const page = createPageConfig(enablePullDownRefresh ? (_d = runtimeHooks.createPullDownComponent) === null || _d === void 0 ? void 0 : _d.call(runtimeHooks, el, location.pathname, framework, routerConfig.PullDownRefresh) : el, pathname + stringify(qs(routerIndex)), {}, loadConfig);
1639
- loadPage(page, pageConfig, routerIndex);
1848
+ const page = createPageConfig(enablePullDownRefresh ? (_d = runtimeHooks.createPullDownComponent) === null || _d === void 0 ? void 0 : _d.call(runtimeHooks, el, location.pathname, framework, handler.PullDownRefresh) : el, pathname + stringify(handler.getQuery(stacksIndex)), {}, loadConfig);
1849
+ return handler.load(page, pageConfig, stacksIndex);
1640
1850
  }
1641
- }, 500);
1851
+ };
1642
1852
  if (history.location.pathname === '/') {
1643
- history.replace(prependBasename(routes[0].path + history.location.search));
1853
+ history.replace(prependBasename(entryPagePath + history.location.search));
1644
1854
  }
1645
1855
  render({ location: history.location, action: r.Push });
1646
- app.onShow(qs(stacks.length));
1856
+ app.onShow(handler.getQuery(stacks.length));
1647
1857
  return history.listen(render);
1648
1858
  }
1649
1859