concat.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import { warn, err } from '../helpers/warn';
  2. import {
  3. diffRouter, vueDevRouteProxy, getRouterNextInfo, formatUserRule, nameToRute, encodeURLQuery, strPathToObjPath, getPages,
  4. } from './util';
  5. import { formatURLQuery } from '../helpers/util';
  6. import { vuelifeHooks, vueMount } from './base';
  7. import { lifeCycle, Global } from '../helpers/config';
  8. let beforeEachCount = 0;
  9. let afterEachCount = 0;
  10. let resolveLaunch = null;
  11. let beforeEnterDep = [];// 记录当前是否有重复的页面进入 避免重复触发
  12. const beforeEachLaunch = new Promise((resolve) => resolveLaunch = resolve);
  13. /**
  14. * 把vue实例进行挂载到dom下
  15. * @param {Router} Router uni-simple-router实例对象
  16. */
  17. export const appMount = function () {
  18. if (vueMount.length == 0) {
  19. return err('检测到您未进行dom模型挂载操作,请调用api\r\n\r\n RouterMount(Vim: any, el: any): void');
  20. }
  21. const {
  22. Vim,
  23. el,
  24. } = vueMount[0];
  25. let formatEl = el;
  26. if (el == null) {
  27. formatEl = '#app'; // 这是uni-app在h5中的官方节点
  28. }
  29. try {
  30. Vim.$mount(formatEl);
  31. } catch (error) {
  32. warn(`挂载vue节点时错误啦${error}`);
  33. }
  34. };
  35. /**
  36. * 格式化 next传递过来的参数 作为vue-router可用的
  37. * @param {Object} to//即将跳转到的路由页面
  38. * @param {*} Intercept
  39. * @param {Funtion} next//路由连接管道
  40. * @param {Router} Router//路由对象
  41. */
  42. export const forMatNext = function (to, Intercept, next, Router) {
  43. const { CONFIG, selfRoutes } = Router;
  44. if (CONFIG.h5.vueRouterDev) { // 完全使用vue-router开发的时候 vueRouterDev:true 不用做啥直接略过
  45. next(Intercept);
  46. return Intercept;
  47. }
  48. if (typeof Intercept === 'object') { // 只有是对象类型的时候 我们才进行格式化
  49. const navType = Reflect.get(Intercept, 'NAVTYPE');
  50. delete Intercept.NAVTYPE;
  51. if (navType == 'push') {
  52. Intercept.replace = false;
  53. Intercept.type = 'navigateTo';
  54. } else {
  55. Intercept.replace = true; // uni-app导航api所谓的NAVTYPE取值在h5都是replace:true
  56. Intercept.type = 'reLaunch';
  57. }
  58. const name = Reflect.get(Intercept, 'name'); // 统一格式化path
  59. Intercept.query = Intercept.params || Intercept.query;
  60. delete Intercept.name;
  61. delete Intercept.params;
  62. if (Intercept.query == null) {
  63. Intercept.query = {};
  64. }
  65. if (name != null) {
  66. const { aliasPath, path } = nameToRute(name, selfRoutes);
  67. Intercept.path = aliasPath || path;
  68. } else { // 当设置别名时可以是别名跳转也可以path跳转
  69. Intercept.path = Reflect.get(Intercept, 'path');
  70. const rute = formatUserRule(Intercept.path, selfRoutes, CONFIG);
  71. if (rute == null) {
  72. return false;
  73. }
  74. Intercept.path = rute;
  75. }
  76. if (CONFIG.encodeURI) { // 如果设置的编码传递则进行编码后传递
  77. const query = encodeURIComponent(JSON.stringify(Intercept.query));
  78. const formatQuery = formatURLQuery(query);
  79. Intercept.query = {};
  80. if (formatQuery != '') {
  81. Intercept.query.query = formatQuery;
  82. }
  83. }
  84. } else if (Intercept != null && Intercept.constructor === String) {
  85. Intercept = formatUserRule(Intercept, selfRoutes, CONFIG);
  86. }
  87. let objPath = Intercept;
  88. if (Intercept != null && Intercept.constructor !== Boolean) {
  89. objPath = strPathToObjPath(Intercept);
  90. if (objPath != null) {
  91. const type = Reflect.get(objPath, 'type');
  92. if (type == null) { // 当next()是一个路径时
  93. objPath.type = 'navigateTo';
  94. }
  95. }
  96. } else if (Intercept === false) {
  97. Router.lifeCycle.routerAfterHooks[0].call(Router, { H5Intercept: true });
  98. }
  99. next(objPath);// 统一格式化为对象的方式传递
  100. return Intercept;
  101. };
  102. /**
  103. * v1.5.4+
  104. * beforeRouteLeave 生命周期
  105. * @param {Object} to 将要去的那个页面 vue-router to对象
  106. * @param {Object} from 从那个页面触发的 vue-router from对象
  107. * @param {Object} next vue-router beforeEach next管道函数
  108. * @param {Object} Router Router路由对象
  109. */
  110. const beforeRouteLeaveHooks = function (to, from, next, Router) {
  111. return new Promise((resolve) => {
  112. const { currentRoute } = Router.$route;
  113. if (currentRoute.path == to.path) { // 如果是同一个页面直接返回 不执行页面中的Leave事件
  114. return resolve();
  115. }
  116. const page = getPages(); // 获取到当前的页面对象
  117. if (page == null || page._HHYANGbeforeRouteLeaveCalled) {
  118. warn('当前环境下无须执行 beforeRouteLeave。 原因:1.page等于null 2.真的的无须执行');
  119. return resolve();
  120. }
  121. const beforeRouteLeaveArray = page.$options.beforeRouteLeave; // 获取到页面下的 beforeRouteLeave 路由守卫
  122. if (beforeRouteLeaveArray == null) { // 当前页面没有预设 beforeRouteLeave 啥都不做
  123. return resolve();
  124. }
  125. const { toRoute, fromRoute } = getRouterNextInfo(to, from, Router);
  126. const beforeRouteLeave = beforeRouteLeaveArray[0]; // 不管怎么样 只执行第一个钩子 其他都不管
  127. beforeRouteLeave.call(page, toRoute, fromRoute, (Intercept) => { // 开始执行生命周期
  128. if (Intercept == null) { // 放行状态 直接返回
  129. return resolve();
  130. }
  131. page._HHYANGbeforeRouteLeaveCalled = true; // 标记一下当前已经做过 beforeRouteLeave 啦
  132. forMatNext(to, Intercept, next, Router); // 直接交给vue-router 处理
  133. });
  134. });
  135. };
  136. /**
  137. * 修复首页beforeEnter执行两次的问题 https://github.com/SilurianYang/uni-simple-router/issues/67
  138. *
  139. * beforeEnter 生命周期
  140. * @param {Object} to
  141. * @param {Object} from
  142. * @param {Object} next
  143. * @param {Object} userHooks
  144. * @param {Object} Router
  145. */
  146. export const beforeEnterHooks = function (to, from, next, userHooks, Router) {
  147. return new Promise(async (resolve) => {
  148. // 修复 (#67)
  149. if (beforeEnterDep.includes(to.path)) {
  150. next();
  151. return resolve();
  152. }
  153. beforeEnterDep = [to.path];
  154. if (Reflect.get(Router, 'H5RouterReady')) {
  155. const res = await new Promise(async (resolveNext) => {
  156. const {
  157. toRoute,
  158. fromRoute,
  159. } = getRouterNextInfo(to, from, Router);
  160. await userHooks(toRoute, fromRoute, resolveNext);
  161. });
  162. forMatNext(to, res, next, Router);
  163. } else {
  164. next();
  165. }
  166. resolve();
  167. });
  168. };
  169. /**
  170. * vueAfter 生命周期
  171. * @param {Object} to
  172. * @param {Object} from
  173. * @param {Object} next
  174. * @param {Object} Router
  175. */
  176. export const afterHooks = async function (to, from, next, Router) {
  177. vuelifeHooks.afterHooks[0](to, from);
  178. if (lifeCycle.afterHooks[0]) {
  179. if (afterEachCount === 0) {
  180. await beforeEachLaunch;
  181. appMount(Router);
  182. }
  183. const {
  184. toRoute,
  185. fromRoute,
  186. } = getRouterNextInfo(to, from, Router);
  187. lifeCycle.afterHooks[0](toRoute, fromRoute);
  188. } else if (afterEachCount === 0) {
  189. appMount(Router);
  190. }
  191. afterEachCount += 1;
  192. Router.lifeCycle.routerAfterHooks[0].call(Router);
  193. };
  194. /**
  195. * vueBefore 生命周期
  196. * @param {Object} to 将要去的那个页面 vue-router to对象
  197. * @param {Object} from 从那个页面触发的 vue-router from对象
  198. * @param {Object} next vue-router beforeEach next管道函数
  199. * @param {Object} H5Config
  200. */
  201. export const beforeHooks = function (to, from, next, Router) {
  202. return new Promise(async (resolve) => {
  203. await Router.lifeCycle.routerbeforeHooks[0].call(Router); // 触发Router内置前置生命周期
  204. await beforeRouteLeaveHooks(to, from, next, Router); // 执行1.5.4+ beforeRouteLeave生命钩子
  205. const H5 = Router.CONFIG.h5;
  206. vuelifeHooks.beforeHooks[0](to, from, async (Intercept) => {
  207. if (Intercept != null && H5.keepUniIntercept === true && H5.vueRouterDev === false) {
  208. next(Intercept);
  209. warn('uni-app 内部强制触发跳转拦截');
  210. beforeEachCount += 1;
  211. return resolve();
  212. }
  213. // 顺序问题 没有触发uni-app里面的方法 修复[#44](https://github.com/SilurianYang/uni-simple-router/issues/44)
  214. if (!lifeCycle.beforeHooks[0]) {
  215. next();
  216. beforeEachCount += 1;
  217. resolveLaunch();
  218. return resolve();
  219. }
  220. const res = await new Promise(async (resolveNext) => {
  221. const {
  222. toRoute,
  223. fromRoute,
  224. } = getRouterNextInfo(to, from, Router);
  225. await lifeCycle.beforeHooks[0](toRoute, fromRoute, resolveNext);
  226. });
  227. const beforeIntercept = forMatNext(to, res, next, Router);
  228. if (beforeEachCount == 0 && beforeIntercept == null && to.meta.isTabBar) { // 首次触发beforeEach,并且首页没有进行跳转的情况下才触发beforeEnter 主要是keep-alive
  229. const {
  230. selfRoutes,
  231. } = Router;
  232. const beforeEnter = Reflect.get(selfRoutes[`/${to.meta.pagePath}`], 'beforeEnter');
  233. if (beforeEnter) {
  234. await beforeEnterHooks(to, from, next, beforeEnter, Router);
  235. }
  236. }
  237. beforeEachCount += 1;
  238. resolveLaunch();
  239. resolve();
  240. });
  241. });
  242. };
  243. /**
  244. * 通过自动调用router api来完成触发生命周期
  245. * 修复 history 模式下报错的问题 https://github.com/SilurianYang/uni-simple-router/issues/38
  246. * 修复 history 模式下刷新页面参数丢失的问题 https://github.com/SilurianYang/uni-simple-router/issues/45
  247. * 修复 history 模式下首次打开页面url错误时不走 path:* 的匹配项 https://github.com/SilurianYang/uni-simple-router/issues/58
  248. *
  249. * @param {Object} Router //当前simple-router 对象
  250. * @param {Object} vueRouter vue-router对象
  251. */
  252. export const triggerLifeCycle = function (Router, vueRouter) {
  253. const { CONFIG } = Router;
  254. const currRoute = vueRouter.currentRoute;
  255. if (vueRouter.mode === 'hash') {
  256. const {
  257. query,
  258. path,
  259. } = currRoute;
  260. const URLQuery = encodeURLQuery(CONFIG, query, 'hash');
  261. vueRouter.replace(`${path}${URLQuery}`);
  262. } else {
  263. const {
  264. toRoute,
  265. } = getRouterNextInfo(currRoute, currRoute, Router);
  266. const URLQuery = encodeURLQuery(CONFIG, currRoute.query, 'history');
  267. vueRouter.replace({
  268. path: toRoute.aliasPath || toRoute.path || currRoute.path,
  269. query: URLQuery,
  270. type: 'redirectTo',
  271. });
  272. }
  273. };
  274. /** 注册自定义的路由到vue-router中 前提是必须使用vueRouter开发模式
  275. * @param {Object} Router
  276. * @param {Object} vueRouter
  277. * @param {Boolean} vueRouterDev
  278. */
  279. export const registerRouter = function (Router, vueRouter, vueRouterDev) {
  280. let routeMap = [];
  281. if (!vueRouterDev) { // 则进行对比两个路由表 主要工作是做路径的优化
  282. routeMap = diffRouter(Router, vueRouter, Router.CONFIG.h5.useUniConfig);
  283. } else { // 完全使用vue-router开发时直接采用开发者的路由表
  284. routeMap = vueDevRouteProxy(Router.CONFIG.routes, Router);
  285. }
  286. const createRouter = () => new vueRouter.constructor({
  287. base: vueRouter.options.base,
  288. mode: vueRouter.options.mode,
  289. routes: routeMap,
  290. });
  291. const router = createRouter();
  292. vueRouter.matcher = router.matcher;
  293. Global.vueRouter = vueRouter; // 把当前vueRouter缓存到全局对象中
  294. Global.RouterReadyPromise(); // router已经准备就绪 调用promise.resolve();
  295. Router.H5RouterReady = true; // 并挂载到Router对象下
  296. // 注册完成所有的钩子及相关参数,手动触发Router的生命周期
  297. setTimeout(() => {
  298. triggerLifeCycle(Router, vueRouter);
  299. });
  300. };