UNPKG

vimo

Version:

A Vue2.x UI Project For Mobile & HyBrid

226 lines (207 loc) 6.91 kB
/** * @class History * @classdesc 通过vue-router的onRouteChangeBefore事件构建本地历史记录 * * ## 问题 * * 单页应用的一个需求是需要知道路由切换是前进还是后退, 但是浏览器对路由切换只给了两个事件 `hashchange` 和 `popstate`, 故无从判断当前操作是后退还是前进. * * ## 解决方案 * * 这个类通过vue-router的onRouteChangeBefore事件构建本地历史记录. 当路由切换时, 内建历史记录数组, 类似于一个stack, 这个能正确反映当前app的浏览历史记录. * * 完成的功能如下: * * - 内建导航记录 * - 此History是对router实例的拓展, 但是不会为router实例添加方法, 而是重新定义$history, 这个可在业务的this中访问到 */ export class History { constructor (router, config, platform) { this._history = [] // 存储当前导航的历史记录, 内容为 route object(路由信息对象) this._direction = 'forward' // forward/backward this._router = router // vue-router实例 this._config = config // config 实例 this._platform = platform // platform 实例 this.isReplace = false // 路由跳转是否是使用replace方法 this.usePushWindow = this._config.getBoolean('usePushWindow', false) // 支付宝 和 钉钉 两个模式下路由跳转是否开启新页面 // 监听路由变化, 维护本地历史记录 // 路由切换前 if (this._router) { const _this = this /** * 方法增强: replace() * */ let replaceCopy = this._router.replace replaceCopy = replaceCopy.bind(this._router) this._router.replace = function () { let args = Array.from(arguments) let to = _this._router.resolve(args[0]).resolved let from = _this._history[_this.length - 1] if (to.fullPath !== from.fullPath || to.name !== from.name) { // replace时, 前后地址不一样的话才处理 _this.isReplace = true replaceCopy.apply(null, arguments) } } if (this.usePushWindow) { /** * 方法增强: back() * */ let backCopy = this._router.back backCopy = backCopy.bind(this._router) this._router.back = function () { let isHandled = _this._platform.popWindow && _this._platform.popWindow() if (!isHandled) { backCopy.apply(null, arguments) } } /** * 方法增强: go() * */ let goCopy = this._router.go goCopy = goCopy.bind(this._router) this._router.go = function (n) { let isHandled = _this._platform.popTo && _this._platform.popTo(n) if (!isHandled) { goCopy.apply(null, arguments) } } } this._router.beforeEach((to, from, next) => { // 如果使用了replace, 则跳过当前拦截的路由信息, 并且将最后一个重置 if (this.isReplace) { this.isReplace = false this._history.pop() this._history.push(to) next() } else if (this.length <= 1) { /** * 当本地维护的历时记录为空或, 意味着页面为首次进入, 并未初始化, * 此时, 可能我们是从app中的某个页面进入的, * 因此, 需要判断下history.length, 此时, 不显示back按钮 * * 同理, length=1也同样处理 * */ this._pushHistory({ to, from, next }) } else { // 向记录后方追溯, 如果有匹配可认为是go(-n)操作, 否则就是push操作 for (let i = this.length - 2; i > -1; i--) { let _previous = this._history[i] if (to.name === _previous.name) { this._popHistory(next, i) return } } // 如果不在过去的历史记录, 则认为是新增加记录 this._pushHistory({ to, from, next }) } }) } } get length () { return this._history.length } // -------- private -------- /** * push to history * @private * */ _pushHistory ({ to, from, next }) { if (to.fullPath === from.fullPath && to.name === from.name) { // 同地址同名称跳转不记录不处理 return } let isHandled = false if ( this.usePushWindow && from.matched.length !== 0 && to.matched.length !== 0 ) { let url = '' let mode = this._router.mode if (mode === 'hash') { url = `${window.location.origin}${window.location.pathname}${window .location.search}#${to.fullPath}` } else { console.error('history.js::只支持 mode: "hash"') } isHandled = this._platform.pushWindow && this._platform.pushWindow(url) } if (!isHandled) { // fallback this._direction = 'forward' this._history.push(to) next() } } /** * pop history record * @private * */ _popHistory (next, i = 0) { // 激活了浏览器的后退,这里只需要更新状态 this._direction = 'backward' this._history = this._history.splice(0, i + 1) next() } /** * 获取当前的页面进行的方向 * 只能是这两个值: forward || backward * @return {string} * */ getDirection () { return this._direction } /** * 判断是否能返回 * @return {Boolean} * */ canGoBack () { return this.length > 1 } /** * 获取当前的导航记录 * @return {Array} * */ getHistory () { return this._history } /** * 返回root页面(在路由信息中标示```route.meta.root=true```的页面) * @example * { * path: '/', * name: 'index', * meta: { * root: true * }, * component: require('@/pages/index.vue') * } * */ toRoot () { // 支付宝方式返回首页 let isHandled = this._platform.popToRoot && this._platform.popToRoot() if (!isHandled) { if (this._router.options.routes) { let routes = this._router.options.routes for (let i = 0, len = routes.length; len > i; i++) { let route = routes[i] if (route && route.meta && route.meta.root) { console.log(route) this._direction = 'backward' this._router.replace(route) this._history = [] return } } } if (!isHandled) { this._router.go(1 - this.length) } } } } export function setupHistory (router, config, platform) { return new History(router, config, platform) }