UNPKG

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