handleHooks.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124
  1. import { isError, getCurStack, print } from './utils';
  2. /**
  3. * 处理 全局钩子队列
  4. * @param {Object} to
  5. * @param {Function} uniRunRoute 被hack的uniapp路由方法
  6. */
  7. export const handleGlobalHooksQueue = function (to, uniRunRoute) {
  8. // 跳过 h5环境中, 调用系统的tabbar功能('tabbar')或系统的navbar上的返回功能('backbutton'), 会触发uni的路由方法
  9. if (['tabBar', 'backbutton'].includes(to.from)) return uniRunRoute();
  10. // 获取当前路由栈信息
  11. const from = getCurStack();
  12. // 跳过 app端 首次进入页面会调用uni路由方法, 导致获取当前路由栈(from)为空,所有直接运行,不进行拦截
  13. if (from === false) return uniRunRoute();
  14. iteratorHook(
  15. this.beforeHooks,
  16. handleNextPipe.bind(this),
  17. () => {
  18. uniRunRoute();
  19. handleAfterHook.call(this, to, from);
  20. },
  21. {
  22. to,
  23. from,
  24. uniRunRoute
  25. }
  26. );
  27. };
  28. /**
  29. * 处理 全局后置钩子
  30. * @param {Object} to
  31. * @param {Object} from
  32. */
  33. const handleAfterHook = function (to, from) {
  34. this.afterHooks.forEach((hook) => {
  35. hook(to, from);
  36. });
  37. };
  38. /**
  39. * 处理 错误信息
  40. * @param {Object|string} err 错误信息、错误栈
  41. */
  42. const handleAbort = function (err) {
  43. if (this.errorCbs.length > 0) {
  44. this.errorCbs.forEach((cb) => {
  45. cb(err);
  46. });
  47. } else {
  48. print('error:' + err, 'error');
  49. }
  50. };
  51. /**
  52. * 遍历并运行 钩子
  53. * @param {Function[]} hookQueue 钩子队列
  54. * @param {Function} everyCb 每次遍历都会运行的回调函数
  55. * @param {Function} endCb 队列运行结束后运行的回调函数
  56. * @param {Object} hookOpts 钩子运行需要的参数
  57. */
  58. const iteratorHook = function (hookQueue, everyCb, endCb, hookOpts) {
  59. const step = (i) => {
  60. // 队列运行结束,运行回调函数
  61. if (i >= hookQueue.length) {
  62. endCb.call(this);
  63. } else {
  64. // 遍历运行钩子
  65. everyCb.call(this, hookQueue[i], hookOpts, (val) => {
  66. // 结束钩子遍历
  67. if (val === false) return;
  68. step(++i);
  69. });
  70. }
  71. };
  72. step(0);
  73. };
  74. /**
  75. * 处理 有next参数的钩子(前置钩子)
  76. * @param {Function} hookCb 钩子函数
  77. * @param {Object} hookOpts 钩子运行需要的参数
  78. * @param {Function} iteratorNextHook 运行下一个钩子
  79. */
  80. const handleNextPipe = function (hookCb, hookOpts, iteratorNextHook) {
  81. hookCb(hookOpts.to, hookOpts.from, (nextVal) => {
  82. try {
  83. // next(false) or next(new Error('xxx')) 中断当前的路径跳转,或中断且注册错误回调
  84. if (nextVal === false || isError(nextVal)) {
  85. handleAbort.call(this, nextVal);
  86. }
  87. // next('/pages/a') or next({ url: '/pages/a' }) 修改 路由
  88. else if (
  89. typeof nextVal === 'string' ||
  90. (typeof nextVal === 'object' && typeof nextVal.url === 'string')
  91. ) {
  92. // 处理字符串路径
  93. typeof nextVal === 'string' && (nextVal = { url: nextVal });
  94. hookOpts.uniRunRoute(nextVal);
  95. handleAfterHook.call(this, hookOpts.to, hookOpts.from);
  96. // 更新引用,替换原来的`url`字段数据
  97. hookOpts.to = Object.assign(hookOpts.to, nextVal);
  98. // 结束钩子遍历
  99. iteratorNextHook(false);
  100. }
  101. // next() 运行下一个管道(next)
  102. else {
  103. iteratorNextHook();
  104. }
  105. } catch (err) {
  106. handleAbort.call(this, err);
  107. }
  108. });
  109. };