all
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
export const builtIn = [{
|
||||
path: '/preview-image',
|
||||
name: 'previewImage',
|
||||
component: {
|
||||
render: () => {},
|
||||
},
|
||||
}, {
|
||||
path: '/choose-location',
|
||||
name: 'chooseLocation',
|
||||
component: {
|
||||
render: () => {},
|
||||
},
|
||||
}, {
|
||||
path: '/open-location',
|
||||
name: 'openLocation',
|
||||
component: {
|
||||
render: () => {},
|
||||
},
|
||||
}]; // uni-app内置路由
|
||||
export const vuelifeHooks = { // vueRouter的原始生命周期
|
||||
beforeHooks: [],
|
||||
afterHooks: [],
|
||||
};
|
||||
export const vueMount = [];// 使用内部对象保留实例化下的appVue,并使用Router进行挂载触发第一次路由钩子
|
||||
@@ -0,0 +1,308 @@
|
||||
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);
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,20 @@
|
||||
/**
|
||||
* 实现一个继承的 数组类 代理掉 vue-router 生命钩子的数据
|
||||
*/
|
||||
class MyArray extends Array {
|
||||
constructor(Router, vueOldHooks, hookFun) {
|
||||
super();
|
||||
this.Router = Router;
|
||||
this.vueOldHooks = vueOldHooks;
|
||||
this.hookFun = hookFun;
|
||||
}
|
||||
|
||||
push(v) {
|
||||
this.vueOldHooks.splice(0, 1, v);// 把vue-router路由生命钩子保存起来
|
||||
this[this.length] = (to, from, next) => {
|
||||
this.hookFun(to, from, next, this.Router);
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default MyArray;
|
||||
@@ -0,0 +1,68 @@
|
||||
import { afterHooks, beforeHooks, registerRouter } from './concat';
|
||||
import { fromatRoutes } from './util';
|
||||
import { err, warn } from '../helpers/warn';
|
||||
import { proxyEachHooks } from './proxy/proxy';
|
||||
/**
|
||||
* 重写掉H5端 uni-app原始存在的bug
|
||||
*
|
||||
* @param {Object} Router
|
||||
*/
|
||||
const rewriteUniFun = function (Router) {
|
||||
if (Router.CONFIG.h5.rewriteFun === false) { // 不需要重写
|
||||
return false;
|
||||
}
|
||||
uni.reLaunch = function ({
|
||||
url,
|
||||
}) {
|
||||
if (url === '/') {
|
||||
warn('H5端 uni.reLaunch(\'/\')时 默认被重写了! 你可以使用 this.$Router.replaceAll() 或者 uni.reLaunch(\'/\'?xxx)');
|
||||
// eslint-disable-next-line
|
||||
if (history.length > 1) { // 只有在有历史记录的时候才返回 不然直接返回首页
|
||||
return Router.back();
|
||||
}
|
||||
return Router.replaceAll('/');
|
||||
}
|
||||
const path = url.match(/^[^?]+|(\/)/)[0];
|
||||
try {
|
||||
const query = {};
|
||||
url.replace(/([^?&]+)=([^?&]+)/g, (s, v, k) => {
|
||||
query[v] = decodeURIComponent(k);
|
||||
return `${k}=${v}`;
|
||||
});
|
||||
Router.replaceAll({
|
||||
path,
|
||||
query,
|
||||
});
|
||||
} catch (e) {
|
||||
err(`${url}解析失败了.... 试试 this.$Router.replaceAll() 吧`);
|
||||
}
|
||||
};
|
||||
uni.navigateBack = function (delta) {
|
||||
let backLayer = delta;
|
||||
if (delta.constructor === Object) { // 这种可能就只是uni-app自带的返回按钮,还有种可能就是开发者另类传递的
|
||||
backLayer = 1;
|
||||
}
|
||||
Router.back(backLayer, delta);
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 拦截并注册vueRouter中的生命钩子,路由表解析
|
||||
* @param {Object} Router
|
||||
* @param {vueRouter} vueRouter
|
||||
*/
|
||||
const init = function (Router, vueRouter) {
|
||||
const CONFIG = Router.CONFIG.h5;
|
||||
vueRouter.afterHooks = proxyEachHooks(Router, 'afterHooks', afterHooks);
|
||||
vueRouter.beforeHooks = proxyEachHooks(Router, 'beforeHooks', beforeHooks);
|
||||
const objVueRoutes = fromatRoutes(vueRouter.options.routes, false, {}); // 返回一个格式化好的routes 键值对的形式
|
||||
const objSelfRoutes = fromatRoutes(Router.CONFIG.routes, true, CONFIG);
|
||||
Router.vueRoutes = objVueRoutes; // 挂载vue-routes到当前的路由下
|
||||
Router.selfRoutes = {
|
||||
...Router.selfRoutes || {},
|
||||
...objSelfRoutes,
|
||||
}; // 挂载self-routes到当前路由下
|
||||
Router.$route = vueRouter; // 挂载vue-router到$route
|
||||
rewriteUniFun(Router); // 重新掉uniapp上的一些有异常的方法
|
||||
registerRouter(Router, vueRouter, CONFIG.vueRouterDev);
|
||||
};
|
||||
export default init;
|
||||
@@ -0,0 +1,44 @@
|
||||
import { beforeEnterHooks } from '../concat';
|
||||
import { vuelifeHooks } from '../base';
|
||||
import MyArray from '../extends/myArray';
|
||||
|
||||
/**
|
||||
* 通过 Object.defineProperty 代理一个对象主要是拦截beforeEnter 生命钩子
|
||||
* @param {Router} Router 路由实例对象
|
||||
* @param {Object} BeProxy 需要代理的路由表
|
||||
*/
|
||||
export const proxyBeforeEnter = function (Router, BeProxy) {
|
||||
const proxyDc = Object.create(null);// 创建没有继承的属性
|
||||
const BeProxyKeys = Object.keys(BeProxy);
|
||||
for (let i = 0; i < BeProxyKeys.length; i += 1) {
|
||||
const key = BeProxyKeys[i];
|
||||
Object.defineProperty(proxyDc, key, {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
get() {
|
||||
const value = BeProxy[key];
|
||||
if (key == 'beforeEnter' && value !== undefined) {
|
||||
return (to, from, next) => {
|
||||
beforeEnterHooks(to, from, next, value, Router);
|
||||
};
|
||||
}
|
||||
return value;
|
||||
},
|
||||
set(v) {
|
||||
BeProxy[key] = v;
|
||||
},
|
||||
});
|
||||
}
|
||||
return proxyDc;
|
||||
};
|
||||
|
||||
/**
|
||||
* 在uni-app没有注入生命周期时先直接代理相关生命周期数组
|
||||
* @param {Object} Router
|
||||
* @param {Object} key
|
||||
* @param {Funtion} hookFun
|
||||
*/
|
||||
export const proxyEachHooks = function (Router, key, hookFun) {
|
||||
const vueOldHooks = vuelifeHooks[key];
|
||||
return new MyArray(Router, vueOldHooks, hookFun);
|
||||
};
|
||||
@@ -0,0 +1,21 @@
|
||||
import { err } from '../helpers/warn';
|
||||
import { formatUserRule, strPathToObjPath } from './util';
|
||||
|
||||
/**
|
||||
* @param {Object} replace vue-router的跳转方式
|
||||
* @param {Object} rule 需要跳转到的路由匹配规则
|
||||
* @param {Object} type 对应的官方跳转模式
|
||||
*
|
||||
* this 为当前 Router 实例
|
||||
*/
|
||||
const H5PushTo = function (replace, rule, type) {
|
||||
if (this.$route == null) {
|
||||
return err('h5端路由为就绪,请检查调用代码');
|
||||
}
|
||||
rule = formatUserRule(rule, this.selfRoutes, this.CONFIG);
|
||||
const objPath = strPathToObjPath(rule);
|
||||
objPath.type = type;
|
||||
this.$route[replace](objPath);
|
||||
};
|
||||
|
||||
export default H5PushTo;
|
||||
@@ -0,0 +1,450 @@
|
||||
import { warn, err } from '../helpers/warn';
|
||||
import { isObject, resolveRule, copyObject } from '../helpers/util';
|
||||
import { proxyBeforeEnter } from './proxy/proxy';
|
||||
import { Global } from '../helpers/config';
|
||||
|
||||
const pagesConfigReg = /props:\s*\(.*\)\s*(\([\s\S]*\))\s*},/;
|
||||
const pagesConfigRegCli = /props:\s*Object\.assign\s*(\([\s\S]*\))\s*},/; // 脚手架项目
|
||||
const defRoutersReg = /props:\s*{([\s\S]+)}\s*},/;
|
||||
|
||||
/**
|
||||
* 解析验证当前的 component 选项是否配置正确 只有vueRouterDev:false 才会调用此方法
|
||||
* @param {Function|Object} component
|
||||
* @param {Object} item
|
||||
* @param {Boolean} useUniConfig
|
||||
*/
|
||||
export const resolveRender = function ({
|
||||
component,
|
||||
components,
|
||||
}, item, useUniConfig) {
|
||||
if (components != null) {
|
||||
warn(`vueRouterDev:false时 路由表配置中 ‘components’ 无效,\r\n\r\n ${JSON.stringify(item)}`);
|
||||
}
|
||||
if (useUniConfig == true) { // 采用uni-pages.json中的配置时component可以为空
|
||||
return false;
|
||||
}
|
||||
if (item.path == '*') { // 唯独这个情况在vue-router中可以不用component
|
||||
return true;
|
||||
}
|
||||
if (component == null) {
|
||||
return err(`vueRouterDev:false时 路由表中 ‘component’ 选项不能为空:\r\n\r\n ${JSON.stringify(item)}`);
|
||||
}
|
||||
if (component.constructor === Function) {
|
||||
item.component = {
|
||||
render: component,
|
||||
};
|
||||
} else if (component.constructor === Object) {
|
||||
if (component.render == null || component.render.constructor !== Function) {
|
||||
err(`vueRouterDev:false时 路由表配置中 ‘render’ 函数缺失或类型不正确:\r\n\r\n ${JSON.stringify(item)}`);
|
||||
}
|
||||
} else {
|
||||
err(
|
||||
`vueRouterDev:false时 路由表配置中 ‘component’ 选项仅支持 Function、Object 类型。并确保 Object 类型时传递了 ‘render’ 函数 :\r\n\r\n ${JSON.stringify(item)}`,
|
||||
);
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 递归解析 H5配置中有存在嵌套对象的情况,优先以path为key存储。没有则找aliasPath作为key
|
||||
* @param {Object} objRoutes
|
||||
* @param {Array} children
|
||||
* @param {Boolean} useUniConfig 是否使用pages.json下的页面配置
|
||||
*/
|
||||
export const resloveChildrenPath = function (objRoutes, children, useUniConfig) {
|
||||
for (let i = 0; i < children.length; i += 1) {
|
||||
const item = children[i];
|
||||
resolveRender(item, item, useUniConfig);
|
||||
if (item.path != null) {
|
||||
objRoutes[item.path] = {
|
||||
...item,
|
||||
...{
|
||||
_ROUTERPATH: true, // 使用page.json中的path为路径
|
||||
},
|
||||
};
|
||||
} else {
|
||||
objRoutes[item.aliasPath] = {
|
||||
...item,
|
||||
...{
|
||||
_ROUTERPATH: false,
|
||||
},
|
||||
};
|
||||
}
|
||||
if (item.children && item.children.constructor === Array) {
|
||||
resloveChildrenPath(objRoutes, item.children, useUniConfig);
|
||||
}
|
||||
}
|
||||
};
|
||||
/**
|
||||
* 格式化原始路由表
|
||||
* @param {Object} routes 路由表
|
||||
* @param {Boolean} userRoute 是否为用户自己配置的路由表
|
||||
* @param {Boolean} H5CONFIG
|
||||
*/
|
||||
export const fromatRoutes = function (routes, userRoute, {
|
||||
vueRouterDev,
|
||||
useUniConfig,
|
||||
}) {
|
||||
if (userRoute && vueRouterDev) { // 如果是用户的路由表并且 完全采用vueRouter开发 则不作处理直接返回
|
||||
return routes;
|
||||
}
|
||||
const objRoutes = {};
|
||||
for (let i = 0; i < routes.length; i += 1) {
|
||||
const item = routes[i];
|
||||
const path = item.path === '/' ? item.alias : item.path;
|
||||
if (userRoute) {
|
||||
if (item.children && item.children.constructor === Array) {
|
||||
resloveChildrenPath(objRoutes, item.children, useUniConfig);
|
||||
}
|
||||
resolveRender(item, item, useUniConfig); // 是否使用pages.json下的页面配置
|
||||
}
|
||||
objRoutes[path] = {
|
||||
...item,
|
||||
...{
|
||||
_PAGEPATH: path.substring(1),
|
||||
},
|
||||
};
|
||||
}
|
||||
return objRoutes;
|
||||
};
|
||||
|
||||
/**
|
||||
* 解析vueRouter中 component 下 render函数中的配置信息
|
||||
* @param {String} FunStr
|
||||
*/
|
||||
export const getFuntionConfig = function (FunStr) {
|
||||
let matchText = FunStr.match(pagesConfigReg);
|
||||
let prefix = '';
|
||||
if (matchText == null) { // 是uni-app自带的默认路由及配置 也可能是脚手架项目
|
||||
matchText = FunStr.match(pagesConfigRegCli);
|
||||
if (matchText == null) { // 确认不是脚手架项目
|
||||
try {
|
||||
// eslint-disable-next-line
|
||||
matchText = FunStr.match(defRoutersReg)[1];
|
||||
// eslint-disable-next-line
|
||||
matchText = eval(`Object.assign({${matchText}})`);
|
||||
prefix = 'system-';
|
||||
} catch (error) {
|
||||
err(`读取uni-app页面构建方法配置错误 \r\n\r\n ${error}`);
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line
|
||||
matchText = eval(`Object.assign${matchText[1]}`);
|
||||
}
|
||||
} else {
|
||||
// eslint-disable-next-line
|
||||
matchText = eval(`Object.assign${matchText[1]}`);
|
||||
}
|
||||
return {
|
||||
config: matchText,
|
||||
prefix,
|
||||
FunStr,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 通过一个未知的路径名称 在路由表中查找指定路由表 并返回
|
||||
* @param {String} path //不管是aliasPath名的路径还是path名的路径
|
||||
* @param {Object} routes//当前对象的所有路由表
|
||||
*/
|
||||
export const pathToRute = function (path, routes) {
|
||||
let PATHKEY = '';
|
||||
let rute = {};
|
||||
const routeKeys = Object.keys(routes);
|
||||
for (let i = 0; i < routeKeys.length; i += 1) {
|
||||
const key = routeKeys[i];
|
||||
const item = routes[key];
|
||||
rute = item;
|
||||
if (item.aliasPath == path) { // path参数是优先采用aliasPath为值得 所以可以先判断是否与aliasPath相同
|
||||
PATHKEY = 'aliasPath';
|
||||
break;
|
||||
}
|
||||
// eslint-disable-next-line
|
||||
if (`/${item._PAGEPATH}` == path) { // 路径相同
|
||||
PATHKEY = 'path';
|
||||
break;
|
||||
}
|
||||
}
|
||||
return {
|
||||
PATHKEY: {
|
||||
[PATHKEY]: path,
|
||||
},
|
||||
rute,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 通过一个路径name 在路由表中查找指定路由表 并返回
|
||||
* @param {String} name//实例化路由时传递的路径表中所匹配的对应路由name
|
||||
* @param {Object} routes//当前对象的所有路由表
|
||||
*/
|
||||
export const nameToRute = function (name, routes) {
|
||||
const routesKeys = Object.keys(routes);
|
||||
for (let i = 0; i < routesKeys.length; i += 1) {
|
||||
const key = routesKeys[i];
|
||||
const item = routes[key];
|
||||
if (item.name == name) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
|
||||
err(`路由表中没有找到 name为:'${name}' 的路由`);
|
||||
};
|
||||
/**
|
||||
* 根据用户传入的路由规则 格式化成正确的路由规则
|
||||
* @param {Object} rule 用户需要跳转的路由规则
|
||||
* @param {Object} selfRoutes simple-router下的所有routes对象
|
||||
* @param {Object} CONFIG 当前路由下的所有配置信息
|
||||
*/
|
||||
export const formatUserRule = function (rule, selfRoutes, CONFIG) {
|
||||
let type = '';
|
||||
const ruleQuery = (type = 'query', rule.query || (type = 'params', rule.params)) || (type = '', {});
|
||||
let rute = {}; // 默认在router中的配置
|
||||
if (type == '' && rule.name != null) { // 那就是可能没有穿任何值咯
|
||||
type = 'params';
|
||||
}
|
||||
if (type != 'params') {
|
||||
const route = pathToRute(rule.path || rule, selfRoutes);
|
||||
if (Object.keys(route.PATHKEY)[0] == '') {
|
||||
err(`'${route.PATHKEY['']}' 路径在路由表中未找到`);
|
||||
return null;
|
||||
}
|
||||
rute = route.rute;
|
||||
if (rule.path) {
|
||||
rule.path = rute.path;
|
||||
}
|
||||
}
|
||||
if (type != '') { // 当然是对象啦 这个主要是首页H5PushTo调用时的
|
||||
if (type == 'params' && CONFIG.h5.paramsToQuery) { // 如果是name规则并且设置了转query,那么就转path跳转了
|
||||
const {
|
||||
aliasPath,
|
||||
path,
|
||||
} = nameToRute(rule.name, selfRoutes);
|
||||
delete rule.name;
|
||||
delete rule.params;
|
||||
rule.path = aliasPath || path;
|
||||
type = 'query';
|
||||
}
|
||||
const query = Global.$parseQuery.transfer(ruleQuery);
|
||||
if (CONFIG.encodeURI) {
|
||||
if (query != '') {
|
||||
rule[type] = {
|
||||
query: query.replace(/^query=/, ''),
|
||||
};
|
||||
}
|
||||
} else {
|
||||
rule[type] = ruleQuery;
|
||||
}
|
||||
} else { // 纯字符串,那就只有是path啦
|
||||
rule = rute.path;
|
||||
}
|
||||
return rule;
|
||||
};
|
||||
|
||||
/**
|
||||
* 根据是否获取非vue-Router next管道参数,来进行格式化
|
||||
*
|
||||
* @param {Object} to
|
||||
* @param {Object} from
|
||||
* @param {Router} Router //router当前实例对象
|
||||
*/
|
||||
export const getRouterNextInfo = function (to, from, Router) {
|
||||
let [toRoute, fromRoute] = [to, from];
|
||||
const H5 = Router.CONFIG.h5;
|
||||
if (H5.vueNext === false && H5.vueRouterDev === false) { // 不采用vue-router中的to和from,需要格式化成Router中$Route获取的一样一样的
|
||||
let [toPath, fromPath] = [{}, {}];
|
||||
toPath[to.meta.PATHKEY] = to.meta.PATHKEY === 'path' ? `/${to.meta.pagePath}` : `${to.path}`;
|
||||
fromPath[from.meta.PATHKEY] = from.meta.PATHKEY === 'path' ? `/${from.meta.pagePath}` : `${from.path}`;
|
||||
|
||||
if (to.meta.PATHKEY == null) { // 未使用uni-pages.json中的配置、通过addRoutes时 meta.PATHKEY 可能未undefined
|
||||
toPath = pathToRute(to.path, Router.selfRoutes).PATHKEY;
|
||||
}
|
||||
if (from.meta.PATHKEY == null) {
|
||||
fromPath = pathToRute(from.path, Router.selfRoutes).PATHKEY;
|
||||
}
|
||||
|
||||
const isEmptyTo = Object.keys(to.query).length != 0 ? copyObject(to.query) : copyObject(to.params);
|
||||
const isEmptyFrom = Object.keys(from.query).length != 0 ? copyObject(from.query) : copyObject(from.params);
|
||||
/* eslint-disable */
|
||||
delete isEmptyTo.__id__; // 删除uni-app下的内置属性
|
||||
delete isEmptyFrom.__id__;
|
||||
/* eslint-enable */
|
||||
const toQuery = Global.$parseQuery.queryGet(isEmptyTo).decode;
|
||||
const fromQuery = Global.$parseQuery.queryGet(isEmptyFrom).decode;
|
||||
toRoute = resolveRule(Router, toPath, toQuery, Object.keys(toPath)[0]);
|
||||
fromRoute = resolveRule(Router, fromPath, fromQuery, Object.keys(fromPath)[0]);
|
||||
} else {
|
||||
if (fromRoute.name == null && toRoute.name != null) { // 这种情况是因为uni-app在使用vue-router时搞了骚操作。
|
||||
fromRoute = {
|
||||
...fromRoute,
|
||||
...{
|
||||
name: toRoute.name,
|
||||
},
|
||||
}; // 这个情况一般出现在首次加载页面
|
||||
}
|
||||
}
|
||||
return {
|
||||
toRoute,
|
||||
fromRoute,
|
||||
};
|
||||
};
|
||||
export const vueDevRouteProxy = function (routes, Router) {
|
||||
const proxyRoutes = [];
|
||||
for (let i = 0; i < routes.length; i += 1) {
|
||||
const item = routes[i];
|
||||
const childrenRoutes = Reflect.get(item, 'children');
|
||||
if (childrenRoutes != null) {
|
||||
const childrenProxy = vueDevRouteProxy(childrenRoutes, Router);
|
||||
item.children = childrenProxy;
|
||||
}
|
||||
const ProxyRoute = proxyBeforeEnter(Router, item);
|
||||
proxyRoutes.push(ProxyRoute);
|
||||
}
|
||||
return proxyRoutes;
|
||||
};
|
||||
/**
|
||||
* 组装成编码后的路由query传递信息
|
||||
* @param {Object} CONFIG simple-router 对象配置
|
||||
* @param {Object} query 传递的参数
|
||||
* @param {Object} mode 路由模式
|
||||
*/
|
||||
|
||||
export const encodeURLQuery = function (CONFIG, query, mode) {
|
||||
if (Object.keys(query).length == 0) { // 没有传值的时候 我们啥都不管
|
||||
return '';
|
||||
}
|
||||
if (CONFIG.h5.vueRouterDev === false) { // 没有采取完全模式开发时 才转换
|
||||
const { strQuery, historyObj } = Global.$parseQuery.queryGet(query);
|
||||
if (mode === 'history') {
|
||||
return historyObj;
|
||||
}
|
||||
return strQuery;
|
||||
} // 完全彩种 vue-router 开发的时候 我们不用管
|
||||
if (mode === 'history') { // 此模式下 需要的就是对象
|
||||
return query;
|
||||
}
|
||||
return Global.$parseQuery.stringify(query); // hash转成字符串拼接
|
||||
};
|
||||
/**
|
||||
* 把一个未知的路由跳转规则进行格式化为 hash、history 可用的,主要表现在 history模式下直接传入path会报错__id__错误的问题
|
||||
* @param {*} path 需要判断修改的路径规则
|
||||
*/
|
||||
export const strPathToObjPath = function (path) {
|
||||
if (path == null) { // 我们也不用管啦,这个情况是路由守卫中传递的
|
||||
return path;
|
||||
}
|
||||
if (isObject(path)) { // 是对象我们不用管
|
||||
return path;
|
||||
}
|
||||
return { // 这种情况就是只有path时,直接返回path对象了
|
||||
path,
|
||||
};
|
||||
};
|
||||
/**
|
||||
* 通过 getCurrentPages() api 获取指定页面的 page 对象 默认是获取当前页面page对象
|
||||
* @param {Number} index //需要获取的页面索引
|
||||
* @param {Boolean} all //是否获取全部的页面
|
||||
*/
|
||||
export const getPages = function (index = 0, all) {
|
||||
const pages = getCurrentPages(all);
|
||||
return pages.reverse()[index];
|
||||
};
|
||||
/**
|
||||
* 获取当前页面下的 Route 信息
|
||||
*
|
||||
* @param {Object} pages 获取页面对象集合
|
||||
* @param {Object} Vim 用户传递的当前页面对象
|
||||
*/
|
||||
export const H5GetPageRoute = function (pages, Vim) {
|
||||
if (pages.length > 0) { // 直接取当前页面的对象
|
||||
const currentRoute = pages[pages.length - 1].$route;
|
||||
return getRouterNextInfo(currentRoute, currentRoute, this).toRoute;
|
||||
} if (Vim && Vim.$route) {
|
||||
return getRouterNextInfo(Vim.$route, Vim.$route, this).toRoute;
|
||||
}
|
||||
return {};
|
||||
};
|
||||
|
||||
/**
|
||||
* 在useUniConfig:true 的情况下重新拼装路由表 useUniConfig:false 不需要读取page.json中的数据 直接使用component作为页面组件
|
||||
* @param {Router} Router//unis-simple-router 路由对象
|
||||
* @param {vueRouter} vueRouter//vue-router对象
|
||||
* @param {Boolean} useUniConfig//是否采用uni-page.json中的配置选项
|
||||
* @param {Array} routes//需要循环的routes表
|
||||
*/
|
||||
export const diffRouter = function (Router, vueRouter, useUniConfig, routes) {
|
||||
const newRouterMap = [];
|
||||
if (useUniConfig) { // 使用pages.json的样式配置 只是单纯的把url路径改成用户自定义的 保留uni的所以的配置及生命周期、缓存
|
||||
const Routes = routes || vueRouter.options.routes;
|
||||
const cloneSelfRoutes = copyObject(Router.selfRoutes); // copy一个对象随便搞xxoo
|
||||
Routes.forEach(((item) => {
|
||||
const path = item.path === '/' ? item.alias : item.path;
|
||||
const vueRoute = (Router.vueRoutes[path] || Router.vueRoutes[item.path]) || Router.selfRoutes[path];
|
||||
const CselfRoute = Router.selfRoutes[path];
|
||||
delete cloneSelfRoutes[path]; // 移除已经添加到容器中的路由,用于最后做对比 是否page.json中没有,而实例化时传递了
|
||||
if (CselfRoute == null) {
|
||||
return err(
|
||||
`读取 ‘pages.json’ 中页面配置错误。实例化时传递的路由表中未找到路径为:${path} \r\n\r\n 可以尝试把 ‘useUniConfig’ 设置为 ‘false’。或者配置正确的路径。如果你是动态添加的则不用理会`,
|
||||
);
|
||||
}
|
||||
let pageConfigJson = {
|
||||
config: {},
|
||||
};
|
||||
if (vueRoute.component) {
|
||||
pageConfigJson = getFuntionConfig(vueRoute.component.render.toString());
|
||||
CselfRoute.component = {
|
||||
render: (h) => vueRoute.component.render(h),
|
||||
};
|
||||
}
|
||||
delete CselfRoute.components; // useUniConfig:true 时不允许携带components
|
||||
delete CselfRoute.children; // useUniConfig:true 时不允许携带children
|
||||
CselfRoute.meta = {
|
||||
...pageConfigJson.config,
|
||||
...item.meta || {},
|
||||
PATHKEY: CselfRoute.aliasPath ? 'aliasPath' : 'path',
|
||||
pagePath: CselfRoute.path.substring(1),
|
||||
};
|
||||
CselfRoute.path = CselfRoute.aliasPath || (item.path === '/' ? item.path : CselfRoute.path);
|
||||
item.alias = item.path === '/' ? item.alias : CselfRoute.path; // 重新给vueRouter赋值一个新的路径,欺骗uni-app源码判断
|
||||
const ProxyRoute = proxyBeforeEnter(Router, CselfRoute);
|
||||
newRouterMap.push(ProxyRoute);
|
||||
}));
|
||||
if (Object.keys(cloneSelfRoutes).length > 0) { // 确实page.json中没有,而实例化时传递了
|
||||
const testG = cloneSelfRoutes['*']; // 全局通配符,他是个例外'通配符'可以被添加
|
||||
if (testG && routes == null) {
|
||||
const ProxyRoute = proxyBeforeEnter(Router, Router.selfRoutes['*']);
|
||||
newRouterMap.push(ProxyRoute);
|
||||
}
|
||||
if (routes == null) { // 非动态添加时才打印警告
|
||||
const cloneSelfRoutesKeys = Object.keys(cloneSelfRoutes);
|
||||
for (let i = 0; i < cloneSelfRoutesKeys.length; i += 1) {
|
||||
const key = cloneSelfRoutesKeys[i];
|
||||
if (key !== '*') { // 通配符不警告
|
||||
warn(`实例化时传递的routes参数:\r\n\r\n ${JSON.stringify(cloneSelfRoutes[key])} \r\n\r\n 在pages.json中未找到。自定排除掉,不会添加到路由中`);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else { // 不使用任何的uni配置完全使用 完全使用component作为页面使用
|
||||
const Routes = routes || Router.selfRoutes;
|
||||
const RoutesKeys = Object.keys(Routes);
|
||||
for (let i = 0; i < RoutesKeys.length; i += 1) {
|
||||
const key = RoutesKeys[i];
|
||||
const item = Routes[key];
|
||||
// eslint-disable-next-line
|
||||
if (item._ROUTERPATH != null) { // 不寻找children下的路径,只取第一层
|
||||
continue;
|
||||
}
|
||||
delete item.components;
|
||||
delete item.children;
|
||||
item.path = item.aliasPath || item.path; // 优先获取别名为路径
|
||||
if (item.path !== '*') {
|
||||
item.component = item.component.render || item.component; // render可能是用户使用addRoutes api进行动态添加的
|
||||
}
|
||||
item.meta = {
|
||||
...item.meta || {},
|
||||
PATHKEY: item.aliasPath ? 'aliasPath' : 'path',
|
||||
pagePath: item.path.substring(1),
|
||||
};
|
||||
const ProxyRoute = proxyBeforeEnter(Router, item);
|
||||
newRouterMap.push(ProxyRoute);
|
||||
}
|
||||
}
|
||||
return newRouterMap;
|
||||
};
|
||||
Reference in New Issue
Block a user