'use strict'; import { BaseHistory, HistoryOptionString, HistoryOption } from 'base-history'; import { BaseLog } from 'base-log'; import { hasHost,parseUrl,stringifyUrl } from 'urlUtils'; import * as routeUtils from './routeUtils'; import * as native from 'web/service/native'; const log = new BaseLog('History',true); const isInWebview = native.isInWebview(); export const historyOptions = { replace: { replace:true }, webview: { webview: true }, origin: { origin: true }, tab: { tab: true } } export interface Config { visit router? baseUrl? } let config; export class History implements BaseHistory{ native = native constructor() { } // == 初始化加载路由跳转配置 static init = (conf: Config) => { config = conf; config.baseUrl = config.baseUrl || window.location.origin; } // == 监听路由变化并更新访问记录 static listen = (path) => { // 未自定义router则不做处理 if(!config.router) return; const visit = config.visit; const addListen = () => { const index = visit.findIndex(path); if(index === -1){ visit.add(path); } else { const len = visit.count(); const step = index - len + 1; visit.remove(step); } } if(visit.isReady){ addListen(); }else{ visit.ready(() => { addListen(); }); } } // == 通用跳转方法 go = (url, params?, option?) => { if(config && !config.router) console.log('未定义router,使用浏览器默认跳转!'); option = this.filterOption(option); const isHasHost = hasHost(url); const isMatchBaseUrl = this.isMatchBaseUrl(url); const isInnerHost = isHasHost && isMatchBaseUrl; // 内部host const isOuterHost = isHasHost && !isMatchBaseUrl; // 外部host // webview则直接交由原生app if(isInWebview) return this.nativeGo(url, params, option); // 外部host使用原始跳转 if(isOuterHost) return this.originGo(url, params, option); // 获取url对应路由 url = isInnerHost ? url.replace(config.baseUrl, '') : url; const route = routeUtils.get(url); // 路由不存在也使用原始跳转 if(!route) { const targetUrl = config.baseUrl + url; return this.originGo(targetUrl, params, option); } // 先合并为全路径,然后再解析确保parms和query参数的完整性 const pattern = route.url; const fullUrl = stringifyUrl(url, params, pattern); const urlObj = parseUrl(fullUrl, pattern); const path = urlObj.path; const query = urlObj.query; params = urlObj.params; const webPath = stringifyUrl(path, params, pattern); option.tab = 'tab' in option ? option.tab : !!route.tab; return this.goTo(webPath, query, option); } // == 执行跳转 goTo = (path, query, option) => { option = this.filterOption(option); const historyIndex = this._indexOf(path); const maxIndex = this.length() - 1; log.info('historyIndex', historyIndex); log.info('maxIndex', maxIndex); log.info('option',option); // 当前路由堆栈不存在 const notExist = maxIndex == -1; // 当前路由堆栈即将不存在 const willNotExist = maxIndex == 0 && option.replace; // 是否有页面索引 const hasIndex = historyIndex > -1; // 要跳转的页面索引与当前页面索引相同 const hasSameIndex = hasIndex && maxIndex == historyIndex; // 索引相同且replace为false则不作处理 if(hasSameIndex && !option.replace) return Promise.resolve(); // 路由堆栈为空或者即将为空则setRoot if(notExist || willNotExist) { return this.setRoot(path, query); } if(option.tab){ return this.tabTo(path, query); }else if(option.replace){ return this.replaceTo(path, query); }else{ return hasIndex ? this.popTo(historyIndex) : this.push(path, query); } } // == 原生跳转 nativeGo = (url, toParams, option) => { native.history.go(url, toParams, option); } // == 原始跳转 originGo = (url, params?, option?) => { option = option || {}; if(typeof url === 'number') { window.history.go(url); }else if(option.replace){ url = stringifyUrl(url,params); window.location.replace(url); }else{ url = stringifyUrl(url,params); window.location.href = url; } }; // 设置首页 setRoot = (path, query?) => { return this.push(path, query); } // 跳转tab页 tabTo = (path, query?) => { return this.push(path, query); } push = (path, query?) => { return new Promise((resolve, reject) => { if(config.router){ return config.router.push({path, query},() => { resolve(); },() => { reject(); }); }else{ return this.originGo(path, query); } }); } // 替换当前页 replaceTo = (path, query?) => { return new Promise((resolve, reject) => { if(config.router){ return config.router.replace({path, query},() => { resolve(); },() => { reject(); }); }else{ return this.originGo(path, query, {replace:true}); } }); } // 后退到指定页 popTo = (historyIndex) => { const len = this.length(); const step = historyIndex - len + 1; return this.goBack(step); } goBack = (step) => { if(config.router){ return config.router.go(step); }else{ return this.originGo(step); } } // 过滤配置 filterOption = (option: HistoryOptionString | HistoryOption):HistoryOption => { if(typeof option === 'string') { let finalOption = {}; if(option.indexOf('|') > -1){ let types = option.split('|'); for (let type of types) { let typeOption = Object.assign({}, historyOptions[type]); if(typeOption) finalOption = Object.assign({}, finalOption, typeOption); } }else{ finalOption = Object.assign({}, historyOptions[option]); } return finalOption; }else if(typeof option === 'number'){ return { duration: option } }else{ return option || {}; } } // 是否匹配baseUrl isMatchBaseUrl = (url) => { if(!hasHost(url)) return false; const newUrl = url.replace(config.baseUrl,''); // 替换后url如何还带host(即未匹配)则指定打开目标为webview return !hasHost(newUrl); } // == 获取当前路径在堆栈中的索引 _indexOf = (path) => { return config.visit.findIndex(path); } length = () => { return config.visit.count(); } }