|
|
|
|
import { warn, err } from '../helpers/warn';
|
|
|
|
|
import {
|
|
|
|
|
diffRouter, vueDevRouteProxy, getRouterNextInfo, formatUserRule, nameToRute, encodeURLQuery, strPathToObjPath, getPages,
|
|
|
|
|
} from './util';
|
|
|
|
|
import { formatURLQuery } from '../helpers/util';
|
|
|
|
|
import { vuelifeHooks, vueMount } from './base';
|
|
|
|
|
import { lifeCycle, Global } from '../helpers/config';
|
|
|
|
|
|
|
|
|
|
let beforeEachCount = 0;
|
|
|
|
|
let afterEachCount = 0;
|
|
|
|
|
let resolveLaunch = null;
|
|
|
|
|
let beforeEnterDep = [];// 记录当前是否有重复的页面进入 避免重复触发
|
|
|
|
|
const beforeEachLaunch = new Promise((resolve) => resolveLaunch = resolve);
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 把vue实例进行挂载到dom下
|
|
|
|
|
* @param {Router} Router uni-simple-router实例对象
|
|
|
|
|
*/
|
|
|
|
|
export const appMount = function () {
|
|
|
|
|
if (vueMount.length == 0) {
|
|
|
|
|
return err('检测到您未进行dom模型挂载操作,请调用api\r\n\r\n RouterMount(Vim: any, el: any): void');
|
|
|
|
|
}
|
|
|
|
|
const {
|
|
|
|
|
Vim,
|
|
|
|
|
el,
|
|
|
|
|
} = vueMount[0];
|
|
|
|
|
let formatEl = el;
|
|
|
|
|
if (el == null) {
|
|
|
|
|
formatEl = '#app'; // 这是uni-app在h5中的官方节点
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
Vim.$mount(formatEl);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
warn(`挂载vue节点时错误啦${error}`);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 格式化 next传递过来的参数 作为vue-router可用的
|
|
|
|
|
* @param {Object} to//即将跳转到的路由页面
|
|
|
|
|
* @param {*} Intercept
|
|
|
|
|
* @param {Funtion} next//路由连接管道
|
|
|
|
|
* @param {Router} Router//路由对象
|
|
|
|
|
*/
|
|
|
|
|
export const forMatNext = function (to, Intercept, next, Router) {
|
|
|
|
|
const { CONFIG, selfRoutes } = Router;
|
|
|
|
|
if (CONFIG.h5.vueRouterDev) { // 完全使用vue-router开发的时候 vueRouterDev:true 不用做啥直接略过
|
|
|
|
|
next(Intercept);
|
|
|
|
|
return Intercept;
|
|
|
|
|
}
|
|
|
|
|
if (typeof Intercept === 'object') { // 只有是对象类型的时候 我们才进行格式化
|
|
|
|
|
const navType = Reflect.get(Intercept, 'NAVTYPE');
|
|
|
|
|
delete Intercept.NAVTYPE;
|
|
|
|
|
if (navType == 'push') {
|
|
|
|
|
Intercept.replace = false;
|
|
|
|
|
Intercept.type = 'navigateTo';
|
|
|
|
|
} else {
|
|
|
|
|
Intercept.replace = true; // uni-app导航api所谓的NAVTYPE取值在h5都是replace:true
|
|
|
|
|
Intercept.type = 'reLaunch';
|
|
|
|
|
}
|
|
|
|
|
const name = Reflect.get(Intercept, 'name'); // 统一格式化path
|
|
|
|
|
Intercept.query = Intercept.params || Intercept.query;
|
|
|
|
|
delete Intercept.name;
|
|
|
|
|
delete Intercept.params;
|
|
|
|
|
if (Intercept.query == null) {
|
|
|
|
|
Intercept.query = {};
|
|
|
|
|
}
|
|
|
|
|
if (name != null) {
|
|
|
|
|
const { aliasPath, path } = nameToRute(name, selfRoutes);
|
|
|
|
|
Intercept.path = aliasPath || path;
|
|
|
|
|
} else { // 当设置别名时可以是别名跳转也可以path跳转
|
|
|
|
|
Intercept.path = Reflect.get(Intercept, 'path');
|
|
|
|
|
const rute = formatUserRule(Intercept.path, selfRoutes, CONFIG);
|
|
|
|
|
if (rute == null) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
Intercept.path = rute;
|
|
|
|
|
}
|
|
|
|
|
if (CONFIG.encodeURI) { // 如果设置的编码传递则进行编码后传递
|
|
|
|
|
const query = encodeURIComponent(JSON.stringify(Intercept.query));
|
|
|
|
|
const formatQuery = formatURLQuery(query);
|
|
|
|
|
Intercept.query = {};
|
|
|
|
|
if (formatQuery != '') {
|
|
|
|
|
Intercept.query.query = formatQuery;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (Intercept != null && Intercept.constructor === String) {
|
|
|
|
|
Intercept = formatUserRule(Intercept, selfRoutes, CONFIG);
|
|
|
|
|
}
|
|
|
|
|
let objPath = Intercept;
|
|
|
|
|
if (Intercept != null && Intercept.constructor !== Boolean) {
|
|
|
|
|
objPath = strPathToObjPath(Intercept);
|
|
|
|
|
if (objPath != null) {
|
|
|
|
|
const type = Reflect.get(objPath, 'type');
|
|
|
|
|
if (type == null) { // 当next()是一个路径时
|
|
|
|
|
objPath.type = 'navigateTo';
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (Intercept === false) {
|
|
|
|
|
Router.lifeCycle.routerAfterHooks[0].call(Router, { H5Intercept: true });
|
|
|
|
|
}
|
|
|
|
|
next(objPath);// 统一格式化为对象的方式传递
|
|
|
|
|
return Intercept;
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* v1.5.4+
|
|
|
|
|
* beforeRouteLeave 生命周期
|
|
|
|
|
* @param {Object} to 将要去的那个页面 vue-router to对象
|
|
|
|
|
* @param {Object} from 从那个页面触发的 vue-router from对象
|
|
|
|
|
* @param {Object} next vue-router beforeEach next管道函数
|
|
|
|
|
* @param {Object} Router Router路由对象
|
|
|
|
|
*/
|
|
|
|
|
const beforeRouteLeaveHooks = function (to, from, next, Router) {
|
|
|
|
|
return new Promise((resolve) => {
|
|
|
|
|
const { currentRoute } = Router.$route;
|
|
|
|
|
if (currentRoute.path == to.path) { // 如果是同一个页面直接返回 不执行页面中的Leave事件
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
const page = getPages(); // 获取到当前的页面对象
|
|
|
|
|
if (page == null || page._HHYANGbeforeRouteLeaveCalled) {
|
|
|
|
|
warn('当前环境下无须执行 beforeRouteLeave。 原因:1.page等于null 2.真的的无须执行');
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
const beforeRouteLeaveArray = page.$options.beforeRouteLeave; // 获取到页面下的 beforeRouteLeave 路由守卫
|
|
|
|
|
if (beforeRouteLeaveArray == null) { // 当前页面没有预设 beforeRouteLeave 啥都不做
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
const { toRoute, fromRoute } = getRouterNextInfo(to, from, Router);
|
|
|
|
|
const beforeRouteLeave = beforeRouteLeaveArray[0]; // 不管怎么样 只执行第一个钩子 其他都不管
|
|
|
|
|
beforeRouteLeave.call(page, toRoute, fromRoute, (Intercept) => { // 开始执行生命周期
|
|
|
|
|
if (Intercept == null) { // 放行状态 直接返回
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
page._HHYANGbeforeRouteLeaveCalled = true; // 标记一下当前已经做过 beforeRouteLeave 啦
|
|
|
|
|
forMatNext(to, Intercept, next, Router); // 直接交给vue-router 处理
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 修复首页beforeEnter执行两次的问题 https://github.com/SilurianYang/uni-simple-router/issues/67
|
|
|
|
|
*
|
|
|
|
|
* beforeEnter 生命周期
|
|
|
|
|
* @param {Object} to
|
|
|
|
|
* @param {Object} from
|
|
|
|
|
* @param {Object} next
|
|
|
|
|
* @param {Object} userHooks
|
|
|
|
|
* @param {Object} Router
|
|
|
|
|
*/
|
|
|
|
|
export const beforeEnterHooks = function (to, from, next, userHooks, Router) {
|
|
|
|
|
return new Promise(async (resolve) => {
|
|
|
|
|
// 修复 (#67)
|
|
|
|
|
if (beforeEnterDep.includes(to.path)) {
|
|
|
|
|
next();
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
beforeEnterDep = [to.path];
|
|
|
|
|
|
|
|
|
|
if (Reflect.get(Router, 'H5RouterReady')) {
|
|
|
|
|
const res = await new Promise(async (resolveNext) => {
|
|
|
|
|
const {
|
|
|
|
|
toRoute,
|
|
|
|
|
fromRoute,
|
|
|
|
|
} = getRouterNextInfo(to, from, Router);
|
|
|
|
|
await userHooks(toRoute, fromRoute, resolveNext);
|
|
|
|
|
});
|
|
|
|
|
forMatNext(to, res, next, Router);
|
|
|
|
|
} else {
|
|
|
|
|
next();
|
|
|
|
|
}
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* vueAfter 生命周期
|
|
|
|
|
* @param {Object} to
|
|
|
|
|
* @param {Object} from
|
|
|
|
|
* @param {Object} next
|
|
|
|
|
* @param {Object} Router
|
|
|
|
|
*/
|
|
|
|
|
export const afterHooks = async function (to, from, next, Router) {
|
|
|
|
|
vuelifeHooks.afterHooks[0](to, from);
|
|
|
|
|
if (lifeCycle.afterHooks[0]) {
|
|
|
|
|
if (afterEachCount === 0) {
|
|
|
|
|
await beforeEachLaunch;
|
|
|
|
|
appMount(Router);
|
|
|
|
|
}
|
|
|
|
|
const {
|
|
|
|
|
toRoute,
|
|
|
|
|
fromRoute,
|
|
|
|
|
} = getRouterNextInfo(to, from, Router);
|
|
|
|
|
lifeCycle.afterHooks[0](toRoute, fromRoute);
|
|
|
|
|
} else if (afterEachCount === 0) {
|
|
|
|
|
appMount(Router);
|
|
|
|
|
}
|
|
|
|
|
afterEachCount += 1;
|
|
|
|
|
Router.lifeCycle.routerAfterHooks[0].call(Router);
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* vueBefore 生命周期
|
|
|
|
|
* @param {Object} to 将要去的那个页面 vue-router to对象
|
|
|
|
|
* @param {Object} from 从那个页面触发的 vue-router from对象
|
|
|
|
|
* @param {Object} next vue-router beforeEach next管道函数
|
|
|
|
|
* @param {Object} H5Config
|
|
|
|
|
*/
|
|
|
|
|
export const beforeHooks = function (to, from, next, Router) {
|
|
|
|
|
return new Promise(async (resolve) => {
|
|
|
|
|
await Router.lifeCycle.routerbeforeHooks[0].call(Router); // 触发Router内置前置生命周期
|
|
|
|
|
await beforeRouteLeaveHooks(to, from, next, Router); // 执行1.5.4+ beforeRouteLeave生命钩子
|
|
|
|
|
const H5 = Router.CONFIG.h5;
|
|
|
|
|
vuelifeHooks.beforeHooks[0](to, from, async (Intercept) => {
|
|
|
|
|
if (Intercept != null && H5.keepUniIntercept === true && H5.vueRouterDev === false) {
|
|
|
|
|
next(Intercept);
|
|
|
|
|
warn('uni-app 内部强制触发跳转拦截');
|
|
|
|
|
beforeEachCount += 1;
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
// 顺序问题 没有触发uni-app里面的方法 修复[#44](https://github.com/SilurianYang/uni-simple-router/issues/44)
|
|
|
|
|
if (!lifeCycle.beforeHooks[0]) {
|
|
|
|
|
next();
|
|
|
|
|
beforeEachCount += 1;
|
|
|
|
|
resolveLaunch();
|
|
|
|
|
return resolve();
|
|
|
|
|
}
|
|
|
|
|
const res = await new Promise(async (resolveNext) => {
|
|
|
|
|
const {
|
|
|
|
|
toRoute,
|
|
|
|
|
fromRoute,
|
|
|
|
|
} = getRouterNextInfo(to, from, Router);
|
|
|
|
|
await lifeCycle.beforeHooks[0](toRoute, fromRoute, resolveNext);
|
|
|
|
|
});
|
|
|
|
|
const beforeIntercept = forMatNext(to, res, next, Router);
|
|
|
|
|
if (beforeEachCount == 0 && beforeIntercept == null && to.meta.isTabBar) { // 首次触发beforeEach,并且首页没有进行跳转的情况下才触发beforeEnter 主要是keep-alive
|
|
|
|
|
const {
|
|
|
|
|
selfRoutes,
|
|
|
|
|
} = Router;
|
|
|
|
|
const beforeEnter = Reflect.get(selfRoutes[`/${to.meta.pagePath}`], 'beforeEnter');
|
|
|
|
|
if (beforeEnter) {
|
|
|
|
|
await beforeEnterHooks(to, from, next, beforeEnter, Router);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
beforeEachCount += 1;
|
|
|
|
|
resolveLaunch();
|
|
|
|
|
resolve();
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
/**
|
|
|
|
|
* 通过自动调用router api来完成触发生命周期
|
|
|
|
|
* 修复 history 模式下报错的问题 https://github.com/SilurianYang/uni-simple-router/issues/38
|
|
|
|
|
* 修复 history 模式下刷新页面参数丢失的问题 https://github.com/SilurianYang/uni-simple-router/issues/45
|
|
|
|
|
* 修复 history 模式下首次打开页面url错误时不走 path:* 的匹配项 https://github.com/SilurianYang/uni-simple-router/issues/58
|
|
|
|
|
*
|
|
|
|
|
* @param {Object} Router //当前simple-router 对象
|
|
|
|
|
* @param {Object} vueRouter vue-router对象
|
|
|
|
|
*/
|
|
|
|
|
export const triggerLifeCycle = function (Router, vueRouter) {
|
|
|
|
|
const { CONFIG } = Router;
|
|
|
|
|
const currRoute = vueRouter.currentRoute;
|
|
|
|
|
if (vueRouter.mode === 'hash') {
|
|
|
|
|
const {
|
|
|
|
|
query,
|
|
|
|
|
path,
|
|
|
|
|
} = currRoute;
|
|
|
|
|
|
|
|
|
|
const URLQuery = encodeURLQuery(CONFIG, query, 'hash');
|
|
|
|
|
|
|
|
|
|
vueRouter.replace(`${path}${URLQuery}`);
|
|
|
|
|
} else {
|
|
|
|
|
const {
|
|
|
|
|
toRoute,
|
|
|
|
|
} = getRouterNextInfo(currRoute, currRoute, Router);
|
|
|
|
|
const URLQuery = encodeURLQuery(CONFIG, currRoute.query, 'history');
|
|
|
|
|
vueRouter.replace({
|
|
|
|
|
path: toRoute.aliasPath || toRoute.path || currRoute.path,
|
|
|
|
|
query: URLQuery,
|
|
|
|
|
type: 'redirectTo',
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
/** 注册自定义的路由到vue-router中 前提是必须使用vueRouter开发模式
|
|
|
|
|
* @param {Object} Router
|
|
|
|
|
* @param {Object} vueRouter
|
|
|
|
|
* @param {Boolean} vueRouterDev
|
|
|
|
|
*/
|
|
|
|
|
export const registerRouter = function (Router, vueRouter, vueRouterDev) {
|
|
|
|
|
let routeMap = [];
|
|
|
|
|
if (!vueRouterDev) { // 则进行对比两个路由表 主要工作是做路径的优化
|
|
|
|
|
routeMap = diffRouter(Router, vueRouter, Router.CONFIG.h5.useUniConfig);
|
|
|
|
|
} else { // 完全使用vue-router开发时直接采用开发者的路由表
|
|
|
|
|
routeMap = vueDevRouteProxy(Router.CONFIG.routes, Router);
|
|
|
|
|
}
|
|
|
|
|
const createRouter = () => new vueRouter.constructor({
|
|
|
|
|
base: vueRouter.options.base,
|
|
|
|
|
mode: vueRouter.options.mode,
|
|
|
|
|
routes: routeMap,
|
|
|
|
|
});
|
|
|
|
|
const router = createRouter();
|
|
|
|
|
vueRouter.matcher = router.matcher;
|
|
|
|
|
Global.vueRouter = vueRouter; // 把当前vueRouter缓存到全局对象中
|
|
|
|
|
Global.RouterReadyPromise(); // router已经准备就绪 调用promise.resolve();
|
|
|
|
|
Router.H5RouterReady = true; // 并挂载到Router对象下
|
|
|
|
|
// 注册完成所有的钩子及相关参数,手动触发Router的生命周期
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
triggerLifeCycle(Router, vueRouter);
|
|
|
|
|
});
|
|
|
|
|
};
|