/** * 路由需要处理以下事情 * 1、兼容(封装)微信路由跳转 * 2、支持params传值跳转 * 3、支持路由栈智能检测,对于已经达到路由栈最大数目的场景进行兼容处理 */ //栈中保存的最大路由数 const MAX = 10; //路由路径集合 const ROUTE_LINE = []; //当前路由的跳转方式 var _routerType = void 0; //元数据集合,以路由为key,保存对应的元数据 const metas = {}; const util = { contains(arr, item, eq) { let len = arr.length; var equal = eq || function (a, b) { return a === b; }; for (let i = 0; i < len; i++) { if (equal(arr[i], item)) { return i; } } return -1; }, /*** * 根据当前页面获取其相对地址的绝对地址 * @param route * @return {*} */ getRealRoute(route) { let pages = getCurrentPages(); let last = ''; if (pages.length > 0) { last = pages[pages.length - 1].__route__; } /** * @param ab_r[string] : 当前绝对地址 * @param r[string]: 相对路径 */ function getRoute(ab_r, r) { ab_r = ab_r || ''; r = r || ''; //取出路径的栈 let paths = ab_r.split('/'); if (r.indexOf('/') === 0) { return r.substr(1); } else if (r.indexOf('./') === 0) { paths.pop(); return paths.join('/') + r.substr(1); } else { if (paths.length > 0) { paths.pop(); } else { throw { message: '路径不合法' }; } if (paths.length > 0) { paths.pop(); } else { throw { message: '路径不合法' }; } let n_ab_r = paths.join('/'); return n_ab_r + '/' + getRoute(n_ab_r, r.substr(2)); } } return getRoute(last, route); }, /*** * 清空对象的属性数据 * @param obj */ clearObject(obj) { if (!obj) { obj = {}; } else if (typeof obj !== 'object' || Array.isArray(obj)) { obj = {}; } else { for (let p in obj) { delete obj[p]; } } }, /*** * 判断给定的参数是否为对象类型 * @param obj */ isObject: function (obj) { return typeof obj === 'object' && !Array.isArray(obj); }, /** * 深拷贝 * @param {*目标对象} t * @param {*源对象} s */ clone(t, s) { const _this = this; for (var p in s) { if (_this.isObject(s[p]) && _this.isObject(t[p])) { clone(t[p], s[p]); } else { //直接赋值 t[p] = s[p]; } } } }; // 小程序bug在调用redirectTo时会有一定几率触发WebviewId not found的错误, 导致页面无响应, 只在android上出现 // 估计是因为页面渲染速度导致的, 所以加个延时做容错 const actions = { //保存当前要页面,跳转到应用中指定的页面 navigateTo(obj) { setTimeout(function() { let pages = getCurrentPages(); //如果当前路由栈已经满了,则关闭当前页面再跳转 if (pages.length >= MAX) { console.log(`当前路由栈数目超过${MAX}个,使用redirect进行路由跳转`); _routerType = global.ROUTER_TYPE.CLOSE_CURRENT; actions.redirectTo(obj); } else { _routerType = global.ROUTER_TYPE.CLOSE_NONE; console.log('navigateTo ' + obj.url); wx.navigateTo({ url: obj.url, fail: function (err) { console.log(err); } }); } }, 100); }, //关闭当前页面,跳转到应用中指定的页面 redirectTo(obj) { setTimeout(function() { let pages = getCurrentPages(); if (pages.length >= 2) { //判断去往的路由是否存在路由中,如果存在则进行退栈,否则进行跳转 let realPath = util.getRealRoute(obj.url.split('?')[0]); console.log('待跳转的绝对地址:' + realPath); if (realPath === pages[pages.length - 2].__route__) { console.log('栈顶地址:' + pages[pages.length - 2].__route__); _routerType = global.ROUTER_TYPE.BACK; wx.navigateBack(); } else { _routerType = global.ROUTER_TYPE.CLOSE_CURRENT; wx.redirectTo({ url: obj.url, fail: function (err) { console.log(err); } }); } } else { _routerType = global.ROUTER_TYPE.CLOSE_CURRENT; wx.redirectTo({ url: obj.url, fail: function (err) { console.log(err); } }); } }, 100); }, //关闭所有页面,跳转到应用中指定的页面 reLaunch(obj, cb) { setTimeout(function() { _routerType = global.ROUTER_TYPE.CLOSE_ALL; wx.reLaunch && wx.reLaunch({ url: obj.url, fail: function (err) { console.log(err); }, complete: function(res) { console.log(res); cb && cb(res); } }); }, 100); }, //关闭当前页面,返回delta层页面数 navigateBack(delta) { setTimeout(function() { _routerType = global.ROUTER_TYPE.BACK; wx.navigateBack({ delta: delta, fail: function (err) { console.log(err); } }); }, 100); }, //关闭其他非tabBar页面,跳转到指定的tabBar页面 switchTab(obj) { setTimeout(function() { wx.switchTab({ url: obj.url, fail: function (err) { console.log(err); } }); }, 100); } }; const assembler = { getQueryStr(q) { if (!q) { return ''; } else { let query = '', first = true; if (typeof q === 'object' && !Array.isArray(q)) { //保存query的值 for (let p in q) { let valType = typeof q[p]; if (valType !== 'string' && valType !== 'number') { throw { message: 'query属性的值必须为数值类型' }; } else { if (first) { query = p + '=' + q[p]; first = false; } else { query = query + '&' + p + '=' + q[p]; } } } return query; } else { throw { message: 'query必须为对象类型' }; } } }, /*** * 保存指定路由的query数据 * @param q */ saveQuery(route, q) { if (!metas[route]) { metas[route] = {}; } else { //util.clearObject(metas[route]) metas[route].query = {}; if (!metas[route].query || typeof metas[route] !== 'object' || Array.isArray(metas[route])) { metas[route].query = {}; } Object.assign(metas[route].query, q); } }, /*** * 将用户设置的数据存储到路由对象中 * @param p */ saveParams(route, p) { if (!metas[route]) { metas[route] = {}; } else { metas[route].params = {}; Object.assign(metas[route].params, p); } }, /*** * 清除指定路由的params数据 */ clearParams(route) { util.clearObject(metas[route]); } }; const router = { /*** * 跳转到应用内指定的页面 * @param obj [object]: 参数对象,必传 * @param obj.path [string]: 跳转的路由地址 * @param obj.type [int]: 1、默认值,navigate类型跳转;2、redirect类型跳转;3、relaunch类型跳转 * @param obj.query [object]: 单层对象,例如{a:3,b:4} * @param obj.params [object]: 对象数据 */ goto(obj, forceRefresh, cb) { //校验obj的数据 if (!obj || !obj.path) { throw { message: '请传入路由路径' }; } else { //获取绝对地址路由,只取query参数之前的路径串 let absRoute = util.getRealRoute(obj.path.split('?')[0]); //获取跳转类型设置 let type = obj.type || global.ROUTER_TYPE.CLOSE_NONE; //如果跳转的目标路由跟当前栈顶的路由相同并且没有强制跳转则不处理 let pages = getCurrentPages(); if (pages[pages.length - 1].route === absRoute && !forceRefresh) { return; } //生成query串 let query = assembler.getQueryStr(obj.query); let url = query === '' ? obj.path : obj.path + '?' + query; //上面的生成queryStr函数已经做过校验 assembler.saveQuery(absRoute, obj.query || {}); //添加params到全局对象 let params = obj.params && typeof obj.params === 'object' ? obj.params : {}; assembler.saveParams(absRoute, params); if (pages && pages.length) { let _page = pages[pages.length - 1]; if (_page) { let _from = _page.route || _page.__route__ || ''; let _to = url; global.tools.memory.setData('PAGE_HIS', { from: _from, to: _to }); } } //路由跳转 console.log('router:' + url, type); if (type === 2) { actions.redirectTo({ url: url }); } else if (type === 3) { actions.reLaunch({ url: url }, cb); } else if (type === 4) { actions.navigateBack(obj.delta || 1); } else if (type === 5) { actions.switchTab({ url: url }); } else { actions.navigateTo({ url: url }); } } }, //回退指定层数的页面 back(delta) { !delta && (delta = 1); wx.navigateBack({ delta: delta }); }, /*** * 返回本次路由的元信息 * @return {*} */ getMeta() { let pages = getCurrentPages(); let current = pages[pages.length - 1]; let meta = metas[current.__route__]; if (typeof meta === 'object' && !Array.isArray(meta)) { meta.type = _routerType; } console.log(meta); return meta || {}; }, /*** * 清空本页面的缓存数据 */ clearCurMeta() { let pages = getCurrentPages(); let current = pages[pages.length - 1]; metas[current.__route__] = null; delete metas[current.__route__]; }, /*** * 请在具体页面调用(建议在onShow中调用) */ recordPath() { let pages = getCurrentPages(); let current = pages[pages.length - 1]; ROUTE_LINE.push(current.__route__); console.log(ROUTE_LINE); }, /** * 获取栈中前一页面的对象 */ getPreviousPage() { const pages = getCurrentPages(); return (pages.length - 1) > 0 ? pages[pages.length - 2] : null; }, /** * 更新栈中前一页面的数据 * data: 要设置的对象。 * 注:防止页面data字段混乱,只支持set页面data中已存在的字段 */ setPreviousPageData(data) { const pages = getCurrentPages(); const hasPrevious = (pages.length - 1) > 0; if (!hasPrevious) { return; } //前一个页面 const previous = pages[pages.length - 2]; const finalData = {}; Object.keys(data).forEach((val, idx) => { //页面data中已经包含的字段才可以set if (previous.data.hasOwnProperty(val)) { const obj = data[val]; if (util.isObject(obj)) { finalData[val] = Array.isArray(obj) ? [] : {}; //深拷贝对象 util.clone(finalData[val], data[val]); } } }); //一次性调用setData previous.setData(finalData); }, // 取当前页面路由 getCurPage: function() { let _url = ''; let _pages = getCurrentPages(); if (_pages && _pages.length) { let _page = _pages[_pages.length - 1]; _url = _page.route || _page.__route__ || ''; } return _url; } }; export default router;