UNPKG

42.3 kBJavaScriptView Raw
1import { createBrowserHistory, createHashHistory, Action, parsePath } from 'history';
2import { Current, eventCenter, createPageConfig, hooks, stringify, requestAnimationFrame } from '@tarojs/runtime';
3import MobileDetect from 'mobile-detect';
4import queryString from 'query-string';
5import { initTabBarApis } from '@tarojs/taro';
6import UniversalRouter from 'universal-router';
7
8/*! *****************************************************************************
9Copyright (c) Microsoft Corporation.
10
11Permission to use, copy, modify, and/or distribute this software for any
12purpose with or without fee is hereby granted.
13
14THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
15REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
16AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
17INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
18LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
19OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
20PERFORMANCE OF THIS SOFTWARE.
21***************************************************************************** */
22
23function __awaiter(thisArg, _arguments, P, generator) {
24 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
25 return new (P || (P = Promise))(function (resolve, reject) {
26 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
27 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
28 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
29 step((generator = generator.apply(thisArg, _arguments || [])).next());
30 });
31}
32
33// export const removeLeadingSlash = (str = '') => str.replace(/^\.?\//, '')
34// export const removeTrailingSearch = (str = '') => str.replace(/\?[\s\S]*$/, '')
35const addLeadingSlash = (url = '') => (url.charAt(0) === '/' ? url : '/' + url);
36const hasBasename = (path = '', prefix = '') => new RegExp('^' + prefix + '(\\/|\\?|#|$)', 'i').test(path) || path === prefix;
37const stripBasename = (path = '', prefix = '') => hasBasename(path, prefix) ? path.substring(prefix.length) : path;
38const stripTrailing = (str = '') => str.replace(/[?#][\s\S]*$/, '');
39class RoutesAlias {
40 constructor() {
41 this.conf = [];
42 this.getConfig = (url = '') => {
43 const customRoute = this.conf.filter((arr) => {
44 return arr.includes(url);
45 });
46 return customRoute[0];
47 };
48 this.getOrigin = (url = '') => {
49 var _a;
50 return ((_a = this.getConfig(url)) === null || _a === void 0 ? void 0 : _a[0]) || url;
51 };
52 this.getAlias = (url = '') => {
53 var _a;
54 return ((_a = this.getConfig(url)) === null || _a === void 0 ? void 0 : _a[1]) || url;
55 };
56 this.getAll = (url = '') => {
57 return this.conf
58 .filter((arr) => arr.includes(url))
59 .reduceRight((p, a) => {
60 p.unshift(a[1]);
61 return p;
62 }, []);
63 };
64 }
65 set(customRoutes = {}) {
66 for (let key in customRoutes) {
67 const path = customRoutes[key];
68 key = addLeadingSlash(key);
69 if (typeof path === 'string') {
70 this.conf.push([key, addLeadingSlash(path)]);
71 }
72 else if ((path === null || path === void 0 ? void 0 : path.length) > 0) {
73 this.conf.push(...path.map(p => [key, addLeadingSlash(p)]));
74 }
75 }
76 }
77}
78const routesAlias = new RoutesAlias();
79
80class RouterConfig {
81 static set config(e) {
82 this.__config = e;
83 }
84 static get config() {
85 return this.__config;
86 }
87 static get pages() {
88 return this.config.pages || [];
89 }
90 static get router() {
91 return this.config.router || {};
92 }
93 static get mode() {
94 return this.router.mode || 'hash';
95 }
96 static get customRoutes() { return this.router.customRoutes || {}; }
97 static isPage(url = '') {
98 return this.pages.findIndex(e => addLeadingSlash(e) === url) !== -1;
99 }
100}
101
102let history;
103let basename = '/';
104class MpaHistory {
105 constructor() {
106 this.back = window.history.back;
107 this.forward = window.history.forward;
108 this.pushState = this.eventState('pushState');
109 this.replaceState = this.eventState('replaceState');
110 }
111 get location() {
112 return {
113 pathname: window.location.pathname,
114 search: window.location.search,
115 hash: window.location.hash,
116 key: `${window.history.length}`,
117 state: window.history.state
118 };
119 }
120 createHref(_to) {
121 throw new Error('Method not implemented.');
122 }
123 parseUrl(to) {
124 let url = to.pathname || '';
125 if (RouterConfig.isPage(url)) {
126 url += '.html';
127 }
128 if (to.search) {
129 url += `?${to.search}`;
130 }
131 if (to.hash) {
132 url += `#${to.hash}`;
133 }
134 return url;
135 }
136 push(to, _state = {}) {
137 window.location.pathname = this.parseUrl(to);
138 // this.pushState(_state, '', this.parseUrl(to))
139 }
140 replace(to, _state = {}) {
141 window.location.replace(this.parseUrl(to));
142 // this.replaceState(_state, '', this.parseUrl(to))
143 }
144 go(delta) {
145 window.history.go(delta);
146 }
147 listen(listener) {
148 function callback(e) {
149 if (e.action === 'pushState') {
150 listener({ action: Action.Push, location: this.location });
151 }
152 else if (e.action === 'replaceState') {
153 listener({ action: Action.Replace, location: this.location });
154 }
155 else {
156 // NOTE: 这里包括 back、forward、go 三种可能,并非是 POP 事件
157 listener({ action: Action.Pop, location: this.location });
158 }
159 }
160 window.addEventListener('popstate', callback);
161 return () => {
162 window.removeEventListener('popstate', callback);
163 };
164 }
165 block(_blocker) {
166 throw new Error('Method not implemented.');
167 }
168 eventState(action) {
169 return (data, unused, url) => {
170 const wrapper = window.history[action](data, unused, url);
171 const evt = new Event(action);
172 evt.action = action;
173 evt.state = data;
174 evt.unused = unused;
175 evt.url = url;
176 window.dispatchEvent(evt);
177 return wrapper;
178 };
179 }
180}
181function setHistoryMode(mode, base = '/') {
182 const options = {
183 window
184 };
185 basename = base;
186 if (mode === 'browser') {
187 history = createBrowserHistory(options);
188 }
189 else if (mode === 'multi') {
190 history = new MpaHistory();
191 }
192 else {
193 // default is hash
194 history = createHashHistory(options);
195 }
196}
197function prependBasename(url = '') {
198 return basename.replace(/\/$/, '') + '/' + url.replace(/^\//, '');
199}
200
201class Stacks {
202 constructor() {
203 this.stacks = [];
204 this.backDelta = 0;
205 }
206 set delta(delta) {
207 if (delta > 0) {
208 this.backDelta = delta;
209 }
210 else if (this.backDelta > 0) {
211 --this.backDelta;
212 }
213 else {
214 this.backDelta = 0;
215 }
216 }
217 get delta() {
218 return this.backDelta;
219 }
220 get length() {
221 return this.stacks.length;
222 }
223 get last() {
224 return this.stacks[this.length - 1];
225 }
226 get() {
227 return this.stacks;
228 }
229 getItem(index) {
230 return this.stacks[index];
231 }
232 getLastIndex(pathname, stateWith = 1) {
233 const list = [...this.stacks].reverse();
234 return list.findIndex((page, i) => { var _a; return i >= stateWith && ((_a = page.path) === null || _a === void 0 ? void 0 : _a.replace(/\?.*/g, '')) === pathname; });
235 }
236 getDelta(pathname) {
237 if (this.backDelta >= 1) {
238 return this.backDelta;
239 }
240 const index = this.getLastIndex(pathname);
241 // NOTE: 此处为了修复浏览器后退多级页面,在大量重复路由状况下可能出现判断错误的情况 (增强判断能力只能考虑在 query 中新增参数来判断,暂时搁置)
242 return index > 0 ? index : 1;
243 }
244 getPrevIndex(pathname, stateWith = 1) {
245 const lastIndex = this.getLastIndex(pathname, stateWith);
246 if (lastIndex < 0) {
247 return -1;
248 }
249 return this.length - 1 - lastIndex;
250 }
251 pop() {
252 return this.stacks.pop();
253 }
254 push(page) {
255 return this.stacks.push(page);
256 }
257}
258const stacks = new Stacks();
259
260function processNavigateUrl(option) {
261 var _a;
262 const pathPieces = parsePath(option.url);
263 // 处理相对路径
264 if ((_a = pathPieces.pathname) === null || _a === void 0 ? void 0 : _a.includes('./')) {
265 const parts = routesAlias.getOrigin(history.location.pathname).split('/');
266 parts.pop();
267 pathPieces.pathname.split('/').forEach((item) => {
268 if (item === '.') {
269 return;
270 }
271 item === '..' ? parts.pop() : parts.push(item);
272 });
273 pathPieces.pathname = parts.join('/');
274 }
275 // 处理自定义路由
276 pathPieces.pathname = routesAlias.getAlias(addLeadingSlash(pathPieces.pathname));
277 // 处理 basename
278 pathPieces.pathname = prependBasename(pathPieces.pathname);
279 // hack fix history v5 bug: https://github.com/remix-run/history/issues/814
280 if (!pathPieces.search)
281 pathPieces.search = '';
282 return pathPieces;
283}
284function navigate(option, method) {
285 return __awaiter(this, void 0, void 0, function* () {
286 return new Promise((resolve, reject) => {
287 const { success, complete, fail } = option;
288 const unListen = history.listen(() => {
289 const res = { errMsg: `${method}:ok` };
290 success === null || success === void 0 ? void 0 : success(res);
291 complete === null || complete === void 0 ? void 0 : complete(res);
292 resolve(res);
293 unListen();
294 });
295 try {
296 if ('url' in option) {
297 const pathPieces = processNavigateUrl(option);
298 const state = { timestamp: Date.now() };
299 if (method === 'navigateTo') {
300 history.push(pathPieces, state);
301 }
302 else if (method === 'redirectTo' || method === 'switchTab') {
303 history.replace(pathPieces, state);
304 }
305 else if (method === 'reLaunch') {
306 stacks.delta = stacks.length;
307 history.replace(pathPieces, state);
308 }
309 }
310 else if (method === 'navigateBack') {
311 stacks.delta = option.delta;
312 history.go(-option.delta);
313 }
314 }
315 catch (error) {
316 const res = { errMsg: `${method}:fail ${error.message || error}` };
317 fail === null || fail === void 0 ? void 0 : fail(res);
318 complete === null || complete === void 0 ? void 0 : complete(res);
319 reject(res);
320 }
321 });
322 });
323}
324function navigateTo(option) {
325 return navigate(option, 'navigateTo');
326}
327function redirectTo(option) {
328 return navigate(option, 'redirectTo');
329}
330function navigateBack(option = { delta: 1 }) {
331 if (!option.delta || option.delta < 1) {
332 option.delta = 1;
333 }
334 return navigate(option, 'navigateBack');
335}
336function switchTab(option) {
337 return navigate(option, 'switchTab');
338}
339function reLaunch(option) {
340 return navigate(option, 'reLaunch');
341}
342function getCurrentPages() {
343 if (process.env.NODE_ENV !== 'production' && RouterConfig.mode === 'multi') {
344 console.warn('多页面路由模式不支持使用 getCurrentPages 方法!');
345 }
346 const pages = stacks.get();
347 return pages.map(e => (Object.assign(Object.assign({}, e), { route: e.path || '' })));
348}
349
350let md;
351let preTitle = document.title;
352let isLoadDdEntry = false;
353function getMobileDetect() {
354 if (!md) {
355 md = new MobileDetect(navigator.userAgent);
356 }
357 return md;
358}
359function setTitle(title) {
360 return __awaiter(this, void 0, void 0, function* () {
361 if (preTitle === title)
362 return title;
363 document.title = title;
364 preTitle = title;
365 if (process.env.SUPPORT_DINGTALK_NAVIGATE !== 'disabled' && isDingTalk()) {
366 if (!isLoadDdEntry) {
367 isLoadDdEntry = true;
368 require('dingtalk-jsapi/platform');
369 }
370 const setDingTitle = require('dingtalk-jsapi/api/biz/navigation/setTitle').default;
371 setDingTitle({ title });
372 }
373 return title;
374 });
375}
376function isDingTalk() {
377 const md = getMobileDetect();
378 return md.match(/DingTalk/ig);
379}
380
381let pageResizeFn;
382function bindPageResize(page) {
383 pageResizeFn && window.removeEventListener('resize', pageResizeFn);
384 pageResizeFn = function () {
385 page.onResize && page.onResize({
386 size: {
387 windowHeight: window.innerHeight,
388 windowWidth: window.innerWidth
389 }
390 });
391 };
392 window.addEventListener('resize', pageResizeFn, false);
393}
394
395const pageScrollFn = {};
396let pageDOM = window;
397function bindPageScroll(page, pageEl, distance = 50) {
398 var _a;
399 const pagePath = (page ? page === null || page === void 0 ? void 0 : page.path : (_a = Current.router) === null || _a === void 0 ? void 0 : _a.path);
400 pageScrollFn[pagePath] && pageEl.removeEventListener('scroll', pageScrollFn[pagePath]);
401 pageDOM = pageEl;
402 let isReachBottom = false;
403 pageScrollFn[pagePath] = function () {
404 var _a;
405 (_a = page.onPageScroll) === null || _a === void 0 ? void 0 : _a.call(page, {
406 scrollTop: pageDOM instanceof Window ? window.scrollY : pageDOM.scrollTop
407 });
408 if (isReachBottom && getOffset() > distance) {
409 isReachBottom = false;
410 }
411 if (page.onReachBottom &&
412 !isReachBottom &&
413 getOffset() < distance) {
414 isReachBottom = true;
415 page.onReachBottom();
416 }
417 };
418 pageDOM.addEventListener('scroll', pageScrollFn[pagePath], false);
419}
420function getOffset() {
421 if (pageDOM instanceof Window) {
422 return document.documentElement.scrollHeight - window.scrollY - window.innerHeight;
423 }
424 else {
425 return pageDOM.scrollHeight - pageDOM.scrollTop - pageDOM.clientHeight;
426 }
427}
428
429// @ts-nocheck
430function initTabbar(config) {
431 if (config.tabBar == null) {
432 return;
433 }
434 // TODO: custom-tab-bar
435 const tabbar = document.createElement('taro-tabbar');
436 const homePage = config.entryPagePath || (config.pages ? config.pages[0] : '');
437 tabbar.conf = config.tabBar;
438 tabbar.conf.homePage = history.location.pathname === '/' ? homePage : history.location.pathname;
439 const routerConfig = config.router;
440 tabbar.conf.mode = routerConfig && routerConfig.mode ? routerConfig.mode : 'hash';
441 if (routerConfig.customRoutes) {
442 tabbar.conf.custom = true;
443 tabbar.conf.customRoutes = routerConfig.customRoutes;
444 }
445 else {
446 tabbar.conf.custom = false;
447 tabbar.conf.customRoutes = {};
448 }
449 if (typeof routerConfig.basename !== 'undefined') {
450 tabbar.conf.basename = routerConfig.basename;
451 }
452 const container = document.getElementById('container');
453 container === null || container === void 0 ? void 0 : container.appendChild(tabbar);
454 initTabBarApis(config);
455}
456
457/* eslint-disable dot-notation */
458class MultiPageHandler {
459 constructor(config) {
460 this.config = config;
461 this.mount();
462 }
463 get appId() { return this.config.appId || 'app'; }
464 get router() { return this.config.router || {}; }
465 get routerMode() { return this.router.mode || 'hash'; }
466 get customRoutes() { return this.router.customRoutes || {}; }
467 get tabBarList() { var _a; return ((_a = this.config.tabBar) === null || _a === void 0 ? void 0 : _a.list) || []; }
468 get PullDownRefresh() { return this.config.PullDownRefresh; }
469 set pathname(p) { this.router.pathname = p; }
470 get pathname() { return this.router.pathname; }
471 get basename() { return this.router.basename || ''; }
472 get pageConfig() { return this.config.route; }
473 get isTabBar() {
474 var _a;
475 const routePath = addLeadingSlash(stripBasename(this.pathname, this.basename));
476 const pagePath = ((_a = Object.entries(this.customRoutes).find(([, target]) => {
477 if (typeof target === 'string') {
478 return target === routePath;
479 }
480 else if ((target === null || target === void 0 ? void 0 : target.length) > 0) {
481 return target.includes(routePath);
482 }
483 return false;
484 })) === null || _a === void 0 ? void 0 : _a[0]) || routePath;
485 return !!pagePath && this.tabBarList.some(t => t.pagePath === pagePath);
486 }
487 get search() { return location.search.substr(1); }
488 getQuery(search = '', options = {}) {
489 search = search ? `${search}&${this.search}` : this.search;
490 const query = search
491 ? queryString.parse(search)
492 : {};
493 return Object.assign(Object.assign({}, query), options);
494 }
495 mount() {
496 setHistoryMode(this.routerMode, this.router.basename);
497 const appId = this.appId;
498 let app = document.getElementById(appId);
499 if (!app) {
500 app = document.createElement('div');
501 app.id = appId;
502 }
503 app.classList.add('taro_router');
504 if (this.tabBarList.length > 1) {
505 const container = document.createElement('div');
506 container.classList.add('taro-tabbar__container');
507 container.id = 'container';
508 const panel = document.createElement('div');
509 panel.classList.add('taro-tabbar__panel');
510 panel.appendChild(app);
511 container.appendChild(panel);
512 document.body.appendChild(container);
513 initTabbar(this.config);
514 }
515 else {
516 document.body.appendChild(app);
517 }
518 }
519 onReady(page, onLoad = true) {
520 var _a;
521 const pageEl = this.getPageContainer(page);
522 if (pageEl && !(pageEl === null || pageEl === void 0 ? void 0 : pageEl['__isReady'])) {
523 const el = pageEl.firstElementChild;
524 (_a = el === null || el === void 0 ? void 0 : el['componentOnReady']) === null || _a === void 0 ? void 0 : _a.call(el);
525 onLoad && (pageEl['__page'] = page);
526 }
527 }
528 load(page, pageConfig = {}) {
529 var _a;
530 if (!page)
531 return;
532 (_a = page.onLoad) === null || _a === void 0 ? void 0 : _a.call(page, this.getQuery('', page.options), () => {
533 var _a;
534 const pageEl = this.getPageContainer(page);
535 this.isTabBar && (pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.add('taro_tabbar_page'));
536 this.onReady(page, true);
537 (_a = page.onShow) === null || _a === void 0 ? void 0 : _a.call(page);
538 this.bindPageEvents(page, pageEl, pageConfig);
539 });
540 }
541 getPageContainer(page) {
542 var _a;
543 const path = page ? page === null || page === void 0 ? void 0 : page.path : (_a = Current.page) === null || _a === void 0 ? void 0 : _a.path;
544 const id = path === null || path === void 0 ? void 0 : path.replace(/([^a-z0-9\u00a0-\uffff_-])/ig, '\\$1');
545 if (page) {
546 return document.querySelector(`.taro_page#${id}`);
547 }
548 const el = (id
549 ? document.querySelector(`.taro_page#${id}`)
550 : document.querySelector('.taro_page') ||
551 document.querySelector('.taro_router'));
552 return el || window;
553 }
554 bindPageEvents(page, pageEl, config = {}) {
555 var _a;
556 if (!pageEl) {
557 pageEl = this.getPageContainer();
558 }
559 const distance = config.onReachBottomDistance || ((_a = this.config.window) === null || _a === void 0 ? void 0 : _a.onReachBottomDistance) || 50;
560 bindPageScroll(page, pageEl, distance);
561 bindPageResize(page);
562 }
563}
564
565// TODO 支持多路由 (APP 生命周期仅触发一次)
566/** Note: 关于多页面应用
567 * - 需要配置路由映射(根目录跳转、404 页面……)
568 * - app.onPageNotFound 事件不支持
569 * - 应用生命周期可能多次触发
570 * - TabBar 会多次加载
571 * - 不支持路由动画
572 */
573function createMultiRouter(app, config, framework) {
574 var _a, _b, _c, _d, _e, _f;
575 return __awaiter(this, void 0, void 0, function* () {
576 RouterConfig.config = config;
577 const handler = new MultiPageHandler(config);
578 const launchParam = {
579 path: config.pageName,
580 query: handler.getQuery(),
581 scene: 0,
582 shareTicket: '',
583 referrerInfo: {}
584 };
585 eventCenter.trigger('__taroRouterLaunch', launchParam);
586 (_a = app.onLaunch) === null || _a === void 0 ? void 0 : _a.call(app, launchParam);
587 app.onError && window.addEventListener('error', e => { var _a; return (_a = app.onError) === null || _a === void 0 ? void 0 : _a.call(app, e.message); });
588 const pathName = config.pageName;
589 const pageConfig = handler.pageConfig;
590 eventCenter.trigger('__taroRouterChange', {
591 toLocation: {
592 path: pathName
593 }
594 });
595 let element;
596 try {
597 element = yield ((_b = pageConfig.load) === null || _b === void 0 ? void 0 : _b.call(pageConfig));
598 if (element instanceof Array) {
599 element = element[0];
600 }
601 }
602 catch (error) {
603 throw new Error(error);
604 }
605 if (!element)
606 return;
607 let enablePullDownRefresh = ((_c = config === null || config === void 0 ? void 0 : config.window) === null || _c === void 0 ? void 0 : _c.enablePullDownRefresh) || false;
608 if (pageConfig) {
609 setTitle((_d = pageConfig.navigationBarTitleText) !== null && _d !== void 0 ? _d : document.title);
610 if (typeof pageConfig.enablePullDownRefresh === 'boolean') {
611 enablePullDownRefresh = pageConfig.enablePullDownRefresh;
612 }
613 }
614 const el = (_e = element.default) !== null && _e !== void 0 ? _e : element;
615 const loadConfig = Object.assign({}, pageConfig);
616 delete loadConfig['path'];
617 delete loadConfig['load'];
618 const page = createPageConfig(enablePullDownRefresh ? hooks.call('createPullDownComponent', el, location.pathname, framework, config.PullDownRefresh) : el, pathName + stringify(launchParam), {}, loadConfig);
619 handler.load(page, pageConfig);
620 (_f = app.onShow) === null || _f === void 0 ? void 0 : _f.call(app, launchParam);
621 });
622}
623
624/**
625 * 插入页面动画需要的样式
626 */
627function loadAnimateStyle(ms = 300) {
628 const css = `
629.taro_router .taro_page {
630 position: absolute;
631 left: 0;
632 top: 0;
633 width: 100%;
634 height: 100%;
635 background-color: #fff;
636 transform: translate(100%, 0);
637 transition: transform ${ms}ms;
638 z-index: 0;
639}
640
641.taro_router .taro_page.taro_tabbar_page,
642.taro_router .taro_page.taro_page_show.taro_page_stationed {
643 transform: none;
644}
645
646.taro_router .taro_page.taro_page_show {
647 transform: translate(0, 0);
648}`;
649 const style = document.createElement('style');
650 style.innerHTML = css;
651 document.getElementsByTagName('head')[0].appendChild(style);
652}
653
654/* eslint-disable dot-notation */
655function setDisplay(el, type = '') {
656 if (el) {
657 el.style.display = type;
658 }
659}
660class PageHandler {
661 constructor(config) {
662 this.defaultAnimation = { duration: 300, delay: 50 };
663 this.config = config;
664 this.homePage = this.getHomePage();
665 this.mount();
666 }
667 get appId() { return this.config.appId || 'app'; }
668 get router() { return this.config.router || {}; }
669 get routerMode() { return this.router.mode || 'hash'; }
670 get customRoutes() { return this.router.customRoutes || {}; }
671 get routes() { return this.config.routes || []; }
672 get tabBarList() { var _a; return ((_a = this.config.tabBar) === null || _a === void 0 ? void 0 : _a.list) || []; }
673 get PullDownRefresh() { return this.config.PullDownRefresh; }
674 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; }
675 get animationDelay() {
676 var _a;
677 return (typeof this.animation === 'object'
678 ? this.animation.delay
679 : this.animation
680 ? (_a = this.defaultAnimation) === null || _a === void 0 ? void 0 : _a.delay
681 : 0) || 0;
682 }
683 get animationDuration() {
684 var _a;
685 return (typeof this.animation === 'object'
686 ? this.animation.duration
687 : this.animation
688 ? (_a = this.defaultAnimation) === null || _a === void 0 ? void 0 : _a.duration
689 : 0) || 0;
690 }
691 set pathname(p) { this.router.pathname = p; }
692 get pathname() { return this.router.pathname; }
693 get basename() { return this.router.basename || ''; }
694 get pageConfig() {
695 const routePath = addLeadingSlash(stripBasename(this.pathname, this.basename));
696 const homePage = addLeadingSlash(this.homePage);
697 return this.routes.find(r => {
698 var _a;
699 const pagePath = addLeadingSlash(r.path);
700 return [pagePath, homePage].includes(routePath) || ((_a = routesAlias.getConfig(pagePath)) === null || _a === void 0 ? void 0 : _a.includes(routePath));
701 });
702 }
703 get isTabBar() {
704 var _a;
705 const routePath = addLeadingSlash(stripBasename(this.pathname, this.basename));
706 const pagePath = ((_a = Object.entries(this.customRoutes).find(([, target]) => {
707 if (typeof target === 'string') {
708 return target === routePath;
709 }
710 else if ((target === null || target === void 0 ? void 0 : target.length) > 0) {
711 return target.includes(routePath);
712 }
713 return false;
714 })) === null || _a === void 0 ? void 0 : _a[0]) || routePath;
715 return !!pagePath && this.tabBarList.some(t => stripTrailing(t.pagePath) === pagePath);
716 }
717 getHomePage() {
718 var _a;
719 const routePath = addLeadingSlash(stripBasename(this.routes[0].path, this.basename));
720 const alias = ((_a = Object.entries(this.customRoutes).find(([key]) => key === routePath)) === null || _a === void 0 ? void 0 : _a[1]) || routePath;
721 return this.config.entryPagePath || (typeof alias === 'string' ? alias : alias[0]) || this.basename;
722 }
723 isSamePage(page) {
724 const routePath = stripBasename(this.pathname, this.basename);
725 const pagePath = stripBasename(page === null || page === void 0 ? void 0 : page.path, this.basename);
726 return pagePath.startsWith(routePath + '?');
727 }
728 get search() {
729 let search = '?';
730 if (this.routerMode === 'hash') {
731 const idx = location.hash.indexOf('?');
732 if (idx > -1) {
733 search = location.hash.slice(idx);
734 }
735 }
736 else {
737 search = location.search;
738 }
739 return search.substr(1);
740 }
741 getQuery(stamp = 0, search = '', options = {}) {
742 search = search ? `${search}&${this.search}` : this.search;
743 const query = search
744 ? queryString.parse(search, { decode: false })
745 : {};
746 query.stamp = stamp.toString();
747 return Object.assign(Object.assign({}, query), options);
748 }
749 mount() {
750 setHistoryMode(this.routerMode, this.router.basename);
751 this.animation && loadAnimateStyle(this.animationDuration);
752 const appId = this.appId;
753 let app = document.getElementById(appId);
754 if (!app) {
755 app = document.createElement('div');
756 app.id = appId;
757 }
758 app.classList.add('taro_router');
759 if (this.tabBarList.length > 1) {
760 const container = document.createElement('div');
761 container.classList.add('taro-tabbar__container');
762 container.id = 'container';
763 const panel = document.createElement('div');
764 panel.classList.add('taro-tabbar__panel');
765 panel.appendChild(app);
766 container.appendChild(panel);
767 document.body.appendChild(container);
768 initTabbar(this.config);
769 }
770 else {
771 document.body.appendChild(app);
772 }
773 }
774 onReady(page, onLoad = true) {
775 var _a, _b;
776 const pageEl = this.getPageContainer(page);
777 if (pageEl && !(pageEl === null || pageEl === void 0 ? void 0 : pageEl['__isReady'])) {
778 const el = pageEl.firstElementChild;
779 (_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(() => {
780 requestAnimationFrame(() => {
781 var _a;
782 (_a = page.onReady) === null || _a === void 0 ? void 0 : _a.call(page);
783 pageEl['__isReady'] = true;
784 });
785 });
786 onLoad && (pageEl['__page'] = page);
787 }
788 }
789 load(page, pageConfig = {}, stacksIndex = 0) {
790 var _a, _b;
791 if (!page)
792 return;
793 // NOTE: 页面栈推入太晚可能导致 getCurrentPages 无法获取到当前页面实例
794 stacks.push(page);
795 const param = this.getQuery(stacks.length, '', page.options);
796 let pageEl = this.getPageContainer(page);
797 if (pageEl) {
798 setDisplay(pageEl);
799 this.isTabBar && pageEl.classList.add('taro_tabbar_page');
800 this.addAnimation(pageEl, stacksIndex === 0);
801 (_a = page.onShow) === null || _a === void 0 ? void 0 : _a.call(page);
802 this.bindPageEvents(page, pageEl, pageConfig);
803 }
804 else {
805 (_b = page.onLoad) === null || _b === void 0 ? void 0 : _b.call(page, param, () => {
806 var _a;
807 pageEl = this.getPageContainer(page);
808 this.isTabBar && (pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.add('taro_tabbar_page'));
809 this.addAnimation(pageEl, stacksIndex === 0);
810 this.onReady(page, true);
811 (_a = page.onShow) === null || _a === void 0 ? void 0 : _a.call(page);
812 this.bindPageEvents(page, pageEl, pageConfig);
813 });
814 }
815 }
816 unload(page, delta = 1, top = false) {
817 var _a, _b, _c;
818 if (!page)
819 return;
820 stacks.delta = --delta;
821 stacks.pop();
822 if (this.animation && top) {
823 if (this.unloadTimer) {
824 clearTimeout(this.unloadTimer);
825 (_b = (_a = this.lastUnloadPage) === null || _a === void 0 ? void 0 : _a.onUnload) === null || _b === void 0 ? void 0 : _b.call(_a);
826 this.unloadTimer = null;
827 }
828 this.lastUnloadPage = page;
829 const pageEl = this.getPageContainer(page);
830 pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.remove('taro_page_stationed');
831 pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.remove('taro_page_show');
832 this.unloadTimer = setTimeout(() => {
833 var _a, _b;
834 this.unloadTimer = null;
835 (_b = (_a = this.lastUnloadPage) === null || _a === void 0 ? void 0 : _a.onUnload) === null || _b === void 0 ? void 0 : _b.call(_a);
836 }, this.animationDuration);
837 }
838 else {
839 const pageEl = this.getPageContainer(page);
840 pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.remove('taro_page_stationed');
841 pageEl === null || pageEl === void 0 ? void 0 : pageEl.classList.remove('taro_page_show');
842 (_c = page === null || page === void 0 ? void 0 : page.onUnload) === null || _c === void 0 ? void 0 : _c.call(page);
843 }
844 if (delta >= 1)
845 this.unload(stacks.last, delta);
846 }
847 show(page, pageConfig = {}, stacksIndex = 0) {
848 var _a, _b;
849 if (!page)
850 return;
851 const param = this.getQuery(stacks.length, '', page.options);
852 let pageEl = this.getPageContainer(page);
853 if (pageEl) {
854 setDisplay(pageEl);
855 this.addAnimation(pageEl, stacksIndex === 0);
856 (_a = page.onShow) === null || _a === void 0 ? void 0 : _a.call(page);
857 this.bindPageEvents(page, pageEl, pageConfig);
858 }
859 else {
860 (_b = page.onLoad) === null || _b === void 0 ? void 0 : _b.call(page, param, () => {
861 var _a;
862 pageEl = this.getPageContainer(page);
863 this.addAnimation(pageEl, stacksIndex === 0);
864 this.onReady(page, false);
865 (_a = page.onShow) === null || _a === void 0 ? void 0 : _a.call(page);
866 this.bindPageEvents(page, pageEl, pageConfig);
867 });
868 }
869 }
870 hide(page) {
871 var _a;
872 if (!page)
873 return;
874 // NOTE: 修复多页并发问题,此处可能因为路由跳转过快,执行时页面可能还没有创建成功
875 const pageEl = this.getPageContainer(page);
876 if (pageEl) {
877 if (this.hideTimer) {
878 clearTimeout(this.hideTimer);
879 this.hideTimer = null;
880 setDisplay(this.lastHidePage, 'none');
881 }
882 this.lastHidePage = pageEl;
883 this.hideTimer = setTimeout(() => {
884 this.hideTimer = null;
885 setDisplay(this.lastHidePage, 'none');
886 }, this.animationDuration + this.animationDelay);
887 (_a = page.onHide) === null || _a === void 0 ? void 0 : _a.call(page);
888 }
889 else {
890 setTimeout(() => this.hide(page), 0);
891 }
892 }
893 addAnimation(pageEl, first = false) {
894 if (!pageEl)
895 return;
896 if (this.animation && !first) {
897 setTimeout(() => {
898 pageEl.classList.add('taro_page_show');
899 setTimeout(() => {
900 pageEl.classList.add('taro_page_stationed');
901 }, this.animationDuration);
902 }, this.animationDelay);
903 }
904 else {
905 pageEl.classList.add('taro_page_show');
906 pageEl.classList.add('taro_page_stationed');
907 }
908 }
909 getPageContainer(page) {
910 var _a;
911 const path = page ? page === null || page === void 0 ? void 0 : page.path : (_a = Current.page) === null || _a === void 0 ? void 0 : _a.path;
912 const id = path === null || path === void 0 ? void 0 : path.replace(/([^a-z0-9\u00a0-\uffff_-])/ig, '\\$1');
913 if (page) {
914 return document.querySelector(`.taro_page#${id}`);
915 }
916 const el = (id
917 ? document.querySelector(`.taro_page#${id}`)
918 : document.querySelector('.taro_page') ||
919 document.querySelector('.taro_router'));
920 return el || window;
921 }
922 bindPageEvents(page, pageEl, config = {}) {
923 var _a;
924 if (!pageEl) {
925 pageEl = this.getPageContainer();
926 }
927 const distance = config.onReachBottomDistance || ((_a = this.config.window) === null || _a === void 0 ? void 0 : _a.onReachBottomDistance) || 50;
928 bindPageScroll(page, pageEl, distance);
929 bindPageResize(page);
930 }
931}
932
933function createRouter(app, config, framework) {
934 var _a, _b;
935 RouterConfig.config = config;
936 const handler = new PageHandler(config);
937 routesAlias.set(handler.router.customRoutes);
938 const basename = handler.router.basename;
939 const routes = handler.routes.map(route => {
940 const routePath = addLeadingSlash(route.path);
941 const paths = routesAlias.getAll(routePath);
942 return {
943 path: paths.length < 1 ? routePath : paths,
944 action: route.load
945 };
946 });
947 const router = new UniversalRouter(routes, { baseUrl: basename || '' });
948 const launchParam = {
949 path: handler.homePage,
950 query: handler.getQuery(stacks.length),
951 scene: 0,
952 shareTicket: '',
953 referrerInfo: {}
954 };
955 eventCenter.trigger('__taroRouterLaunch', launchParam);
956 (_a = app.onLaunch) === null || _a === void 0 ? void 0 : _a.call(app, launchParam);
957 app.onError && window.addEventListener('error', e => { var _a; return (_a = app.onError) === null || _a === void 0 ? void 0 : _a.call(app, e.message); });
958 const render = ({ location, action }) => __awaiter(this, void 0, void 0, function* () {
959 var _c, _d, _e, _f, _g;
960 handler.pathname = decodeURI(location.pathname);
961 eventCenter.trigger('__taroRouterChange', {
962 toLocation: {
963 path: handler.pathname
964 }
965 });
966 let element, params;
967 try {
968 const result = yield router.resolve(handler.router.forcePath || handler.pathname);
969 [element, , params] = yield Promise.all(result);
970 }
971 catch (error) {
972 if (error.status === 404) {
973 (_c = app.onPageNotFound) === null || _c === void 0 ? void 0 : _c.call(app, {
974 path: handler.pathname
975 });
976 }
977 else if (/Loading hot update .* failed./.test(error.message)) {
978 // NOTE: webpack5 与 prebundle 搭配使用时,开发环境下初次启动时偶发错误,由于 HMR 加载 chunk hash 错误,导致热更新失败
979 window.location.reload();
980 }
981 else {
982 throw new Error(error);
983 }
984 }
985 if (!element)
986 return;
987 const pageConfig = handler.pageConfig;
988 let enablePullDownRefresh = ((_d = config === null || config === void 0 ? void 0 : config.window) === null || _d === void 0 ? void 0 : _d.enablePullDownRefresh) || false;
989 if (pageConfig) {
990 document.title = (_e = pageConfig.navigationBarTitleText) !== null && _e !== void 0 ? _e : document.title;
991 setTitle((_f = pageConfig.navigationBarTitleText) !== null && _f !== void 0 ? _f : document.title);
992 if (typeof pageConfig.enablePullDownRefresh === 'boolean') {
993 enablePullDownRefresh = pageConfig.enablePullDownRefresh;
994 }
995 }
996 const currentPage = Current.page;
997 const pathname = handler.pathname;
998 let shouldLoad = false;
999 if (action === 'POP') {
1000 // NOTE: 浏览器事件退后多次时,该事件只会被触发一次
1001 const prevIndex = stacks.getPrevIndex(pathname);
1002 const delta = stacks.getDelta(pathname);
1003 // NOTE: Safari 内核浏览器在非应用页面返回上一页时,会触发额外的 POP 事件,此处需避免当前页面被错误卸载
1004 if (currentPage !== stacks.getItem(prevIndex)) {
1005 handler.unload(currentPage, delta, prevIndex > -1);
1006 if (prevIndex > -1) {
1007 handler.show(stacks.getItem(prevIndex), pageConfig, prevIndex);
1008 }
1009 else {
1010 shouldLoad = true;
1011 }
1012 }
1013 }
1014 else {
1015 if (handler.isTabBar) {
1016 if (handler.isSamePage(currentPage))
1017 return;
1018 const prevIndex = stacks.getPrevIndex(pathname, 0);
1019 handler.hide(currentPage);
1020 if (prevIndex > -1) {
1021 // NOTE: tabbar 页且之前出现过,直接复用
1022 return handler.show(stacks.getItem(prevIndex), pageConfig, prevIndex);
1023 }
1024 }
1025 else if (action === 'REPLACE') {
1026 const delta = stacks.getDelta(pathname);
1027 // NOTE: 页面路由记录并不会清空,只是移除掉缓存的 stack 以及页面
1028 handler.unload(currentPage, delta);
1029 }
1030 else if (action === 'PUSH') {
1031 handler.hide(currentPage);
1032 }
1033 shouldLoad = true;
1034 }
1035 if (shouldLoad || stacks.length < 1) {
1036 const el = (_g = element.default) !== null && _g !== void 0 ? _g : element;
1037 const loadConfig = Object.assign({}, pageConfig);
1038 const stacksIndex = stacks.length;
1039 delete loadConfig['path'];
1040 delete loadConfig['load'];
1041 const page = createPageConfig(enablePullDownRefresh ? hooks.call('createPullDownComponent', el, location.pathname, framework, handler.PullDownRefresh) : el, pathname + stringify(handler.getQuery(stacksIndex)), {}, loadConfig);
1042 if (params)
1043 page.options = params;
1044 return handler.load(page, pageConfig, stacksIndex);
1045 }
1046 });
1047 const routePath = addLeadingSlash(stripBasename(history.location.pathname, handler.basename));
1048 if (routePath === '/') {
1049 history.replace(prependBasename(handler.homePage + history.location.search));
1050 }
1051 render({ location: history.location, action: Action.Push });
1052 (_b = app.onShow) === null || _b === void 0 ? void 0 : _b.call(app, launchParam);
1053 return history.listen(render);
1054}
1055
1056export { createMultiRouter, createRouter, getCurrentPages, history, navigateBack, navigateTo, reLaunch, redirectTo, switchTab };
1057//# sourceMappingURL=index.esm.js.map