├── .eslintignore ├── .eslintrc.js ├── .github └── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── .gitignore ├── .npmignore ├── CODE_OF_CONDUCT.md ├── LICENSE ├── README.md ├── RFC.md ├── api-extractor.json ├── dist ├── link.vue ├── uni-simple-router.d.ts └── uni-simple-router.js ├── examples ├── app-idle-toPage.zip ├── h5-scrollTo-demo.zip ├── login-auth-demo.zip └── uni-simple-router2.0 │ ├── .hbuilderx │ └── launch.json │ ├── App.vue │ ├── common │ ├── com │ │ └── com.vue │ └── comChild │ │ └── comChild.vue │ ├── dist │ ├── link.vue │ ├── plugins │ │ └── json2.js │ ├── uni-simple-router.d.ts │ ├── uni-simple-router.js │ └── uni-simple-router.js.map │ ├── hybrid │ └── html │ │ └── local.html │ ├── main.js │ ├── manifest.json │ ├── package-lock.json │ ├── package.json │ ├── pages.json │ ├── pages │ ├── 404 │ │ └── 404.vue │ ├── animation │ │ └── animation.vue │ ├── beforeRouteLeave │ │ └── beforeRouteLeave.vue │ ├── builtIn │ │ └── builtIn.vue │ ├── dynamicPage │ │ └── dynamicPage.vue │ ├── empty │ │ └── empty.vue │ ├── events │ │ └── events.vue │ ├── eventsEmit │ │ └── eventsEmit.vue │ ├── guard │ │ └── guard.vue │ ├── index │ │ └── index.vue │ ├── login │ │ └── login.vue │ ├── navigate │ │ └── navigate.vue │ ├── nvue1 │ │ └── nvue1.nvue │ ├── other │ │ └── other.vue │ ├── page2 │ │ └── page2.vue │ ├── page3 │ │ └── page3.vue │ ├── page4 │ │ └── page4.vue │ ├── relativePath │ │ ├── relativePath.vue │ │ └── relativePath2.vue │ ├── relativePathP.vue │ ├── routerlink │ │ └── routerlink.vue │ └── webView │ │ └── webView.vue │ ├── router.js │ ├── static │ ├── component.png │ ├── componentHL.png │ ├── extui.png │ ├── extuiHL.png │ ├── logo.png │ ├── wait.gif │ ├── wait2.gif │ └── wait3.gif │ ├── uni.scss │ └── vue.config.js ├── github.sh ├── jest.config.js ├── package-lock.json ├── package.json ├── publish ├── build.js └── index.js ├── src ├── H5 │ ├── buildRouter.ts │ ├── patch.ts │ └── proxyHook.ts ├── app │ └── appPatch.ts ├── applets │ └── appletPatch.ts ├── component │ └── link.vue ├── global.d.ts ├── helpers │ ├── config.ts │ ├── createRouteMap.ts │ ├── lifeCycle.ts │ ├── mixins.ts │ ├── utils.ts │ └── warn.ts ├── index.ts ├── options │ ├── base.ts │ └── config.ts └── public │ ├── beforeProxyHook.ts │ ├── hooks.ts │ ├── methods.ts │ ├── page.ts │ ├── query.ts │ ├── rewrite.ts │ ├── router.ts │ └── uniOrigin.ts ├── tea.yaml ├── test ├── path-to-regexp.spec.ts └── query-toggle.spec.ts ├── tsconfig.json └── webpack ├── webpack.common.js ├── webpack.dev.js └── webpack.prod.js /.eslintignore: -------------------------------------------------------------------------------- 1 | dist 2 | /node_modules 3 | /webpack 4 | /src/global.d.ts 5 | /test 6 | /jest.config.js 7 | /plugins -------------------------------------------------------------------------------- /.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { 4 | browser: true, 5 | node: true, 6 | es6: true 7 | }, 8 | globals: { 9 | uni: true, 10 | plus: true, 11 | getCurrentPages: true, 12 | getApp: true, 13 | __uniConfig: true, 14 | __uniRoutes: true, 15 | $npm_package_name: true, 16 | $npm_package_version: true, 17 | $npm_package_last_version: true 18 | }, 19 | parser: '@typescript-eslint/parser', 20 | extends: ['eslint:recommended'], 21 | plugins: ['@typescript-eslint'], 22 | rules: { 23 | '@typescript-eslint/consistent-type-definitions': [ 24 | 'error', 25 | 'interface' 26 | ], 27 | 'accessor-pairs': 2, 28 | 'arrow-spacing': [ 29 | 2, 30 | { 31 | before: true, 32 | after: true 33 | } 34 | ], 35 | 'block-spacing': [2, 'always'], 36 | 'brace-style': [ 37 | 2, 38 | '1tbs', 39 | { 40 | allowSingleLine: true 41 | } 42 | ], 43 | camelcase: [ 44 | 0, 45 | { 46 | properties: 'always' 47 | } 48 | ], 49 | 'comma-dangle': [2, 'never'], 50 | 'comma-spacing': [ 51 | 2, 52 | { 53 | before: false, 54 | after: true 55 | } 56 | ], 57 | 'comma-style': [2, 'last'], 58 | 'constructor-super': 2, 59 | curly: [2, 'multi-line'], 60 | 'dot-location': [2, 'property'], 61 | 'eol-last': 2, 62 | eqeqeq: ['error', 'always', {null: 'ignore'}], 63 | 'generator-star-spacing': [ 64 | 2, 65 | { 66 | before: true, 67 | after: true 68 | } 69 | ], 70 | 'handle-callback-err': [2, '^(err|error)$'], 71 | indent: ['error', 4], 72 | 'jsx-quotes': [2, 'prefer-single'], 73 | 'key-spacing': [ 74 | 2, 75 | { 76 | beforeColon: false, 77 | afterColon: true 78 | } 79 | ], 80 | 'keyword-spacing': [ 81 | 2, 82 | { 83 | before: true, 84 | after: true 85 | } 86 | ], 87 | 'new-cap': [ 88 | 2, 89 | { 90 | newIsCap: true, 91 | capIsNew: false 92 | } 93 | ], 94 | 'new-parens': 2, 95 | 'no-array-constructor': 2, 96 | 'no-caller': 2, 97 | 'no-console': 'off', 98 | 'no-class-assign': 2, 99 | 'no-cond-assign': 2, 100 | 'no-const-assign': 2, 101 | 'no-control-regex': 0, 102 | 'no-delete-var': 2, 103 | 'no-dupe-args': 2, 104 | 'no-dupe-class-members': 2, 105 | 'no-dupe-keys': 2, 106 | 'no-duplicate-case': 2, 107 | 'no-empty-character-class': 2, 108 | 'no-empty-pattern': 2, 109 | 'no-eval': 2, 110 | 'no-ex-assign': 2, 111 | 'no-extend-native': 2, 112 | 'no-extra-bind': 2, 113 | 'no-extra-boolean-cast': 2, 114 | 'no-extra-parens': [2, 'functions'], 115 | 'no-fallthrough': 2, 116 | 'no-floating-decimal': 2, 117 | 'no-func-assign': 2, 118 | 'no-implied-eval': 2, 119 | 'no-inner-declarations': [2, 'functions'], 120 | 'no-invalid-regexp': 2, 121 | 'no-irregular-whitespace': 2, 122 | 'no-iterator': 2, 123 | 'no-label-var': 2, 124 | 'no-labels': [ 125 | 2, 126 | { 127 | allowLoop: false, 128 | allowSwitch: false 129 | } 130 | ], 131 | 'no-lone-blocks': 2, 132 | 'no-mixed-spaces-and-tabs': 2, 133 | 'no-multi-spaces': 2, 134 | 'no-multi-str': 2, 135 | 'no-multiple-empty-lines': [ 136 | 2, 137 | { 138 | max: 1 139 | } 140 | ], 141 | 'no-native-reassign': 2, 142 | 'no-negated-in-lhs': 2, 143 | 'no-new-object': 2, 144 | 'no-new-require': 2, 145 | 'no-new-symbol': 2, 146 | 'no-new-wrappers': 2, 147 | 'no-obj-calls': 2, 148 | 'no-octal': 2, 149 | 'no-octal-escape': 2, 150 | 'no-path-concat': 2, 151 | 'no-proto': 2, 152 | 'no-redeclare': 2, 153 | 'no-regex-spaces': 2, 154 | 'no-return-assign': [2, 'except-parens'], 155 | 'no-self-assign': 2, 156 | 'no-self-compare': 2, 157 | 'no-sequences': 2, 158 | 'no-shadow-restricted-names': 2, 159 | 'no-spaced-func': 2, 160 | 'no-sparse-arrays': 2, 161 | 'no-this-before-super': 2, 162 | 'no-throw-literal': 2, 163 | 'no-trailing-spaces': 2, 164 | 'no-undef': 2, 165 | 'no-undef-init': 2, 166 | 'no-unexpected-multiline': 2, 167 | 'no-unmodified-loop-condition': 2, 168 | 'no-unneeded-ternary': [ 169 | 2, 170 | { 171 | defaultAssignment: false 172 | } 173 | ], 174 | 'no-unreachable': 2, 175 | 'no-unsafe-finally': 2, 176 | 'no-unused-vars': [ 177 | 2, 178 | { 179 | vars: 'all', 180 | args: 'none' 181 | } 182 | ], 183 | 'no-useless-call': 2, 184 | 'no-useless-computed-key': 2, 185 | 'no-useless-constructor': 2, 186 | 'no-useless-escape': 0, 187 | 'no-whitespace-before-property': 2, 188 | 'no-with': 2, 189 | 'one-var': [ 190 | 2, 191 | { 192 | initialized: 'never' 193 | } 194 | ], 195 | 'operator-linebreak': [ 196 | 2, 197 | 'after', 198 | { 199 | overrides: { 200 | '?': 'before', 201 | ':': 'before' 202 | } 203 | } 204 | ], 205 | 'padded-blocks': [2, 'never'], 206 | quotes: [ 207 | 2, 208 | 'single', 209 | { 210 | avoidEscape: true, 211 | allowTemplateLiterals: true 212 | } 213 | ], 214 | semi: 'off', 215 | 'semi-spacing': [ 216 | 2, 217 | { 218 | before: false, 219 | after: true 220 | } 221 | ], 222 | 'space-before-blocks': [2, 'always'], 223 | 'space-before-function-paren': [2, 'never'], 224 | 'space-in-parens': [2, 'never'], 225 | 'space-infix-ops': 2, 226 | 'space-unary-ops': [ 227 | 2, 228 | { 229 | words: true, 230 | nonwords: false 231 | } 232 | ], 233 | 'spaced-comment': [ 234 | 2, 235 | 'always', 236 | { 237 | markers: [ 238 | 'global', 239 | 'globals', 240 | 'eslint', 241 | 'eslint-disable', 242 | '*package', 243 | '!', 244 | ',' 245 | ] 246 | } 247 | ], 248 | 'template-curly-spacing': [2, 'never'], 249 | 'use-isnan': 2, 250 | 'valid-typeof': 2, 251 | 'wrap-iife': [2, 'any'], 252 | 'yield-star-spacing': [2, 'both'], 253 | yoda: [2, 'never'], 254 | 'prefer-const': 2, 255 | 'no-debugger': process.env.NODE_ENV === 'production' ? 2 : 0, 256 | 'object-curly-spacing': 'off', 257 | 'array-bracket-spacing': [2, 'never'] 258 | } 259 | }; 260 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 报告问题(Bug report) 3 | about: 详细描述你遇到的问题并寻求社区帮助 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **问题描述** 11 | [问题描述:尽可能简洁清晰地把问题描述清楚] 12 | 13 | **复现步骤** 14 | [复现问题的步骤] 15 | 1. 启动 '...' 16 | 2. 点击 '....' 17 | 3. 查看 18 | 19 | [或者可以直接贴源代码] 20 | 21 | **预期结果** 22 | [使用简洁清晰的语言描述你希望生效的预期结果] 23 | 24 | **实际结果** 25 | [这里请贴上你的报错截图或文字] 26 | 27 | 28 | **系统信息:** 29 | - 发行平台: [如 微信小程序、H5平台、5+ App等] 30 | - 操作系统 [如 iOS 12.1.2、Android 7.0] 31 | - HBuilderX版本 [如使用HBuilderX,则需提供 HBuilderX 版本号] 32 | - 项目创建方法 [如使用Vue-cli创建/HBuilderX] 33 | - 设备信息 [如 iPhone8 Plus] 34 | - uni-simple-router版本 [如 v1.5.4] 35 | 36 | 37 | **补充信息** 38 | [可选] 39 | [根据你的分析,出现这个问题的原因可能在哪里?] 40 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/feature_request.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: 建议新功能(Feature Request) 3 | about: 对 uni-simple-router 提出改善建议 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **新功能描述** 11 | 简洁描述你希望补充完善的增强功能 12 | 13 | **现状及问题** 14 | [当前现状及由此导致的不便] 15 | 16 | **尝试方案** 17 | [如果你有尝试绕开或其它解决方案,在这里描述你的建议方案] 18 | 19 | **补充信息** 20 | [其它你认为有参考价值的信息] 21 | 22 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /examples/*/node_modules 3 | /examples/*/unpackage 4 | /examples/*/uni-simple-router.js.map 5 | /temp 6 | /dist/src 7 | package-lock.json -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | /node_modules 2 | /examples 3 | /temp 4 | /dist/src 5 | /publish 6 | package-lock.json -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, sex characteristics, gender identity and expression, 9 | level of experience, education, socio-economic status, nationality, personal 10 | appearance, race, religion, or sexual identity and orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at 1606726660@qq.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html 72 | 73 | [homepage]: https://www.contributor-covenant.org 74 | 75 | For answers to common questions about this code of conduct, see 76 | https://www.contributor-covenant.org/faq 77 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 hhyang 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # uni-simple-router 2 | 3 | > 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造 4 | 5 | ## V3 版本 6 | 7 | 如果你正在使用 `vue3` + `vite` 请参考 [uni-simple-router v3 路由、拦截、最优雅的解决方案重磅来袭](https://www.hhyang.cn/blog/function-preview.html),或者查看 [官方文档](https://www.hhyang.cn/) 8 | 9 | ## 介绍 10 | 11 | `uni-simple-router` 是专为 [uni-app](https://uniapp.dcloud.io/) 打造的路由器。它与 [uni-app](https://uniapp.dcloud.io/) 核心深度集成,让 [uni-app](https://uniapp.dcloud.io/) 构建单页应用程序变得轻而易举。功能包括: 12 | 13 | * `H5端` 能完全使用 `vue-router` 进行开发。 14 | 15 | * 模块化,基于组件的路由器配置。 16 | 17 | * 路由参数,查询,通配符。 18 | 19 | * `H5端` 查看由 `uni-simple-router` 过渡系统提供动力的过渡效果。 20 | 21 | * 更细粒度的导航控制。 22 | 23 | * `H端`自动控制活动的CSS类链接。 24 | 25 | * 通配小程序端、APP端、H5端。 26 | 27 | 28 | 开始使用 [查看文档](https://v2.hhyang.cn/v2/),或 [使用示例](https://github.com/SilurianYang/uni-simple-router/tree/master/examples)(请参见下面的示例)。 29 | 30 | ## 问题 31 | 在提交问题的之前,请确保阅读 [“问题报告清单”](https://github.com/SilurianYang/uni-simple-router/issues/new?assignees=&labels=&template=bug_report.md&title=) 。不符合准则的问题可能会立即被解决。 32 | 33 | ## 贡献 34 | 提出拉取请求之前,请务必先阅读 [查看文档](https://v2.hhyang.cn/v2/)(请参见下面的示例)。。 35 | 36 | ## 变更日志 37 | [发行说明](https://github.com/SilurianYang/uni-simple-router/releases) 中记录了每个发行版的详细信息更改。 38 | 39 | ## 特别感谢 40 | 41 | 特别感谢 [markrgba](https://github.com/markrgba) 一直以来对文档和相关测试的维护。 42 | 43 | ## 技术交流 44 | 45 | uni-app  插件 46 | 47 | -------------------------------------------------------------------------------- /RFC.md: -------------------------------------------------------------------------------- 1 | ```flow 2 | 3 | st=>start: 开始跳转 4 | e=>end: 跳转结束 5 | platform=>operation: 平台选择 6 | H5=>condition: H5 7 | APP=>condition: APP 8 | applets=>condition: 小程序 9 | routerBeforeEach=>operation: routerBeforeEach 10 | lock=>condition: 跳转加锁 11 | 12 | runH5=>operation: H5 13 | runAPP=>parallel: APP 14 | runapplets=>parallel: 小程序 15 | 16 | beforeRouteLeave=>condition: beforeRouteLeave 17 | beforeEach=>condition: beforeEach 18 | beforeEnter=>condition: beforeEnter 19 | afterEach=>operation: afterEach 20 | runJump=>condition: 执行跳转成功或者失败 21 | stopJump=>operation: next(false) 停止跳转 22 | errorJump=>operation: 跳转失败 23 | routerErrorEach=>operation: routerErrorEach 24 | routerAfterEach=>operation: routerAfterEach 25 | 26 | st->platform(right)->applets(yes)->routerBeforeEach 27 | applets(no)->APP(yes)->routerBeforeEach 28 | APP(no)->H5(yes)->routerBeforeEach 29 | routerBeforeEach->lock(yes)->runAPP(path1)->runapplets(path1)->beforeRouteLeave 30 | lock(no)->runH5->beforeRouteLeave(no)->stopJump->routerErrorEach 31 | beforeRouteLeave(yes)->beforeEach(no)->stopJump->routerErrorEach 32 | beforeEach(yes)->beforeEnter(no)->stopJump->routerErrorEach 33 | beforeEnter(yes)->runJump(no)->errorJump->routerErrorEach 34 | runJump(yes)->afterEach->routerAfterEach 35 | routerAfterEach->e 36 | routerErrorEach->e 37 | 38 | ``` -------------------------------------------------------------------------------- /api-extractor.json: -------------------------------------------------------------------------------- 1 | // this the shared base config for all packages. 2 | { 3 | "$schema": "https://developer.microsoft.com/json-schemas/api-extractor/v7/api-extractor.schema.json", 4 | 5 | "mainEntryPointFilePath": "./dist/src/index.d.ts", 6 | 7 | "apiReport": { 8 | "enabled": true, 9 | "reportFolder": "./temp/" 10 | }, 11 | 12 | "docModel": { 13 | "enabled": true 14 | }, 15 | 16 | "dtsRollup": { 17 | "enabled": true, 18 | "untrimmedFilePath": "./dist/.d.ts" 19 | }, 20 | 21 | "tsdocMetadata": { 22 | "enabled": false 23 | }, 24 | 25 | "messages": { 26 | "compilerMessageReporting": { 27 | "default": { 28 | "logLevel": "warning" 29 | } 30 | }, 31 | 32 | "extractorMessageReporting": { 33 | "default": { 34 | "logLevel": "warning", 35 | "addToApiReportFile": true 36 | }, 37 | 38 | "ae-missing-release-tag": { 39 | "logLevel": "none" 40 | } 41 | }, 42 | 43 | "tsdocMessageReporting": { 44 | "default": { 45 | "logLevel": "warning" 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /dist/link.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 86 | -------------------------------------------------------------------------------- /dist/uni-simple-router.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export declare interface AppConfig { 3 | registerLoadingPage?: boolean; 4 | loadingPageStyle?: () => object; 5 | loadingPageHook?: (view: any) => void; 6 | launchedHook?: () => void; 7 | animation?: startAnimationRule; 8 | } 9 | 10 | export declare interface appletConfig { 11 | animationDuration?: number; 12 | } 13 | 14 | export declare function assertDeepObject(object: objectAny): boolean; 15 | 16 | export declare function assertNewOptions(options: T): T | never; 17 | 18 | export declare function assertParentChild(parentPath: string, vueVim: any): boolean; 19 | 20 | export declare type backTypeRule = 'backbutton' | 'navigateBack'; 21 | 22 | export declare function baseClone(source: T, target: Array | objectAny): Array | objectAny | null; 25 | 26 | export declare function copyData(object: T): T; 27 | 28 | export declare function createRouter(params: InstantiateConfig): Router; 29 | 30 | export declare interface debuggerArrayConfig { 31 | error?: boolean; 32 | warn?: boolean; 33 | log?: boolean; 34 | } 35 | 36 | export declare type debuggerConfig = boolean | debuggerArrayConfig; 37 | 38 | export declare function deepClone(source: T): T; 39 | 40 | export declare function deepDecodeQuery(query: objectAny): objectAny; 41 | 42 | export declare function def(defObject: objectAny, key: string, getValue: Function): void; 43 | 44 | export declare interface endAnimationRule { 45 | animationType?: endAnimationType; 46 | animationDuration?: number; 47 | } 48 | 49 | export declare type endAnimationType = 'slide-out-right' | 'slide-out-left' | 'slide-out-top' | 'slide-out-bottom' | 'pop-out' | 'fade-out' | 'zoom-in' | 'zoom-fade-in' | 'none'; 50 | 51 | export declare function forMatNextToFrom(router: Router, to: T, from: T): { 52 | matTo: T; 53 | matFrom: T; 54 | }; 55 | 56 | export declare function getDataType(data: T): string; 57 | 58 | export declare function getRoutePath(route: RoutesRule, router: Router): { 59 | finallyPath: string | string[]; 60 | aliasPath: string; 61 | path: string; 62 | alias: string | string[] | undefined; 63 | }; 64 | 65 | export declare function getUniCachePage(pageIndex?: number): T | []; 66 | 67 | export declare function getWildcardRule(router: Router, msg?: navErrorRule): RoutesRule | never; 68 | 69 | export declare type guardHookRule = (to: totalNextRoute, from: totalNextRoute, next: (rule?: navtoRule | false) => void) => void; 70 | 71 | export declare interface H5Config { 72 | paramsToQuery?: boolean; 73 | vueRouterDev?: boolean; 74 | vueNext?: boolean; 75 | mode?: string; 76 | base?: string; 77 | linkActiveClass?: string; 78 | linkExactActiveClass?: string; 79 | scrollBehavior?: Function; 80 | fallback?: boolean; 81 | } 82 | 83 | export declare interface h5NextRule { 84 | fullPath?: string | undefined; 85 | hash?: string | undefined; 86 | matched?: Array; 87 | meta?: object; 88 | name?: undefined | string; 89 | type?: undefined | string; 90 | } 91 | 92 | export declare type hookListRule = Array<(router: Router, to: totalNextRoute, from: totalNextRoute, toRoute: RoutesRule, next: Function) => void>; 93 | 94 | export declare interface hookObjectRule { 95 | options: Array; 96 | hook: Function; 97 | } 98 | 99 | declare type hookRule = (args: Array, next: (args: Array) => void, router: Router) => void; 100 | 101 | export declare enum hookToggle { 102 | 'beforeHooks' = "beforeEach", 103 | 'afterHooks' = "afterEach", 104 | 'enterHooks' = "beforeEnter" 105 | } 106 | 107 | export declare interface InstantiateConfig { 108 | [key: string]: any; 109 | keepUniOriginNav?: boolean; 110 | platform: platformRule; 111 | h5?: H5Config; 112 | APP?: AppConfig; 113 | applet?: appletConfig; 114 | beforeProxyHooks?: proxyHooksConfig; 115 | debugger?: debuggerConfig; 116 | routerBeforeEach?: (to: navtoRule, from: navtoRule, next: (rule?: navtoRule | false) => void) => void; 117 | routerAfterEach?: (to: navtoRule, from: navtoRule, next?: Function) => void; 118 | routerErrorEach?: (error: navErrorRule, router: Router) => void; 119 | resolveQuery?: (jsonQuery: objectAny) => objectAny; 120 | parseQuery?: (jsonQuery: objectAny) => objectAny; 121 | detectBeforeLock?: (router: Router, to: string | number | totalNextRoute | navRoute, navType: NAVTYPE) => void; 122 | routes: RoutesRule[]; 123 | } 124 | 125 | export declare interface LifeCycleConfig { 126 | beforeHooks: hookListRule; 127 | afterHooks: hookListRule; 128 | routerBeforeHooks: hookListRule; 129 | routerAfterHooks: hookListRule; 130 | routerErrorHooks: Array<(error: navErrorRule, router: Router) => void>; 131 | } 132 | 133 | export declare function lockDetectWarn(router: Router, to: string | number | totalNextRoute | navRoute, navType: NAVTYPE, next: Function, uniActualData?: uniBackApiRule | uniBackRule | undefined, passiveType?: 'beforeHooks' | 'afterHooks'): void; 134 | 135 | export declare function mergeConfig(baseConfig: T, userConfig: T): T; 136 | 137 | export declare interface navErrorRule { 138 | type: navRuleStatus; 139 | msg: string; 140 | to?: totalNextRoute; 141 | from?: totalNextRoute; 142 | nextTo?: any; 143 | [propName: string]: any; 144 | } 145 | 146 | export declare type navMethodRule = Promise; 147 | 148 | export declare interface navRoute extends h5NextRule, navtoRule { 149 | } 150 | 151 | export declare type navRuleStatus = 0 | 1 | 2 | 3; 152 | 153 | export declare interface navtoRule { 154 | NAVTYPE?: NAVTYPE; 155 | path?: string; 156 | name?: string | undefined; 157 | query?: objectAny; 158 | params?: objectAny; 159 | animationType?: startAnimationType | endAnimationType; 160 | animationDuration?: number; 161 | events?: objectAny; 162 | success?: Function; 163 | fail?: Function; 164 | complete?: Function; 165 | } 166 | 167 | export declare type NAVTYPE = 'push' | 'replace' | 'replaceAll' | 'pushTab' | 'back'; 168 | 169 | export declare enum navtypeToggle { 170 | 'push' = "navigateTo", 171 | 'replace' = "redirectTo", 172 | 'replaceAll' = "reLaunch", 173 | 'pushTab' = "switchTab", 174 | 'back' = "navigateBack" 175 | } 176 | 177 | export declare function notDeepClearNull(object: T): T; 178 | 179 | export declare function notRouteTo404(router: Router, toRoute: RoutesRule | { 180 | redirect: any; 181 | path: string; 182 | }, parseToRule: totalNextRoute, navType: NAVTYPE): RoutesRule | totalNextRoute | never; 183 | 184 | export declare type objectAny = { 185 | [propName: string]: any; 186 | }; 187 | 188 | export declare interface originMixins extends uniNavApiRule { 189 | BACKTYPE: '' | backTypeRule; 190 | } 191 | 192 | export declare type pageTypeRule = 'app' | 'page' | 'component'; 193 | 194 | export declare function paramsToQuery(router: Router, toRule: totalNextRoute | string): totalNextRoute | string; 195 | 196 | export declare type platformRule = 'h5' | 'app-plus' | 'app-lets' | 'mp-weixin' | 'mp-baidu' | 'mp-alipay' | 'mp-toutiao' | 'mp-qq' | 'mp-360'; 197 | 198 | export declare type PromiseResolve = (value?: void | PromiseLike | undefined) => void; 199 | 200 | export declare type proxyDepsRule = { 201 | resetIndex: Array; 202 | hooks: { 203 | [key: number]: { 204 | proxyHook: () => void; 205 | callHook: (enterPath: string) => void; 206 | resetHook: () => void; 207 | }; 208 | }; 209 | options: { 210 | [key: number]: Array; 211 | }; 212 | }; 213 | 214 | export declare type proxyHookName = 'beforeHooks' | 'afterHooks'; 215 | 216 | export declare interface proxyHooksConfig { 217 | onLaunch?: hookRule; 218 | onShow?: hookRule; 219 | onHide?: hookRule; 220 | onError?: hookRule; 221 | onInit?: hookRule; 222 | onLoad?: hookRule; 223 | onReady?: hookRule; 224 | onUnload?: hookRule; 225 | onResize?: hookRule; 226 | destroyed?: hookRule; 227 | created?: hookRule; 228 | beforeCreate?: hookRule; 229 | beforeMount?: hookRule; 230 | mounted?: hookRule; 231 | beforeDestroy?: hookRule; 232 | } 233 | 234 | export declare type reloadNavRule = totalNextRoute | false | undefined | string; 235 | 236 | export declare function removeSimpleValue(array: Array, value: string): Boolean; 237 | 238 | export declare type reNavMethodRule = 'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab'; 239 | 240 | export declare type reNotNavMethodRule = 'navigateBack'; 241 | 242 | export declare function resolveAbsolutePath(path: string, router: Router): string | never; 243 | 244 | export declare enum rewriteMethodToggle { 245 | 'navigateTo' = "push", 246 | 'navigate' = "push", 247 | 'redirectTo' = "replace", 248 | 'reLaunch' = "replaceAll", 249 | 'switchTab' = "pushTab", 250 | 'navigateBack' = "back" 251 | } 252 | 253 | export declare interface Router { 254 | readonly lifeCycle: LifeCycleConfig; 255 | readonly options: InstantiateConfig; 256 | $lockStatus: boolean; 257 | $route: object | null; 258 | enterPath: string; 259 | runId: number; 260 | Vue: any; 261 | appMain: { 262 | NAVTYPE: reNavMethodRule | reNotNavMethodRule; 263 | path: string; 264 | } | {}; 265 | proxyHookDeps: proxyDepsRule; 266 | routesMap: routesMapRule | {}; 267 | mount: Array<{ 268 | app: any; 269 | el: string; 270 | }>; 271 | install(Vue: any): void; 272 | push(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 273 | replace(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 274 | replaceAll(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 275 | pushTab(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 276 | back(level: number | undefined, origin?: uniBackRule | uniBackApiRule): void; 277 | forceGuardEach(navType: NAVTYPE | undefined, forceNav: boolean): void; 278 | beforeEach(userGuard: guardHookRule): void; 279 | afterEach(userGuard: (to: totalNextRoute, from: totalNextRoute) => void): void; 280 | } 281 | 282 | export declare function RouterMount(Vim: any, router: Router, el?: string | undefined): void | never; 283 | 284 | export declare interface routeRule { 285 | name: string | undefined; 286 | meta: objectAny; 287 | path: string; 288 | query: objectAny; 289 | params: objectAny; 290 | fullPath: string; 291 | NAVTYPE: NAVTYPE | ''; 292 | BACKTYPE?: backTypeRule | ''; 293 | [propName: string]: any; 294 | } 295 | 296 | export declare function routesForMapRoute(router: Router, path: string, mapArrayKey: Array, deepFind?: boolean | undefined): RoutesRule | never; 297 | 298 | export declare type routesMapKeysRule = 'finallyPathList' | 'finallyPathMap' | 'aliasPathMap' | 'pathMap' | 'nameMap' | 'vueRouteMap'; 299 | 300 | export declare interface routesMapRule { 301 | [key: string]: any; 302 | finallyPathList: Array; 303 | finallyPathMap: RoutesRule; 304 | aliasPathMap: RoutesRule; 305 | pathMap: RoutesRule; 306 | nameMap: RoutesRule; 307 | vueRouteMap: objectAny; 308 | } 309 | 310 | export declare interface RoutesRule { 311 | path: string; 312 | component?: object; 313 | name?: string; 314 | components?: object; 315 | redirect?: string | Function; 316 | props?: boolean | object | Function; 317 | aliasPath?: string; 318 | alias?: string | Array; 319 | children?: Array; 320 | beforeEnter?: guardHookRule; 321 | meta?: any; 322 | [propName: string]: any; 323 | } 324 | 325 | export declare function runtimeQuit(title?: string | undefined): void; 326 | 327 | export declare interface startAnimationRule { 328 | animationType?: startAnimationType; 329 | animationDuration?: number; 330 | } 331 | 332 | export declare type startAnimationType = 'slide-in-right' | 'slide-in-left' | 'slide-in-top' | 'slide-in-bottom' | 'pop-in' | 'fade-in' | 'zoom-out' | 'zoom-fade-out' | 'none'; 333 | 334 | export declare function timeOut(time: number): Promise; 335 | 336 | export declare interface totalNextRoute extends h5NextRule, navtoRule { 337 | path: string; 338 | delta?: number; 339 | [propName: string]: any; 340 | } 341 | 342 | export declare interface uniBackApiRule { 343 | delta?: number; 344 | animationDuration?: number; 345 | animationType?: endAnimationType; 346 | } 347 | 348 | export declare interface uniBackRule { 349 | from: backTypeRule; 350 | } 351 | 352 | export declare interface uniNavApiRule { 353 | url: string; 354 | openType?: 'appLaunch'; 355 | query?: objectAny; 356 | path?: string; 357 | delta?: number; 358 | detail?: { 359 | [propName: string]: any; 360 | }; 361 | animationType?: startAnimationType; 362 | animationDuration?: number; 363 | events?: { 364 | [propName: string]: any; 365 | }; 366 | success?: Function; 367 | fail?: Function; 368 | complete?: Function; 369 | animation?: { 370 | animationType?: startAnimationType; 371 | animationDuration?: number; 372 | }; 373 | } 374 | 375 | export declare function urlToJson(url: string): { 376 | path: string; 377 | query: objectAny; 378 | }; 379 | 380 | export declare function voidFun(...args: any): void; 381 | 382 | export declare type vueHookNameRule = 'onLaunch' | 'onShow' | 'onHide' | 'onError' | 'onInit' | 'onLoad' | 'onReady' | 'onUnload' | 'onResize' | 'created' | 'beforeMount' | 'mounted' | 'beforeDestroy' | 'destroyed'; 383 | 384 | export declare type vueOptionRule = { 385 | [propName in vueHookNameRule]: Array | undefined; 386 | }; 387 | 388 | export { } 389 | 390 | // @ts-ignore 391 | declare module 'vue/types/vue' { 392 | interface Vue { 393 | $Router: Router; 394 | $Route: routeRule; 395 | } 396 | } 397 | -------------------------------------------------------------------------------- /examples/app-idle-toPage.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/app-idle-toPage.zip -------------------------------------------------------------------------------- /examples/h5-scrollTo-demo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/h5-scrollTo-demo.zip -------------------------------------------------------------------------------- /examples/login-auth-demo.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/login-auth-demo.zip -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/.hbuilderx/launch.json: -------------------------------------------------------------------------------- 1 | { // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/ 2 | // launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数 3 | "version": "0.0", 4 | "configurations": [{ 5 | "app-plus" : 6 | { 7 | "launchtype" : "remote" 8 | }, 9 | "default" : 10 | { 11 | "launchtype" : "remote" 12 | }, 13 | "h5" : 14 | { 15 | "launchtype" : "remote" 16 | }, 17 | "mp-weixin" : 18 | { 19 | "launchtype" : "remote" 20 | }, 21 | "type" : "uniCloud" 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/App.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 29 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/common/com/com.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 39 | 40 | 43 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/common/comChild/comChild.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 34 | 35 | 37 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/dist/link.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 86 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/dist/plugins/json2.js: -------------------------------------------------------------------------------- 1 | var rx_one = /^[\],:{}\s]*$/; 2 | var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g; 3 | var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g; 4 | var rx_four = /(?:^|:|,)(?:\s*\[)+/g; 5 | var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g; 6 | var JSON={}; 7 | JSON.parse = function(text, reviver) { 8 | debugger 9 | // The parse method takes a text and an optional reviver function, and returns 10 | // a JavaScript value if the text is a valid JSON text. 11 | 12 | var j; 13 | 14 | function walk(holder, key) { 15 | 16 | // The walk method is used to recursively walk the resulting structure so 17 | // that modifications can be made. 18 | 19 | var k; 20 | var v; 21 | var value = holder[key]; 22 | if (value && typeof value === "object") { 23 | for (k in value) { 24 | if (Object.prototype.hasOwnProperty.call(value, k)) { 25 | v = walk(value, k); 26 | if (v !== undefined) { 27 | value[k] = v; 28 | } else { 29 | delete value[k]; 30 | } 31 | } 32 | } 33 | } 34 | return reviver.call(holder, key, value); 35 | } 36 | 37 | 38 | // Parsing happens in four stages. In the first stage, we replace certain 39 | // Unicode characters with escape sequences. JavaScript handles many characters 40 | // incorrectly, either silently deleting them, or treating them as line endings. 41 | 42 | text = String(text); 43 | rx_dangerous.lastIndex = 0; 44 | if (rx_dangerous.test(text)) { 45 | text = text.replace(rx_dangerous, function(a) { 46 | return ( 47 | "\\u" + 48 | ("0000" + a.charCodeAt(0).toString(16)).slice(-4) 49 | ); 50 | }); 51 | } 52 | 53 | // In the second stage, we run the text against regular expressions that look 54 | // for non-JSON patterns. We are especially concerned with "()" and "new" 55 | // because they can cause invocation, and "=" because it can cause mutation. 56 | // But just to be safe, we want to reject all unexpected forms. 57 | 58 | // We split the second stage into 4 regexp operations in order to work around 59 | // crippling inefficiencies in IE's and Safari's regexp engines. First we 60 | // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we 61 | // replace all simple value tokens with "]" characters. Third, we delete all 62 | // open brackets that follow a colon or comma or that begin the text. Finally, 63 | // we look to see that the remaining characters are only whitespace or "]" or 64 | // "," or ":" or "{" or "}". If that is so, then the text is safe for eval. 65 | 66 | if ( 67 | rx_one.test( 68 | text 69 | .replace(rx_two, "@") 70 | .replace(rx_three, "]") 71 | .replace(rx_four, "") 72 | ) 73 | ) { 74 | 75 | // In the third stage we use the eval function to compile the text into a 76 | // JavaScript structure. The "{" operator is subject to a syntactic ambiguity 77 | // in JavaScript: it can begin a block or an object literal. We wrap the text 78 | // in parens to eliminate the ambiguity. 79 | 80 | j = eval("(" + text + ")"); 81 | 82 | // In the optional fourth stage, we recursively walk the new structure, passing 83 | // each name/value pair to a reviver function for possible transformation. 84 | 85 | return (typeof reviver === "function") ? 86 | walk({ 87 | "": j 88 | }, "") : 89 | j; 90 | } 91 | 92 | // If the text is not JSON parseable, then a SyntaxError is thrown. 93 | 94 | throw new SyntaxError("JSON.parse"); 95 | }; 96 | 97 | export default JSON; -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/dist/uni-simple-router.d.ts: -------------------------------------------------------------------------------- 1 | 2 | export declare interface AppConfig { 3 | loddingPageStyle?: () => object; 4 | loddingPageHook?: (view: any) => void; 5 | launchedHook?: () => void; 6 | animation?: startAnimationRule; 7 | } 8 | 9 | export declare interface appletsVueHookConfig { 10 | app: appVueHookConfig; 11 | page: pageVueHookConfig; 12 | component: baseAppHookConfig[]; 13 | } 14 | 15 | export declare interface appVueHookConfig extends baseAppHookConfig { 16 | onLaunch: Array; 17 | onShow: Array; 18 | onHide: Array; 19 | } 20 | 21 | export declare type appVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'onLaunch' | 'onShow' | 'onHide' | 'beforeDestroy' | 'destroyed'; 22 | 23 | export declare interface baseAppHookConfig { 24 | [key: string]: Array; 25 | created: Array; 26 | beforeMount: Array; 27 | mounted: Array; 28 | beforeDestroy: Array; 29 | destroyed: Array; 30 | } 31 | 32 | export declare type comVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'beforeDestroy' | 'destroyed'; 33 | 34 | export declare function createRouter(params: InstantiateConfig): Router; 35 | 36 | export declare interface debuggerArrayConfig { 37 | error?: boolean; 38 | warn?: boolean; 39 | log?: boolean; 40 | } 41 | 42 | export declare type debuggerConfig = boolean | debuggerArrayConfig; 43 | 44 | export declare interface endAnimationRule { 45 | animationType?: endAnimationType; 46 | animationDuration?: number; 47 | } 48 | 49 | export declare type endAnimationType = 'slide-out-right' | 'slide-out-left' | 'slide-out-top' | 'slide-out-bottom' | 'pop-out' | 'fade-out' | 'zoom-in' | 'zoom-fade-in' | 'none'; 50 | 51 | export declare type guardHookRule = (to: totalNextRoute, from: totalNextRoute, next: (rule?: navtoRule | false) => void) => void; 52 | 53 | export declare interface H5Config { 54 | paramsToQuery?: boolean; 55 | vueRouterDev?: boolean; 56 | vueNext?: boolean; 57 | mode?: string; 58 | base?: string; 59 | linkActiveClass?: string; 60 | linkExactActiveClass?: string; 61 | scrollBehavior?: Function; 62 | fallback?: boolean; 63 | } 64 | 65 | export declare interface h5NextRule { 66 | fullPath?: string | undefined; 67 | hash?: string | undefined; 68 | matched?: Array; 69 | meta?: object; 70 | name?: undefined | string; 71 | type?: undefined | string; 72 | } 73 | 74 | export declare type hookListRule = Array<(router: Router, to: totalNextRoute, from: totalNextRoute, toRoute: RoutesRule, next: Function) => void>; 75 | 76 | export declare interface hookObjectRule { 77 | options: Array; 78 | hook: Function; 79 | } 80 | 81 | export declare enum hookToggle { 82 | 'beforeHooks' = "beforeEach", 83 | 'afterHooks' = "afterEach", 84 | 'enterHooks' = "beforeEnter" 85 | } 86 | 87 | export declare interface InstantiateConfig { 88 | [key: string]: any; 89 | keepUniOriginNav?: boolean; 90 | platform: 'h5' | 'app-plus' | 'app-lets' | 'mp-weixin' | 'mp-baidu' | 'mp-alipay' | 'mp-toutiao' | 'mp-qq' | 'mp-360'; 91 | h5?: H5Config; 92 | APP?: AppConfig; 93 | debugger?: debuggerConfig; 94 | routerBeforeEach?: (to: navtoRule, from: navtoRule, next: (rule?: navtoRule | false) => void) => void; 95 | routerAfterEach?: (to: navtoRule, from: navtoRule, next?: Function) => void; 96 | routerErrorEach?: (error: navErrorRule, router: Router) => void; 97 | resolveQuery?: (jsonQuery: objectAny) => objectAny; 98 | parseQuery?: (jsonQuery: objectAny) => objectAny; 99 | detectBeforeLock?: (router: Router, to: string | number | totalNextRoute | navRoute, navType: NAVTYPE) => void; 100 | routes: RoutesRule[]; 101 | } 102 | 103 | export declare interface LifeCycleConfig { 104 | beforeHooks: hookListRule; 105 | afterHooks: hookListRule; 106 | routerBeforeHooks: hookListRule; 107 | routerAfterHooks: hookListRule; 108 | routerErrorHooks: Array<(error: navErrorRule, router: Router) => void>; 109 | } 110 | 111 | export declare interface navErrorRule { 112 | type: navRuleStatus; 113 | msg: string; 114 | to?: totalNextRoute; 115 | from?: totalNextRoute; 116 | nextTo?: any; 117 | [propName: string]: any; 118 | } 119 | 120 | export declare type navMethodRule = Promise; 121 | 122 | export declare interface navRoute extends h5NextRule, navtoRule { 123 | } 124 | 125 | export declare type navRuleStatus = 0 | 1 | 2 | 3; 126 | 127 | export declare interface navtoRule { 128 | NAVTYPE?: NAVTYPE; 129 | path?: string; 130 | name?: string | undefined; 131 | query?: objectAny; 132 | params?: objectAny; 133 | animationType?: startAnimationType | endAnimationType; 134 | animationDuration?: number; 135 | events?: objectAny; 136 | success?: Function; 137 | fail?: Function; 138 | complete?: Function; 139 | } 140 | 141 | export declare type NAVTYPE = 'push' | 'replace' | 'replaceAll' | 'pushTab' | 'back'; 142 | 143 | export declare enum navtypeToggle { 144 | 'push' = "navigateTo", 145 | 'replace' = "redirectTo", 146 | 'replaceAll' = "reLaunch", 147 | 'pushTab' = "switchTab", 148 | 'back' = "navigateBack" 149 | } 150 | 151 | export declare type notCallProxyHookRule = 'onHide' | 'beforeDestroy' | 'destroyed' | 'onUnload' | 'onResize'; 152 | 153 | export declare type objectAny = { 154 | [propName: string]: any; 155 | }; 156 | 157 | export declare type pageTypeRule = 'app' | 'page' | 'component'; 158 | 159 | export declare interface pageVueHookConfig extends baseAppHookConfig { 160 | onShow: Array; 161 | onHide: Array; 162 | onLoad: Array; 163 | onReady: Array; 164 | onUnload: Array; 165 | onResize: Array; 166 | } 167 | 168 | export declare type pageVueSortHookRule = 'beforeCreate' | 'created' | 'beforeMount' | 'mounted' | 'onLoad' | 'onReady' | 'onShow' | 'onResize' | 'onHide' | 'beforeDestroy' | 'destroyed' | 'onUnload'; 169 | 170 | export declare type proxyHookName = 'beforeHooks' | 'afterHooks'; 171 | 172 | export declare type reloadNavRule = totalNextRoute | false | undefined | string; 173 | 174 | export declare type reNavMethodRule = 'navigateTo' | 'redirectTo' | 'reLaunch' | 'switchTab'; 175 | 176 | export declare type reNotNavMethodRule = 'navigateBack'; 177 | 178 | export declare enum rewriteMethodToggle { 179 | 'navigateTo' = "push", 180 | 'navigate' = "push", 181 | 'redirectTo' = "replace", 182 | 'reLaunch' = "replaceAll", 183 | 'switchTab' = "pushTab", 184 | 'navigateBack' = "back" 185 | } 186 | 187 | export declare interface Router { 188 | [key: string]: any; 189 | readonly lifeCycle: LifeCycleConfig; 190 | readonly options: InstantiateConfig; 191 | $lockStatus: boolean; 192 | $route: object | null; 193 | enterPath: string; 194 | appProxyHook: { 195 | app: appVueHookConfig; 196 | }; 197 | appMain: { 198 | NAVTYPE: reNavMethodRule | reNotNavMethodRule; 199 | path: string; 200 | } | {}; 201 | appletsProxyHook: appletsVueHookConfig; 202 | routesMap: routesMapRule | {}; 203 | mount: Array<{ 204 | app: any; 205 | el: string; 206 | }>; 207 | install(Vue: any): void; 208 | push(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 209 | replace(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 210 | replaceAll(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 211 | pushTab(to: totalNextRoute | navRoute | string, from?: totalNextRoute): void; 212 | back(level: number | undefined, origin?: uniBackRule | uniBackApiRule): void; 213 | forceGuardEach(navType: NAVTYPE | undefined, forceNav: boolean): void; 214 | beforeEach(userGuard: guardHookRule): void; 215 | afterEach(userGuard: (to: totalNextRoute, from: totalNextRoute) => void): void; 216 | } 217 | 218 | export declare function RouterMount(Vim: any, router: Router, el?: string | undefined): void | never; 219 | 220 | export declare interface routeRule { 221 | name: string | undefined; 222 | meta: objectAny; 223 | path: string; 224 | query: objectAny; 225 | params: objectAny; 226 | fullPath: string; 227 | NAVTYPE: NAVTYPE | ''; 228 | [propName: string]: any; 229 | } 230 | 231 | export declare type routesMapKeysRule = 'finallyPathList' | 'finallyPathMap' | 'aliasPathMap' | 'pathMap' | 'nameMap' | 'vueRouteMap'; 232 | 233 | export declare interface routesMapRule { 234 | [key: string]: any; 235 | finallyPathList: Array; 236 | finallyPathMap: RoutesRule; 237 | aliasPathMap: RoutesRule; 238 | pathMap: RoutesRule; 239 | nameMap: RoutesRule; 240 | vueRouteMap: objectAny; 241 | } 242 | 243 | export declare interface RoutesRule { 244 | path: string; 245 | component?: object; 246 | name?: string; 247 | components?: object; 248 | redirect?: string | Function; 249 | props?: boolean | object | Function; 250 | aliasPath?: string; 251 | alias?: string | Array; 252 | children?: Array; 253 | beforeEnter?: guardHookRule; 254 | meta?: any; 255 | [propName: string]: any; 256 | } 257 | 258 | export declare function runtimeQuit(title?: string | undefined): void; 259 | 260 | export declare interface startAnimationRule { 261 | animationType?: startAnimationType; 262 | animationDuration?: number; 263 | } 264 | 265 | export declare type startAnimationType = 'slide-in-right' | 'slide-in-left' | 'slide-in-top' | 'slide-in-bottom' | 'pop-in' | 'fade-in' | 'zoom-out' | 'zoom-fade-out' | 'none'; 266 | 267 | export declare interface totalNextRoute extends h5NextRule, navtoRule { 268 | path: string; 269 | [propName: string]: any; 270 | } 271 | 272 | export declare interface uniBackApiRule { 273 | delta?: number; 274 | animationDuration?: number; 275 | animationType?: endAnimationType; 276 | } 277 | 278 | export declare interface uniBackRule { 279 | from: string; 280 | } 281 | 282 | export declare interface uniNavApiRule { 283 | url: string; 284 | openType?: 'appLaunch'; 285 | query?: objectAny; 286 | path?: string; 287 | detail?: { 288 | [propName: string]: any; 289 | }; 290 | animationType?: startAnimationType; 291 | animationDuration?: number; 292 | events?: { 293 | [propName: string]: any; 294 | }; 295 | success?: Function; 296 | fail?: Function; 297 | complete?: Function; 298 | } 299 | 300 | export { } 301 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/hybrid/html/local.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 9 | 10 | 11 |
12 |

这是加载的本地html页面

13 | 14 |
15 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/main.js: -------------------------------------------------------------------------------- 1 | import Vue from 'vue' 2 | import App from './App' 3 | import {router, RouterMount} from './router.js' 4 | import routerLInk from './dist/link.vue' 5 | Vue.component('RouterLink1', routerLInk) 6 | 7 | Vue.use(router) 8 | 9 | // import VConsole from 'vconsole' 10 | // var vConsole = new VConsole(); 11 | 12 | App.mpType = 'app' 13 | 14 | // Vue.mixin({ 15 | // onShow(){ 16 | // if(this._uid!=0){ 17 | // router.forceGuardEach() 18 | // } 19 | // } 20 | // }) 21 | 22 | const app = new Vue({ 23 | ...App 24 | }) 25 | 26 | // v1.3.5起 H5端 你应该去除原有的app.$mount();使用路由自带的渲染方式 27 | // #ifdef H5 28 | RouterMount(app, router, '#app') 29 | // #endif 30 | 31 | // #ifndef H5 32 | app.$mount(); // 为了兼容小程序及app端必须这样写才有效果 33 | // #endif 34 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name" : "uni-simple-router2.0", 3 | "appid" : "__UNI__DB9F679", 4 | "description" : "", 5 | "versionName" : "1.0.0", 6 | "versionCode" : "100", 7 | "transformPx" : false, 8 | /* 5+App特有相关 */ 9 | "app-plus" : { 10 | "usingComponents" : true, 11 | "nvueCompiler" : "uni-app", 12 | "compilerVersion" : 3, 13 | "splashscreen" : { 14 | "alwaysShowBeforeRender" : false, 15 | "waiting" : true, 16 | "autoclose" : false, 17 | "delay" : 0 18 | }, 19 | /* 模块配置 */ 20 | "modules" : { 21 | "Statistic" : {} 22 | }, 23 | /* 应用发布信息 */ 24 | "distribute" : { 25 | /* android打包配置 */ 26 | "android" : { 27 | "permissions" : [ 28 | "", 29 | "", 30 | "", 31 | "", 32 | "", 33 | "", 34 | "", 35 | "", 36 | "", 37 | "", 38 | "", 39 | "", 40 | "", 41 | "", 42 | "", 43 | "", 44 | "", 45 | "", 46 | "", 47 | "", 48 | "" 49 | ], 50 | "autoSdkPermissions" : true 51 | }, 52 | /* ios打包配置 */ 53 | "ios" : {}, 54 | /* SDK配置 */ 55 | "sdkConfigs" : { 56 | "ad" : {} 57 | } 58 | }, 59 | "uniStatistics" : { 60 | "enable" : true 61 | } 62 | }, 63 | /* 快应用特有相关 */ 64 | "quickapp" : {}, 65 | /* 小程序特有相关 */ 66 | "mp-weixin" : { 67 | "appid" : "wx857b3a454bf68899", 68 | "setting" : { 69 | "urlCheck" : false 70 | }, 71 | "usingComponents" : true 72 | }, 73 | "mp-alipay" : { 74 | "usingComponents" : true 75 | }, 76 | "mp-baidu" : { 77 | "usingComponents" : true 78 | }, 79 | "mp-toutiao" : { 80 | "usingComponents" : true 81 | }, 82 | "uniStatistics" : { 83 | "enable" : false 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uni-simple-router2.0", 3 | "version": "1.0.0", 4 | "lockfileVersion": 1, 5 | "requires": true, 6 | "dependencies": { 7 | "uni-read-pages": { 8 | "version": "1.0.5", 9 | "resolved": "https://r.cnpmjs.org/uni-read-pages/download/uni-read-pages-1.0.5.tgz", 10 | "integrity": "sha1-RSyNyqiXe7rvYAkJvpJsjZcEOHw=" 11 | }, 12 | "vconsole": { 13 | "version": "3.3.4", 14 | "resolved": "https://registry.npmjs.org/vconsole/-/vconsole-3.3.4.tgz", 15 | "integrity": "sha512-9yihsic96NPoMLQx/lCQwH9d89H0bbMW3LZPzo/t4yGQcS1X+vTCe9OHm1XSH7WNxzGDmcSwBiKLsFGwvJpQBg==" 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uni-simple-router2.0", 3 | "version": "1.0.0", 4 | "main": "main.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "author": "", 9 | "license": "ISC", 10 | "dependencies": { 11 | "uni-read-pages": "^1.0.5", 12 | "vconsole": "^3.3.4" 13 | }, 14 | "devDependencies": {}, 15 | "keywords": [], 16 | "description": "" 17 | } 18 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages.json: -------------------------------------------------------------------------------- 1 | { 2 | "pages": [ 3 | // // #ifdef APP-PLUS 4 | // { 5 | // "path" : "pages/empty/empty", 6 | // "name":"empty", 7 | // "style" : 8 | // { 9 | // "navigationBarTitleText": "", 10 | // "enablePullDownRefresh": false 11 | // } 12 | 13 | // }, 14 | // // #endif 15 | { 16 | "path": "pages/index/index", 17 | "name": "index", 18 | "aliasPath": "/" 19 | }, 20 | { 21 | "path": "pages/login/login", 22 | "name": "login", 23 | "style": { 24 | "navigationBarTitleText": "", 25 | "enablePullDownRefresh": false 26 | } 27 | }, 28 | { 29 | "path": "pages/page2/page2", 30 | "aliasPath": "/page2/:id", 31 | "name": "page2" 32 | }, 33 | { 34 | "path": "pages/page3/page3", 35 | "aliasPath": "/:name/page3/:id", 36 | "name": "page3" 37 | }, 38 | { 39 | "path": "pages/404/404", 40 | "name": "404" 41 | }, { 42 | "path": "pages/navigate/navigate", 43 | "style": { 44 | "navigationBarTitleText": "", 45 | "enablePullDownRefresh": false 46 | } 47 | }, { 48 | "path": "pages/other/other", 49 | "name": "tabother", 50 | "style": { 51 | "navigationBarTitleText": "", 52 | "enablePullDownRefresh": false 53 | } 54 | 55 | }, { 56 | "path": "pages/page4/page4", 57 | "style": { 58 | "navigationBarTitleText": "", 59 | "enablePullDownRefresh": false 60 | } 61 | 62 | }, { 63 | "path": "pages/builtIn/builtIn", 64 | "style": { 65 | "navigationBarTitleText": "", 66 | "enablePullDownRefresh": false 67 | } 68 | 69 | }, { 70 | "path": "pages/routerlink/routerlink", 71 | "name": "routerLink", 72 | "style": { 73 | "navigationBarTitleText": "", 74 | "enablePullDownRefresh": false 75 | } 76 | 77 | }, { 78 | "path": "pages/relativePath/relativePath", 79 | "style": { 80 | "navigationBarTitleText": "", 81 | "enablePullDownRefresh": false 82 | } 83 | 84 | }, { 85 | "path": "pages/relativePath/relativePath2", 86 | "name": "relativePath2", 87 | "style": { 88 | "navigationBarTitleText": "", 89 | "enablePullDownRefresh": false 90 | } 91 | 92 | }, { 93 | "path": "pages/dynamicPage/dynamicPage", 94 | "name": "dynamicPage", 95 | "aliasPath": "/dynamicPage", 96 | "style": { 97 | "navigationBarTitleText": "", 98 | "enablePullDownRefresh": false 99 | } 100 | 101 | }, { 102 | "path": "pages/guard/guard", 103 | "name": "guard", 104 | "style": { 105 | "navigationBarTitleText": "", 106 | "enablePullDownRefresh": false 107 | } 108 | 109 | }, { 110 | "path": "pages/relativePathP", 111 | "style": { 112 | "navigationBarTitleText": "", 113 | "enablePullDownRefresh": false 114 | } 115 | 116 | }, { 117 | "path": "pages/animation/animation", 118 | "name": "animation", 119 | "style": { 120 | "navigationBarTitleText": "", 121 | "enablePullDownRefresh": false, 122 | "app-plus": { 123 | "animationType": "slide-in-bottom", 124 | "animationDuration": 600 125 | } 126 | }, 127 | "animation": { 128 | "animationType": "zoom-fade-out", 129 | "animationDuration": 500 130 | } 131 | 132 | }, { 133 | "path": "pages/beforeRouteLeave/beforeRouteLeave", 134 | "aliasPath": "/beforeRouteLeave", 135 | "name": "beforeRouteLeave", 136 | "style": { 137 | "navigationBarTitleText": "", 138 | "enablePullDownRefresh": false 139 | } 140 | 141 | }, { 142 | "path": "pages/events/events", 143 | "name": "events", 144 | "style": { 145 | "navigationBarTitleText": "", 146 | "enablePullDownRefresh": false 147 | } 148 | 149 | }, { 150 | "path": "pages/eventsEmit/eventsEmit", 151 | "name": "eventsEmit", 152 | "style": { 153 | "navigationBarTitleText": "", 154 | "enablePullDownRefresh": false 155 | } 156 | 157 | }, { 158 | "path": "pages/nvue1/nvue1", 159 | "name": "nvue1", 160 | "style": { 161 | "navigationBarTitleText": "", 162 | "enablePullDownRefresh": false 163 | } 164 | 165 | }, { 166 | "path": "pages/webView/webView", 167 | "name": "webView", 168 | "style": { 169 | "navigationBarTitleText": "", 170 | "enablePullDownRefresh": false 171 | } 172 | 173 | } 174 | 175 | ], 176 | "tabBar": { 177 | "list": [{ 178 | "pagePath": "pages/index/index", 179 | "iconPath": "static/component.png", 180 | "selectedIconPath": "static/componentHL.png", 181 | "text": "首页" 182 | }, { 183 | "pagePath": "pages/other/other", 184 | "iconPath": "static/extui.png", 185 | "selectedIconPath": "static/extuiHL.png", 186 | "text": "其他" 187 | }] 188 | }, 189 | "globalStyle": { 190 | "navigationBarTextStyle": "black", 191 | "navigationBarTitleText": "uni-app", 192 | "navigationBarBackgroundColor": "#F8F8F8", 193 | "backgroundColor": "#F8F8F8" 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/404/404.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/animation/animation.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/beforeRouteLeave/beforeRouteLeave.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 41 | 42 | 45 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/builtIn/builtIn.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 74 | 75 | 78 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/dynamicPage/dynamicPage.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 27 | 28 | 31 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/empty/empty.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 7 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/events/events.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/eventsEmit/eventsEmit.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 28 | 29 | 32 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/guard/guard.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 32 | 33 | 36 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/index/index.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 164 | 165 | 192 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/login/login.vue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 40 | 41 | 44 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/navigate/navigate.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 221 | 222 | 225 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/nvue1/nvue1.nvue: -------------------------------------------------------------------------------- 1 | 7 | 8 | 22 | 23 | 26 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/other/other.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/page2/page2.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 71 | 72 | 75 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/page3/page3.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/page4/page4.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 36 | 37 | 40 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/relativePath/relativePath.vue: -------------------------------------------------------------------------------- 1 | 25 | 26 | 42 | 43 | 46 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/relativePath/relativePath2.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 23 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/relativePathP.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 29 | 30 | 33 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/routerlink/routerlink.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 84 | 85 | 88 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/pages/webView/webView.vue: -------------------------------------------------------------------------------- 1 | 6 | 7 | 23 | 24 | 27 | -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/router.js: -------------------------------------------------------------------------------- 1 | import { 2 | RouterMount, 3 | createRouter, 4 | runtimeQuit 5 | } from './dist/uni-simple-router.js'; 6 | 7 | 8 | let pageHookAnimationEnd=null; 9 | const pageHookAnimation=new Promise(resolve=>pageHookAnimationEnd=resolve); 10 | 11 | // #ifndef APP-PLUS 12 | pageHookAnimationEnd(); 13 | // #endif 14 | 15 | let first = null; 16 | const router = createRouter({ 17 | platform: process.env.VUE_APP_PLATFORM, 18 | APP:{ 19 | animation:{ 20 | animationType:'slide-in-top', 21 | animationDuration:300 22 | }, 23 | registerLoadingPage:true, 24 | loadingPageStyle:()=>JSON.parse('{"backgroundColor":"#FFCCCC"}'), 25 | loadingPageHook:async (view)=>{ 26 | console.log('------------loadingPageHook--------------') 27 | view.show(); 28 | const [,{screenWidth}]=await uni.getSystemInfo(); 29 | view.drawBitmap('/static/wait3.gif', {}, { 30 | top: 'auto', 31 | left: 'auto', 32 | width: screenWidth+'px', 33 | height: 'auto' 34 | }); 35 | plus.navigator.closeSplashscreen(); 36 | setTimeout(()=>{ 37 | pageHookAnimationEnd(); 38 | },3500) 39 | }, 40 | launchedHook:()=>{ 41 | plus.navigator.closeSplashscreen(); 42 | console.log('APP加载完成啦') 43 | } 44 | }, 45 | applet:{ 46 | animationDuration:300 47 | }, 48 | routerErrorEach:({type,level,...args})=>{ 49 | console.log({type,level,...args}); 50 | if(type===3){ 51 | router.$lockStatus=false; 52 | // #ifdef APP-PLUS 53 | if(level==1&&args.uniActualData.from==='backbutton'){ 54 | runtimeQuit(); 55 | } 56 | // #endif 57 | }else if(type===0){ 58 | router.$lockStatus=false; 59 | } 60 | 61 | }, 62 | beforeProxyHooks: { 63 | onLaunch(options,next){ 64 | console.log(this) 65 | setTimeout(()=>{ 66 | return next([{ 67 | ...options 68 | }]) 69 | },3000) 70 | }, 71 | onLoad: function(options, next,router) { 72 | console.log(options); 73 | console.log(this) 74 | next([ 75 | {name:111} 76 | ]) 77 | }, 78 | 79 | }, 80 | debugger:false, 81 | routes: [ 82 | ...ROUTES, 83 | { 84 | path: '*', 85 | redirect:(...args)=>{ 86 | return {name:'404'} 87 | } 88 | }, 89 | ] 90 | }); 91 | console.log(router) 92 | let count=0; 93 | router.beforeEach(async (to, from, next) => { 94 | count++ 95 | // if(to.name=='index' && to.BACKTYPE=='navigateBack'){ 96 | // next(false); 97 | // }else{ 98 | // next(); 99 | // } 100 | 101 | await pageHookAnimation; 102 | next(); 103 | 104 | // if(count==1){ 105 | // next({ 106 | // path:'/pages/login/login', 107 | // NAVTYPE:'replaceAll' 108 | // }) 109 | // }else{ 110 | // next(); 111 | // } 112 | 113 | }); 114 | router.afterEach((to, from, next) => { 115 | console.log('afterEach---跳转结束') 116 | }); 117 | 118 | export { 119 | router, 120 | RouterMount 121 | } -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/component.png -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/componentHL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/componentHL.png -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/extui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/extui.png -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/extuiHL.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/extuiHL.png -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/logo.png -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/wait.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/wait.gif -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/wait2.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/wait2.gif -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/static/wait3.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SilurianYang/uni-simple-router/a10bea6db12918ed76c579d205a499f04e69cf9f/examples/uni-simple-router2.0/static/wait3.gif -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/uni.scss: -------------------------------------------------------------------------------- 1 | /** 2 | * 这里是uni-app内置的常用样式变量 3 | * 4 | * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 5 | * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App 6 | * 7 | */ 8 | 9 | /** 10 | * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 11 | * 12 | * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 13 | */ 14 | 15 | /* 颜色变量 */ 16 | 17 | /* 行为相关颜色 */ 18 | $uni-color-primary: #007aff; 19 | $uni-color-success: #4cd964; 20 | $uni-color-warning: #f0ad4e; 21 | $uni-color-error: #dd524d; 22 | 23 | /* 文字基本颜色 */ 24 | $uni-text-color:#333;//基本色 25 | $uni-text-color-inverse:#fff;//反色 26 | $uni-text-color-grey:#999;//辅助灰色,如加载更多的提示信息 27 | $uni-text-color-placeholder: #808080; 28 | $uni-text-color-disable:#c0c0c0; 29 | 30 | /* 背景颜色 */ 31 | $uni-bg-color:#ffffff; 32 | $uni-bg-color-grey:#f8f8f8; 33 | $uni-bg-color-hover:#f1f1f1;//点击状态颜色 34 | $uni-bg-color-mask:rgba(0, 0, 0, 0.4);//遮罩颜色 35 | 36 | /* 边框颜色 */ 37 | $uni-border-color:#c8c7cc; 38 | 39 | /* 尺寸变量 */ 40 | 41 | /* 文字尺寸 */ 42 | $uni-font-size-sm:24rpx; 43 | $uni-font-size-base:28rpx; 44 | $uni-font-size-lg:32rpx; 45 | 46 | /* 图片尺寸 */ 47 | $uni-img-size-sm:40rpx; 48 | $uni-img-size-base:52rpx; 49 | $uni-img-size-lg:80rpx; 50 | 51 | /* Border Radius */ 52 | $uni-border-radius-sm: 4rpx; 53 | $uni-border-radius-base: 6rpx; 54 | $uni-border-radius-lg: 12rpx; 55 | $uni-border-radius-circle: 50%; 56 | 57 | /* 水平间距 */ 58 | $uni-spacing-row-sm: 10px; 59 | $uni-spacing-row-base: 20rpx; 60 | $uni-spacing-row-lg: 30rpx; 61 | 62 | /* 垂直间距 */ 63 | $uni-spacing-col-sm: 8rpx; 64 | $uni-spacing-col-base: 16rpx; 65 | $uni-spacing-col-lg: 24rpx; 66 | 67 | /* 透明度 */ 68 | $uni-opacity-disabled: 0.3; // 组件禁用态的透明度 69 | 70 | /* 文章场景相关 */ 71 | $uni-color-title: #2C405A; // 文章标题颜色 72 | $uni-font-size-title:40rpx; 73 | $uni-color-subtitle: #555555; // 二级标题颜色 74 | $uni-font-size-subtitle:36rpx; 75 | $uni-color-paragraph: #3F536E; // 文章段落颜色 76 | $uni-font-size-paragraph:30rpx; -------------------------------------------------------------------------------- /examples/uni-simple-router2.0/vue.config.js: -------------------------------------------------------------------------------- 1 | const TransformPages = require('uni-read-pages') 2 | const {webpack} = new TransformPages() 3 | module.exports = { 4 | configureWebpack: { 5 | plugins: [ 6 | new webpack.DefinePlugin({ 7 | ROUTES: webpack.DefinePlugin.runtimeValue(() => { 8 | const tfPages = new TransformPages({ 9 | includes: ['path', 'name', 'aliasPath','animation'] 10 | }); 11 | return JSON.stringify(tfPages.routes) 12 | }, true ) 13 | }) 14 | ] 15 | } 16 | } -------------------------------------------------------------------------------- /github.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # author hhyang 4 | # home https://github.com/SilurianYang 5 | 6 | printf "\n -------------- Ctrl+D可以退出程序 --------------- \n\n" 7 | 8 | select name in "auto" "status" "add" "commit" "push" "pull" "branch" "checkout" "*"; do 9 | case "$name" in 10 | # 自动同步文件 11 | "auto") 12 | cp -avx ./examples/node_modules/uni-simple-router/* ./npm-package 13 | rm -rf ./npm-package/package-lock.json 14 | cp -avx ./README.md ./npm-package 15 | cp -avx ./package.json ./npm-package 16 | cp -avx ./npm-package/* ./src 17 | rm -rf ./src/README.md 18 | rm -rf ./src/package.json 19 | 20 | printf "\n -------------- 自动化构建目录完毕 --------------- \n\n" 21 | ;; 22 | 23 | # 查询status 24 | "status") 25 | git status 26 | printf "\n -------------- 查询完毕 --------------- \n\n" 27 | ;; 28 | 29 | # 添加文件 .或* 全部文件 可自定义文件路径 30 | "add") 31 | while read -p "请输入更多提交命令 【默认全部.】 :" add; do 32 | if [[ "$add" == "" ]]; then 33 | eval "git add ." 34 | else 35 | eval "git add ${add}" 36 | fi 37 | printf "\n -------------- 添加完成 --------------- \n\n" 38 | break 39 | done 40 | ;; 41 | 42 | # 提交文件 43 | "commit") 44 | while read -p "请输入提交信息:" readme; do 45 | if [[ "$readme" != "" ]]; then 46 | eval "git commit -m '${readme}'" 47 | printf "\n -------------- 提交本地完成 --------------- \n\n" 48 | break 49 | else 50 | printf "\n警告====> 提交信息不能为空! \n \n" 51 | fi 52 | done 53 | ;; 54 | 55 | # 推送到服务端 56 | "push") 57 | read -p "请输入提交的分支(不输入默认主分支 [master] ):" branch 58 | printf "\n\n -------------- 正在推送github,请稍后.... --------------- \n\n" 59 | if [[ "$branch" == "" ]]; then 60 | git push 61 | else 62 | eval "git push origin ${branch}" 63 | fi 64 | printf "\n -------------- 推送github完成 --------------- \n\n" 65 | ;; 66 | 67 | # 拉取最新代码 68 | "pull") 69 | printf "\n\n -------------- 正在拉取,请稍后.... --------------- \n\n" 70 | git pull 71 | printf "\n -------------- 正在拉取完成 --------------- \n\n" 72 | ;; 73 | 74 | # 切换分支操作 75 | "branch") 76 | read -p "请输入添加更多指令 【分支】 :" branchs 77 | if [[ "$branchs" == "" ]]; then 78 | printf "\n分支列表如下:\n\n" 79 | git branch 80 | else 81 | eval "git branch ${branchs}" 82 | fi 83 | printf "\n -------------- 分支操作完毕 --------------- \n\n" 84 | ;; 85 | # 86 | "checkout") 87 | read -p "请输入添加更多指令 【默认切换到master】 :" out 88 | if [[ "$out" == "" ]]; then 89 | git checkout master 90 | else 91 | eval "git checkout ${out}" 92 | fi 93 | printf "\n -------------- 执行完毕 --------------- \n\n" 94 | ;; 95 | # 自定义指令 96 | *) 97 | while read -p "请输入自定义命令 【输入:q退出】:" code; do 98 | if [[ "$code" == ":q" ]];then 99 | printf "\n" 100 | break 101 | fi 102 | printf "\n\n -------------- 正在执行,请稍后.... --------------- \n\n" 103 | eval "$code" 104 | printf "\n -------------- 执行完毕 --------------- \n\n" 105 | done 106 | esac 107 | done 108 | -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | preset: 'ts-jest', 3 | testEnvironment: 'node', 4 | moduleDirectories:['node_modules','src'] 5 | }; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "uni-simple-router", 3 | "version": "2.0.8-beta.4", 4 | "description": "> 一个更为简洁的[Vue-router](https://router.vuejs.org/zh/),专为 [uni-app](https://uniapp.dcloud.io/) 量身打造", 5 | "main": "dist/uni-simple-router.js", 6 | "types": "dist/uni-simple-router.d.ts", 7 | "dependencies": { 8 | "@microsoft/api-extractor": "^7.11.2", 9 | "@types/node": "^14.14.2", 10 | "@types/webpack": "^4.41.23", 11 | "@types/webpack-env": "^1.15.3", 12 | "copy-webpack-plugin": "^5.1.2", 13 | "jest-cli": "^26.6.3", 14 | "lodash.merge": "^4.6.2", 15 | "node-cmd": "^5.0.0", 16 | "path-to-regexp": "^1.8.0", 17 | "ts-loader": "^8.0.5", 18 | "typescript": "^4.0.3", 19 | "webpack": "^5.1.0", 20 | "webpack-cli": "^4.0.0" 21 | }, 22 | "devDependencies": { 23 | "@types/jest": "^26.0.22", 24 | "@typescript-eslint/eslint-plugin": "^4.4.1", 25 | "@typescript-eslint/parser": "^4.4.1", 26 | "eslint": "^7.11.0", 27 | "jest": "^26.6.3", 28 | "lodash": "^4.17.20", 29 | "rimraf": "^3.0.2", 30 | "ts-jest": "^26.5.5", 31 | "ts-node": "^9.0.0", 32 | "vconsole": "^3.3.4", 33 | "webpack-merge": "^5.2.0" 34 | }, 35 | "scripts": { 36 | "dev": "webpack --watch --progress --config webpack/webpack.dev.js", 37 | "dist": "webpack --progress --config webpack/webpack.prod.js", 38 | "dist:dts": "api-extractor run --local --verbose", 39 | "lint": "eslint --ext .js,.ts src", 40 | "lintFix": "eslint --ext .js,.ts src --fix", 41 | "test": "jest test/query-toggle.spec.ts", 42 | "publish": "node ./publish/index.js", 43 | "build": "node ./publish/build.js" 44 | }, 45 | "repository": { 46 | "type": "git", 47 | "url": "git+https://github.com/SilurianYang/uni-simple-router.git" 48 | }, 49 | "keywords": [ 50 | "router", 51 | "uni-app-router", 52 | "interceptor", 53 | "uni-app", 54 | "uniapp" 55 | ], 56 | "author": "hhyang", 57 | "license": "MIT", 58 | "bugs": { 59 | "url": "https://github.com/SilurianYang/uni-simple-router/issues" 60 | }, 61 | "homepage": "https://github.com/SilurianYang/uni-simple-router#readme" 62 | } 63 | -------------------------------------------------------------------------------- /publish/build.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {resolve} = require('path'); 3 | const {exec} = require('child_process'); 4 | const packageJSON = require('../package.json'); 5 | 6 | function rootPath(dir) { 7 | return resolve(process.cwd(), dir); 8 | } 9 | 10 | exec('npm run dist && npm run dist:dts', { cwd: process.cwd() }, (err, stdout, stderr) => { 11 | if (err) { 12 | return console.log(err); 13 | } 14 | 15 | console.log(stderr); 16 | 17 | const doFile = rootPath('dist/uni-simple-router.d.ts'); 18 | fs.writeFileSync(doFile, ` 19 | // @ts-ignore 20 | declare module 'vue/types/vue' { 21 | interface Vue { 22 | $Router: Router; 23 | $Route: routeRule; 24 | } 25 | } 26 | `, {flag: 'a+'}); 27 | 28 | console.log( 29 | `✅ ${packageJSON.name}@${packageJSON.version}----------------打包成功` 30 | ); 31 | }); 32 | -------------------------------------------------------------------------------- /publish/index.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const {resolve} = require('path'); 3 | const {exec} = require('child_process'); 4 | const packageJSON = require('../package.json'); 5 | 6 | function rootPath(dir) { 7 | return resolve(process.cwd(), dir); 8 | } 9 | 10 | const doFile = rootPath('package.json'); 11 | 12 | const reg = /\s+("dependencies"|"devDependencies")\s*:\s*{[^\}]+\},/g; 13 | 14 | const originalJSON = fs.readFileSync(doFile).toString(); 15 | 16 | const writeFile = originalJSON.replace(reg, ''); 17 | 18 | fs.writeFileSync(doFile, writeFile); 19 | 20 | exec('npm publish', (err, stdout, stderr) => { 21 | if (err) { 22 | return console.log(err); 23 | } 24 | console.log(stderr); 25 | console.log( 26 | `✔ ${packageJSON.name}@${packageJSON.version}----------------发布成功` 27 | ); 28 | 29 | fs.writeFileSync(doFile, originalJSON); 30 | }); 31 | -------------------------------------------------------------------------------- /src/H5/buildRouter.ts: -------------------------------------------------------------------------------- 1 | import {RoutesRule, Router, routesMapRule, totalNextRoute, hookToggle, navtoRule} from '../options/base'; 2 | import {H5Config} from '../options/config'; 3 | import {warn} from '../helpers/warn' 4 | import {getDataType, getRoutePath} from '../helpers/utils' 5 | import { onTriggerEachHook } from '../public/hooks'; 6 | 7 | export function buildVueRoutes(router: Router, vueRouteMap:RoutesRule):RoutesRule { 8 | const {pathMap, finallyPathList} = (router.routesMap as routesMapRule); 9 | const vueRoutePathList:Array = Object.keys(vueRouteMap); 10 | for (let i = 0; i < vueRoutePathList.length; i++) { 11 | const path = vueRoutePathList[i]; 12 | const myRoute:RoutesRule = pathMap[path]; 13 | const vueRoute:RoutesRule = vueRouteMap[path]; 14 | if (!myRoute) { 15 | warn(`${path} 路由地址在路由表中未找到,确定是否传递漏啦`, router, true); 16 | } else { 17 | const {finallyPath} = getRoutePath(myRoute, router); 18 | if (finallyPath instanceof Array) { 19 | throw new Error(`非 vueRouterDev 模式下,alias、aliasPath、path 无法提供数组类型! ${JSON.stringify(myRoute)}`); 20 | } 21 | if (myRoute.name != null) { 22 | vueRoute.name = myRoute.name; 23 | } 24 | const vuePath = vueRoute['path']; 25 | const vueAlias = vueRoute['alias']; 26 | delete vueRoute['alias']; 27 | vueRoute['path'] = (finallyPath as string); 28 | if (vuePath === '/' && vueAlias != null) { 29 | vueRoute['alias'] = vueAlias; 30 | vueRoute['path'] = vuePath; 31 | } 32 | const beforeEnter = myRoute.beforeEnter; 33 | if (beforeEnter) { 34 | vueRoute['beforeEnter'] = function( 35 | to:totalNextRoute, 36 | from: totalNextRoute, 37 | next:(rule?: navtoRule|false)=>void, 38 | ):void{ 39 | onTriggerEachHook(to, from, router, hookToggle['enterHooks'], next) 40 | }; 41 | } 42 | } 43 | } 44 | if (finallyPathList.includes('*')) { 45 | vueRouteMap['*'] = pathMap['*'] 46 | } 47 | return vueRouteMap 48 | } 49 | 50 | export function buildVueRouter(router:Router, vueRouter:any, vueRouteMap:RoutesRule|RoutesRule[]) :void |never { 51 | let routes:RoutesRule[] = []; 52 | if (getDataType(vueRouteMap) === '[object Array]') { 53 | routes = (vueRouteMap as RoutesRule[]); 54 | } else { 55 | routes = Object.values(vueRouteMap); 56 | } 57 | const {scrollBehavior, fallback} = router.options.h5 as H5Config; 58 | const oldScrollBehavior = vueRouter.options.scrollBehavior; 59 | vueRouter.options.scrollBehavior = function proxyScrollBehavior( 60 | to:totalNextRoute, 61 | from:totalNextRoute, 62 | savedPosition:any 63 | ) { 64 | oldScrollBehavior && oldScrollBehavior(to, from, savedPosition); 65 | return (scrollBehavior as Function)(to, from, savedPosition) 66 | } 67 | vueRouter.fallback = fallback; 68 | // Detail see: https://github.com/vuejs/vue-router/issues/1234#issuecomment-357941465 69 | const newVueRouter:any = new vueRouter.constructor({ 70 | ...router.options.h5, 71 | base: vueRouter.options.base, 72 | mode: vueRouter.options.mode, 73 | routes 74 | }); 75 | vueRouter.matcher = newVueRouter.matcher; 76 | } 77 | -------------------------------------------------------------------------------- /src/H5/patch.ts: -------------------------------------------------------------------------------- 1 | import { removeSimpleValue } from '../helpers/utils'; 2 | import { Router} from '../options/base'; 3 | 4 | let [dynamicCacheName, __id__] = ['', '']; 5 | 6 | export const addKeepAliveInclude = function( 7 | router:Router 8 | ):void{ 9 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/316 2021年12月10日14:30:13 10 | const app = getApp(); 11 | const keepAliveInclude:Array = app.keepAliveInclude; 12 | if (router.runId === 0 && keepAliveInclude.length === 0) { 13 | __id__ = app.$route.params.__id__; 14 | dynamicCacheName = app.$route.meta.name; 15 | const cacheId = dynamicCacheName + '-' + __id__; 16 | app.keepAliveInclude.push(cacheId) 17 | } else { 18 | if (dynamicCacheName !== '') { 19 | const arrayCacheId = app.keepAliveInclude; 20 | for (let i = 0; i < arrayCacheId.length; i++) { 21 | const cacheId:string = arrayCacheId[i]; 22 | const cacheIdReg = new RegExp(`${dynamicCacheName}-(\\d+)$`); 23 | const firstCacheId = `${dynamicCacheName}-${__id__}`; 24 | if (cacheIdReg.test(cacheId) && cacheId !== firstCacheId) { 25 | removeSimpleValue(arrayCacheId, firstCacheId); 26 | dynamicCacheName = ''; 27 | break; 28 | } 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /src/H5/proxyHook.ts: -------------------------------------------------------------------------------- 1 | import {Router, proxyHookName, totalNextRoute, navtoRule} from '../options/base'; 2 | 3 | export class MyArray extends Array { 4 | constructor( 5 | private router:Router, 6 | private vueEachArray:Array, 7 | private myEachHook:Function, 8 | private hookName:'beforeHooks'| 'afterHooks', 9 | ) { 10 | super(); 11 | Object.setPrototypeOf(this, MyArray.prototype) 12 | } 13 | push(v:any):any { 14 | this.vueEachArray.push(v); 15 | const index = this.length; 16 | this[this.length] = (to: totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void) => { 17 | if (index > 0) { 18 | this.vueEachArray[index](to, from, () => { 19 | next && next() 20 | }); 21 | } else { 22 | this.myEachHook(to, from, (nextTo?:navtoRule|false) => { 23 | // Fixe https://github.com/SilurianYang/uni-simple-router/issues/241 2021年3月6日22:15:27 24 | // 目前不调用uni-app的守卫函数,因为会丢失页面栈信息 25 | if (nextTo === false) { 26 | next(false); 27 | } else { 28 | this.vueEachArray[index](to, from, (uniNextTo?:navtoRule|false) => { 29 | next(nextTo); 30 | }) 31 | } 32 | }, this.router, true); 33 | } 34 | }; 35 | } 36 | } 37 | 38 | export function proxyEachHook(router:Router, vueRouter:any):void { 39 | const hookList:Array<'beforeHooks'| 'afterHooks'> = ['beforeHooks', 'afterHooks']; 40 | for (let i = 0; i < hookList.length; i++) { 41 | const hookName = hookList[i]; 42 | const myEachHook = router.lifeCycle[(hookName as proxyHookName)][0]; 43 | if (myEachHook) { 44 | const vueEachArray:Array = vueRouter[hookName]; 45 | vueRouter[hookName] = new MyArray(router, vueEachArray, myEachHook, hookName); 46 | } 47 | } 48 | } 49 | export function proxyH5Mount(router:Router):void { 50 | if (router.mount.length === 0) { 51 | if (router.options.h5?.vueRouterDev) { 52 | return 53 | } 54 | const uAgent = navigator.userAgent; 55 | const isIos = !!uAgent.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/) 56 | if (isIos) { 57 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/109 58 | setTimeout(() => { 59 | const element = document.getElementsByTagName('uni-page'); 60 | if (element.length > 0) { 61 | return false 62 | } 63 | window.location.reload(); 64 | }, 0); 65 | } 66 | } else { 67 | const [{app}] = router.mount; 68 | app.$mount(); 69 | router.mount = []; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/app/appPatch.ts: -------------------------------------------------------------------------------- 1 | import { navtoRule, objectAny, Router, totalNextRoute } from '../options/base'; 2 | import { AppConfig } from '../options/config'; 3 | 4 | let quitBefore:number|null = null; 5 | let TABBAR:objectAny|null = null; 6 | 7 | export function registerLoddingPage( 8 | router:Router, 9 | ):void{ 10 | if (router.options.APP?.registerLoadingPage) { 11 | const { loadingPageHook, loadingPageStyle } = router.options.APP as AppConfig; // 获取app所有配置 12 | const view = new plus.nativeObj.View('router-loadding', { 13 | top: '0px', 14 | left: '0px', 15 | height: '100%', 16 | width: '100%', 17 | ...(loadingPageStyle as Function)() 18 | }); 19 | (loadingPageHook as Function)(view); // 触发等待页面生命周期 20 | } 21 | } 22 | 23 | export function runtimeQuit( 24 | title:string|undefined = '再按一次退出应用' 25 | ):void{ 26 | const nowTime = +new Date(); 27 | if (!quitBefore) { 28 | quitBefore = nowTime; 29 | uni.showToast({ 30 | title, 31 | icon: 'none', 32 | position: 'bottom', 33 | duration: 1000 34 | }); 35 | setTimeout(() => { quitBefore = null }, 1000); 36 | } else { 37 | if (nowTime - quitBefore < 1000) { 38 | plus.runtime.quit(); 39 | } 40 | } 41 | } 42 | 43 | export function HomeNvueSwitchTab( 44 | router:Router, 45 | to:navtoRule, 46 | oldMethod:Function 47 | ):Promise { 48 | return new Promise(( 49 | resolve:(value:boolean)=>void 50 | ) => { 51 | if (router.runId !== 0) { 52 | return resolve(false) 53 | } 54 | if (!(__uniConfig.tabBar && Array.isArray(__uniConfig.tabBar.list))) { 55 | return resolve(false) 56 | } 57 | // Fixe https://github.com/SilurianYang/uni-simple-router/issues/373 2022-4-3 19:40:59 58 | oldMethod({ 59 | url: __uniConfig.entryPagePath, 60 | animationDuration:0, 61 | complete: () => resolve(true) 62 | }); 63 | }) 64 | } 65 | 66 | export function tabIndexSelect( 67 | to:totalNextRoute, 68 | from:totalNextRoute 69 | ):boolean { 70 | if (!(__uniConfig.tabBar && Array.isArray(__uniConfig.tabBar.list))) { 71 | return false 72 | } 73 | const tabBarList = __uniConfig.tabBar.list; 74 | const routes:Array = []; 75 | let activeIndex:number = 0; 76 | for (let i = 0; i < tabBarList.length; i++) { 77 | const route:totalNextRoute = tabBarList[i]; 78 | if ('/' + route.pagePath === to.path || '/' + route.pagePath === from.path) { 79 | if (route.pagePath === from.path) { 80 | activeIndex = i; 81 | } 82 | routes.push(route); 83 | } 84 | if (routes.length === 2) { 85 | break 86 | } 87 | } 88 | if (routes.length !== 2) { 89 | return false 90 | } 91 | if (TABBAR == null) { 92 | TABBAR = uni.requireNativePlugin('uni-tabview') 93 | } 94 | (TABBAR as objectAny).switchSelect({ 95 | index: activeIndex 96 | }) 97 | return true 98 | } 99 | -------------------------------------------------------------------------------- /src/applets/appletPatch.ts: -------------------------------------------------------------------------------- 1 | import { Router} from '../options/base'; 2 | 3 | export function getEnterPath( 4 | vueVim:any, 5 | router:Router, 6 | ) :string { 7 | switch (router.options.platform) { 8 | case 'mp-alipay': 9 | case 'mp-weixin': 10 | case 'mp-toutiao': 11 | case 'mp-qq': 12 | return vueVim.$options.mpInstance.route; 13 | case 'mp-baidu': 14 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/251 15 | return vueVim.$options.mpInstance.is || vueVim.$options.mpInstance.pageinstance.route; 16 | } 17 | return vueVim.$options.mpInstance.route; // 这是暂时的 因为除了以上的小程序 其他没测试 先这样写 18 | } 19 | -------------------------------------------------------------------------------- /src/component/link.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 86 | -------------------------------------------------------------------------------- /src/global.d.ts: -------------------------------------------------------------------------------- 1 | declare var uni:any; 2 | declare var plus:any; 3 | declare var __uniConfig:any; 4 | declare var __uniRoutes:any; 5 | declare function getCurrentPages(isAll:boolean|undefined=false):any; 6 | declare function getApp(args?:{allowDefault: true}):any; 7 | declare var $npm_package_name:string; 8 | declare var $npm_package_version:string; 9 | declare var $npm_package_last_version:string; -------------------------------------------------------------------------------- /src/helpers/config.ts: -------------------------------------------------------------------------------- 1 | import {err} from './warn' 2 | import { InstantiateConfig, LifeCycleConfig} from '../options/config' 3 | import { vueHookNameRule, proxyDepsRule } from '../options/base'; 4 | import { parseQuery } from '../public/query'; 5 | 6 | export const mpPlatformReg = '(^mp-weixin$)|(^mp-baidu$)|(^mp-alipay$)|(^mp-toutiao$)|(^mp-qq$)|(^mp-360$)' // 小程序下不能直接导出正则 需要重新组装成正则 不然bug一推 诡异 7 | 8 | export const baseConfig:InstantiateConfig = { 9 | h5: { 10 | paramsToQuery: false, 11 | vueRouterDev: false, 12 | vueNext: false, 13 | mode: 'hash', 14 | base: '/', 15 | linkActiveClass: 'router-link-active', 16 | linkExactActiveClass: 'router-link-exact-active', 17 | scrollBehavior: (to:any, from:any, savedPostion:Function) => ({ x: 0, y: 0 }), 18 | fallback: true 19 | }, 20 | APP: { 21 | registerLoadingPage: true, 22 | loadingPageStyle: () => JSON.parse('{"backgroundColor":"#FFF"}'), 23 | loadingPageHook: (view:any) => { view.show(); }, 24 | launchedHook: () => { plus.navigator.closeSplashscreen(); }, 25 | animation: {} 26 | }, 27 | applet: { 28 | animationDuration: 300 29 | }, 30 | beforeProxyHooks: { 31 | onLoad: ([options], next, router) => { 32 | next([parseQuery({query: options}, router)]) 33 | } 34 | }, 35 | platform: 'h5', 36 | keepUniOriginNav: false, 37 | debugger: false, 38 | routerBeforeEach: (to, from, next) => { next() }, 39 | routerAfterEach: (to, from) => {}, 40 | routerErrorEach: (error, router) => { router.$lockStatus = false; err(error, router, true); }, 41 | detectBeforeLock: (router, to, navType) => {}, 42 | routes: [ 43 | { 44 | path: '/choose-location' 45 | }, 46 | { 47 | path: '/open-location' 48 | }, 49 | { 50 | path: '/preview-image' 51 | } 52 | ] 53 | } 54 | 55 | export const lifeCycle:LifeCycleConfig = { 56 | beforeHooks: [], 57 | afterHooks: [], 58 | routerBeforeHooks: [], 59 | routerAfterHooks: [], 60 | routerErrorHooks: [] 61 | }; 62 | 63 | export const proxyHookDeps:proxyDepsRule = { 64 | resetIndex: [], // 还原时执行的生命周期的索引 65 | hooks: {}, 66 | options: {} 67 | } 68 | 69 | export const proxyHookName:Array = [ 70 | 'onLaunch', 71 | 'onShow', 72 | 'onHide', 73 | 'onError', 74 | 'onInit', 75 | 'onLoad', 76 | 'onReady', 77 | 'onUnload', 78 | 'onResize', 79 | 'created', 80 | 'beforeMount', 81 | 'mounted', 82 | 'beforeDestroy', 83 | 'destroyed' 84 | ] 85 | -------------------------------------------------------------------------------- /src/helpers/createRouteMap.ts: -------------------------------------------------------------------------------- 1 | import {RoutesRule, Router, routesMapRule} from '../options/base'; 2 | import {H5Config} from '../options/config'; 3 | import {warn} from './warn' 4 | import {getRoutePath} from './utils' 5 | 6 | export function createRouteMap( 7 | router: Router, 8 | routes: RoutesRule[], 9 | ): routesMapRule|never { 10 | const routesMap:routesMapRule = { 11 | finallyPathList: [], 12 | finallyPathMap: Object.create(null), 13 | aliasPathMap: Object.create(null), 14 | pathMap: Object.create(null), 15 | vueRouteMap: Object.create(null), 16 | nameMap: Object.create(null) 17 | } 18 | routes.forEach(route => { 19 | const { finallyPath, aliasPath, path} = getRoutePath(route, router); 20 | if (path == null) { 21 | throw new Error(`请提供一个完整的路由对象,包括以绝对路径开始的 ‘path’ 字符串 ${JSON.stringify(route)}`); 22 | } 23 | if (finallyPath instanceof Array) { 24 | if (!(router.options.h5 as H5Config).vueRouterDev && router.options.platform === 'h5') { 25 | throw new Error(`非 vueRouterDev 模式下,route.alias 目前无法提供数组类型! ${JSON.stringify(route)}`); 26 | } 27 | } 28 | const strFinallyPath = (finallyPath as string); 29 | const strAliasPath = (aliasPath as string); 30 | if (router.options.platform !== 'h5') { 31 | if (strFinallyPath.indexOf('/') !== 0 && path !== '*') { 32 | warn(`当前路由对象下,route:${JSON.stringify(route)} 是否缺少了前缀 ‘/’`, router, true); 33 | } 34 | } 35 | if (!routesMap.finallyPathMap[strFinallyPath]) { 36 | routesMap.finallyPathMap[strFinallyPath] = route; 37 | routesMap.aliasPathMap[strAliasPath] = route; 38 | routesMap.pathMap[path] = route; 39 | routesMap.finallyPathList.push(strFinallyPath); 40 | if (route.name != null) { 41 | routesMap.nameMap[route.name] = route; 42 | } 43 | } 44 | }) 45 | 46 | return routesMap; 47 | } 48 | -------------------------------------------------------------------------------- /src/helpers/lifeCycle.ts: -------------------------------------------------------------------------------- 1 | import { navtoRule, navErrorRule, Router, proxyHookName, guardHookRule, totalNextRoute, hookToggle} from '../options/base'; 2 | import { LifeCycleConfig, InstantiateConfig} from '../options/config'; 3 | import {onTriggerEachHook} from '../public/hooks' 4 | 5 | export function registerHook(list:Array, fn:Function):void { 6 | list[0] = fn; 7 | } 8 | 9 | export function registerRouterHooks(cycleHooks:T, options:InstantiateConfig):T { 10 | registerHook(cycleHooks.routerBeforeHooks, function(to:totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void):void { 11 | (options.routerBeforeEach as Function)(to, from, next); 12 | }) 13 | registerHook(cycleHooks.routerAfterHooks, function(to:totalNextRoute, from: totalNextRoute):void { 14 | (options.routerAfterEach as Function)(to, from); 15 | }) 16 | registerHook(cycleHooks.routerErrorHooks, function(error:navErrorRule, router:Router):void { 17 | (options.routerErrorEach as Function)(error, router); 18 | }) 19 | return cycleHooks; 20 | } 21 | 22 | export function registerEachHooks(router:Router, hookType:proxyHookName, userGuard:guardHookRule) { 23 | registerHook(router.lifeCycle[hookType], function( 24 | to:totalNextRoute, 25 | from: totalNextRoute, 26 | next:(rule?: navtoRule|false)=>void, 27 | router:Router, 28 | auto:boolean, 29 | ):void { 30 | if (auto) { // h5端 vue-router自动触发 非自己调用触发 31 | onTriggerEachHook(to, from, router, hookToggle[hookType], next) 32 | } else { 33 | userGuard(to, from, next) 34 | } 35 | }) 36 | } 37 | -------------------------------------------------------------------------------- /src/helpers/mixins.ts: -------------------------------------------------------------------------------- 1 | import { Router, routesMapRule, RoutesRule, pageTypeRule} from '../options/base'; 2 | import {createRouteMap} from '../helpers/createRouteMap' 3 | import {buildVueRoutes, buildVueRouter} from '../H5/buildRouter' 4 | import {proxyEachHook} from '../H5/proxyHook' 5 | import {registerLoddingPage} from '../app/appPatch'; 6 | import { proxyPageHook } from '../public/page'; 7 | import { forceGuardEach } from '../public/methods'; 8 | import { assertParentChild, voidFun } from './utils'; 9 | import { getEnterPath } from '../applets/appletPatch'; 10 | import { mpPlatformReg } from './config'; 11 | import {beforeProxyHook} from '../public/beforeProxyHook' 12 | 13 | let registerRouter:boolean = false; 14 | let onloadProxyOk:boolean = false; 15 | 16 | const appletProxy:{ 17 | app:boolean; 18 | page:string; 19 | } = { 20 | app: false, 21 | page: '' 22 | } 23 | 24 | export function getMixins(Vue:any, router: Router):{ 25 | beforeCreate(this: any): void; 26 | } | { 27 | beforeCreate(): void; 28 | } | { 29 | onLaunch(): void; 30 | } { 31 | let platform = router.options.platform; 32 | if (new RegExp(mpPlatformReg, 'g').test(platform)) { 33 | platform = 'app-lets'; 34 | } 35 | const toggleHooks = { 36 | h5: { 37 | beforeCreate(this: any): void { 38 | beforeProxyHook(this, router); 39 | if (this.$options.router) { 40 | router.$route = this.$options.router; // 挂载vue-router到路由对象下 41 | let vueRouteMap:RoutesRule[]|RoutesRule = []; 42 | if (router.options.h5?.vueRouterDev) { 43 | vueRouteMap = router.options.routes; 44 | } else { 45 | vueRouteMap = createRouteMap(router, this.$options.router.options.routes).finallyPathMap; 46 | (router.routesMap as routesMapRule).vueRouteMap = vueRouteMap; 47 | buildVueRoutes(router, vueRouteMap); 48 | } 49 | buildVueRouter(router, this.$options.router, vueRouteMap); 50 | proxyEachHook(router, this.$options.router); 51 | } 52 | } 53 | }, 54 | 'app-plus': { 55 | beforeCreate(this: any): void { 56 | beforeProxyHook(this, router); 57 | if (!registerRouter) { 58 | registerRouter = true; 59 | proxyPageHook(this, router, 'app'); 60 | registerLoddingPage(router); 61 | } 62 | } 63 | }, 64 | 'app-lets': { 65 | beforeCreate(this: any): void { 66 | beforeProxyHook(this, router); 67 | 68 | // 保证这个函数不会被重写 69 | const pluginMark = $npm_package_name; 70 | voidFun(pluginMark); 71 | 72 | let isProxy:boolean = true; 73 | const pageType:pageTypeRule = this.$options.mpType; 74 | 75 | if (onloadProxyOk) { 76 | return 77 | } 78 | 79 | if (pageType === 'component') { 80 | isProxy = assertParentChild(appletProxy['page'], this); 81 | } else { 82 | if (pageType === 'page') { 83 | appletProxy[pageType] = getEnterPath(this, router); 84 | router.enterPath = appletProxy[pageType]; // 我不确定在不同端是否都是同样的变现?可能有的为非绝对路径? 85 | } else { 86 | appletProxy[pageType] = true; 87 | } 88 | } 89 | if (isProxy) { 90 | proxyPageHook(this, router, pageType); 91 | } 92 | }, 93 | onLoad(this: any):void{ 94 | // 保证这个函数不会被重写,否则必须在启动页写onLoad 95 | const pluginMark = $npm_package_name; 96 | voidFun(pluginMark); 97 | 98 | if (!onloadProxyOk && assertParentChild(appletProxy['page'], this)) { 99 | onloadProxyOk = true; 100 | forceGuardEach(router); 101 | } 102 | } 103 | } 104 | }; 105 | return toggleHooks[(platform as 'h5'|'app-plus'|'app-lets')]; 106 | } 107 | export function initMixins(Vue: any, router: Router) { 108 | const routesMap = createRouteMap(router, router.options.routes); 109 | router.routesMap = routesMap; // 挂载自身路由表到路由对象下 110 | // Vue.util.defineReactive(router, '_Route', createRoute(router, 19970806)) 111 | Vue.mixin({ 112 | ...getMixins(Vue, router) 113 | }); 114 | } 115 | -------------------------------------------------------------------------------- /src/helpers/warn.ts: -------------------------------------------------------------------------------- 1 | 2 | import {debuggerConfig, debuggerArrayConfig} from '../options/config' 3 | import {Router} from '../options/base' 4 | 5 | type callType='error'|'warn'|'log'; 6 | 7 | export function isLog(type:callType, dev:debuggerConfig, errText:any, enforce:boolean = false):boolean { 8 | if (!enforce) { 9 | const isObject = dev.toString() === '[object Object]'; 10 | if (dev === false) { 11 | return false 12 | } else if (isObject) { 13 | if ((dev as debuggerArrayConfig)[type] === false) { 14 | return false; 15 | } 16 | } 17 | } 18 | console[type](errText); 19 | return true; 20 | } 21 | export function err(errText:any, router:Router, enforce?:boolean):void { 22 | const dev = (router.options.debugger as debuggerConfig); 23 | isLog('error', dev, errText, enforce); 24 | } 25 | 26 | export function warn(errText:any, router:Router, enforce?:boolean):void { 27 | const dev = (router.options.debugger as debuggerConfig); 28 | isLog('warn', dev, errText, enforce); 29 | } 30 | 31 | export function log(errText:any, router:Router, enforce?:boolean):void { 32 | const dev = (router.options.debugger as debuggerConfig); 33 | isLog('log', dev, errText, enforce); 34 | } 35 | export function warnLock(errText:any):void { 36 | console.warn(errText); 37 | } 38 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export * from './options/base' 2 | export * from './options/config' 3 | export * from './helpers/utils' 4 | 5 | export { 6 | runtimeQuit 7 | } from './app/appPatch' 8 | 9 | export { 10 | RouterMount, 11 | createRouter 12 | } from './public/router' 13 | 14 | const version = $npm_package_version; 15 | if (/[A-Z]/g.test(version)) { 16 | console.warn(`【${$npm_package_name.toLocaleLowerCase()} 提示】:当前版本 ${version.toLocaleLowerCase()} 此版本为测试版。有BUG请退回正式版,线上正式版本:${$npm_package_last_version}`) 17 | } 18 | -------------------------------------------------------------------------------- /src/options/base.ts: -------------------------------------------------------------------------------- 1 | import {InstantiateConfig, LifeCycleConfig} from '../options/config'; 2 | 3 | export enum hookToggle{ 4 | 'beforeHooks'='beforeEach', 5 | 'afterHooks'='afterEach', 6 | 'enterHooks'='beforeEnter' 7 | } 8 | export enum navtypeToggle{ 9 | 'push'='navigateTo', 10 | 'replace'='redirectTo', 11 | 'replaceAll'='reLaunch', 12 | 'pushTab'='switchTab', 13 | 'back'='navigateBack' 14 | } 15 | export enum rewriteMethodToggle{ 16 | 'navigateTo'='push', 17 | 'navigate'='push', 18 | 'redirectTo'='replace', 19 | 'reLaunch'='replaceAll', 20 | 'switchTab'='pushTab', 21 | 'navigateBack'='back', 22 | } 23 | export type proxyDepsRule={ 24 | resetIndex:Array; 25 | hooks: { 26 | [key: number]:{ 27 | proxyHook:()=>void; 28 | callHook:(enterPath:string)=>void; 29 | resetHook: ()=>void 30 | } 31 | }; 32 | options: {[key: number]: Array;}; 33 | }; 34 | export type backTypeRule='backbutton'|'navigateBack' 35 | export type pageTypeRule='app'|'page'|'component'; 36 | export type vueHookNameRule='onLaunch'|'onShow'|'onHide'|'onError'|'onInit'|'onLoad'|'onReady'|'onUnload'|'onResize'|'created'|'beforeMount'|'mounted'|'beforeDestroy'|'destroyed' 37 | export type reNavMethodRule='navigateTo'|'redirectTo'|'reLaunch'|'switchTab'; 38 | export type reNotNavMethodRule='navigateBack'; 39 | export type reloadNavRule=totalNextRoute | false | undefined|string; 40 | export type hookListRule=Array<(router:Router, to:totalNextRoute, from: totalNextRoute, toRoute:RoutesRule,next:Function)=>void> 41 | export type guardHookRule=(to: totalNextRoute, from: totalNextRoute, next:(rule?: navtoRule|false)=>void)=>void; 42 | export type navRuleStatus= 0|1|2|3; //0: next(false) 1:next(unknownType) 2:加锁状态,禁止跳转 3:在获取页面栈的时候,页面栈不够level获取 43 | export type proxyHookName='beforeHooks'|'afterHooks'; 44 | export type navMethodRule = Promise; 45 | export type objectAny={[propName: string]: any;}; 46 | export type NAVTYPE = 'push' | 'replace' | 'replaceAll' | 'pushTab'|'back'; 47 | export type startAnimationType = 48 | | 'slide-in-right' 49 | | 'slide-in-left' 50 | | 'slide-in-top' 51 | | 'slide-in-bottom' 52 | | 'pop-in' 53 | | 'fade-in' 54 | | 'zoom-out' 55 | | 'zoom-fade-out' 56 | | 'none'; 57 | export type endAnimationType = 58 | | 'slide-out-right' 59 | | 'slide-out-left' 60 | | 'slide-out-top' 61 | | 'slide-out-bottom' 62 | | 'pop-out' 63 | | 'fade-out' 64 | | 'zoom-in' 65 | | 'zoom-fade-in' 66 | | 'none'; 67 | 68 | export type vueOptionRule = { 69 | [propName in vueHookNameRule]: Array | undefined; 70 | }; 71 | 72 | // 跳转api时,传递的跳转规则 73 | export interface navtoRule { 74 | NAVTYPE?: NAVTYPE; // 跳转类型 v1.1.0+ 75 | path?: string; // 跳转路径 绝对路径 76 | name?: string | undefined; // 跳转路径名称 77 | query?: objectAny; // 跳转使用path时 query包含需要传递的参数 78 | params?: objectAny; // 跳转使用name时 params包含需要传递的参数 79 | animationType?: startAnimationType|endAnimationType; 80 | animationDuration?: number; 81 | events?: objectAny; 82 | success?: Function; 83 | fail?: Function; 84 | complete?: Function; 85 | } 86 | // h5 next管道函数中传递的from及to对象 87 | export interface h5NextRule { 88 | fullPath?: string | undefined; 89 | hash?: string | undefined; 90 | matched?: Array; 91 | meta?: object; 92 | name?: undefined | string; 93 | type?: undefined | string; 94 | } 95 | 96 | export interface totalNextRoute extends h5NextRule, navtoRule { 97 | path:string; 98 | delta?:number; 99 | [propName: string]: any; 100 | } 101 | export interface navRoute extends h5NextRule, navtoRule { 102 | 103 | } 104 | 105 | // 开始切换窗口动画 app端可用 106 | export interface startAnimationRule { 107 | animationType?: startAnimationType; // 窗口关闭的动画效果 108 | animationDuration?: number; // 窗口关闭动画的持续时间 109 | } 110 | // 关闭窗口时的动画 app端可用 111 | export interface endAnimationRule { 112 | animationType?: endAnimationType; // 窗口关闭的动画效果 113 | animationDuration?: number; // 窗口关闭动画的持续时间 114 | } 115 | export interface hookObjectRule { 116 | options:Array; 117 | hook:Function; 118 | } 119 | 120 | // 执行路由跳转失败或者 next(false) 时走的规则 121 | export interface navErrorRule { 122 | type: navRuleStatus; 123 | msg: string; 124 | to?:totalNextRoute; 125 | from?:totalNextRoute; 126 | nextTo?:any; 127 | [propName:string]:any; 128 | } 129 | // uni原生api跳转时的规则 130 | export interface uniNavApiRule { 131 | url: string; 132 | openType?:'appLaunch', 133 | query?:objectAny; 134 | path?:string; 135 | delta?:number; 136 | detail?:{[propName:string]:any}; 137 | animationType?:startAnimationType; 138 | animationDuration?:number; 139 | events?:{[propName:string]:any}; 140 | success?:Function; 141 | fail?:Function; 142 | complete?:Function; 143 | animation?:{ 144 | animationType?:startAnimationType; 145 | animationDuration?:number; 146 | } 147 | } 148 | 149 | export interface originMixins extends uniNavApiRule{ 150 | BACKTYPE:''|backTypeRule 151 | } 152 | 153 | // uni-app 原始返回api 回调参数 154 | export interface uniBackRule{ 155 | from:backTypeRule; 156 | } 157 | 158 | export interface uniBackApiRule{ 159 | delta?: number; 160 | animationDuration?: number; 161 | animationType?:endAnimationType; 162 | } 163 | 164 | export type routesMapKeysRule= 165 | 'finallyPathList'| 166 | 'finallyPathMap'| 167 | 'aliasPathMap'| 168 | 'pathMap'| 169 | 'nameMap'| 170 | 'vueRouteMap'; 171 | 172 | export interface routesMapRule{ 173 | [key:string]:any; 174 | finallyPathList: Array; 175 | finallyPathMap:RoutesRule; 176 | aliasPathMap: RoutesRule; 177 | pathMap: RoutesRule; 178 | nameMap:RoutesRule, 179 | vueRouteMap:objectAny 180 | } 181 | 182 | export interface routeRule{ 183 | name:string|undefined; 184 | meta:objectAny; 185 | path:string; 186 | query:objectAny; 187 | params:objectAny; 188 | fullPath:string; 189 | NAVTYPE:NAVTYPE|''; 190 | BACKTYPE?:backTypeRule|''; // v2.0.5 + 191 | [propName: string]: any; 192 | } 193 | 194 | 195 | export interface RoutesRule { 196 | path: string; // pages.json中的path 必须加上 '/' 开头 197 | component?: object; // H5端可用 198 | name?: string; // 命名路由 199 | components?: object; // 命名视图组件,H5端可用 200 | redirect?: string | Function; // H5端可用 201 | props?: boolean | object | Function; // H5端可用 202 | aliasPath?: string; // h5端 设置一个别名路径来替换 uni-app的默认路径 203 | alias?: string | Array; // H5端可用 204 | children?: Array; // 嵌套路由,H5端可用 205 | beforeEnter?:guardHookRule; // 路由元守卫 206 | meta?: any; // 其他格外参数 207 | [propName: string]: any; 208 | } 209 | 210 | export interface Router { 211 | readonly lifeCycle: LifeCycleConfig; 212 | readonly options: InstantiateConfig; 213 | $lockStatus:boolean; 214 | $route: object | null; 215 | enterPath:string; 216 | runId:number; 217 | Vue:any; 218 | appMain:{ 219 | NAVTYPE:reNavMethodRule|reNotNavMethodRule, 220 | path:string 221 | }|{}; 222 | proxyHookDeps: proxyDepsRule; 223 | routesMap: routesMapRule|{}; 224 | mount: Array<{app: any; el: string}>; 225 | install(Vue: any): void; 226 | push(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 保留浏览历史 227 | replace(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 关闭当前页面,跳转到的某个页面。 228 | replaceAll(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 URL 关闭所有页面,打开到应用内的某个页面 229 | pushTab(to: totalNextRoute|navRoute | string,from?:totalNextRoute): void; // 动态的导航到一个新 url 关闭所有页面,打开到应用内的某个tab 230 | back(level:number|undefined,origin?:uniBackRule|uniBackApiRule):void; 231 | forceGuardEach(navType:NAVTYPE|undefined,forceNav:boolean):void; //强制触发当前守卫 232 | beforeEach(userGuard:guardHookRule): void; // 添加全局前置路由守卫 233 | afterEach(userGuard:(to: totalNextRoute, from: totalNextRoute)=>void): void; // 添加全局后置路由守卫 234 | } 235 | 236 | 237 | export type PromiseResolve=(value?: void | PromiseLike | undefined) => void; 238 | 239 | // @ts-ignore 240 | declare module 'vue/types/vue' { 241 | interface Vue { 242 | $Router: Router; 243 | $Route: routeRule; 244 | } 245 | } -------------------------------------------------------------------------------- /src/options/config.ts: -------------------------------------------------------------------------------- 1 | import {startAnimationRule, hookListRule, RoutesRule, navtoRule, navErrorRule, Router, objectAny, NAVTYPE, totalNextRoute, navRoute} from './base'; 2 | 3 | export type debuggerConfig=boolean|debuggerArrayConfig; 4 | export type platformRule='h5'|'app-plus'|'app-lets'|'mp-weixin'|'mp-baidu'|'mp-alipay'|'mp-toutiao'|'mp-qq'|'mp-360'; 5 | 6 | export interface H5Config { 7 | paramsToQuery?: boolean; // h5端上通过params传参时规则是vue-router 刷新会丢失 开启此开关将变成?连接的方式 8 | vueRouterDev?: boolean; // 完全使用采用vue-router的开发模式 9 | vueNext?: boolean; // 在next管道函数中是否获取vueRouter next的原本参数 10 | mode?: string; 11 | base?: string; 12 | linkActiveClass?: string; 13 | linkExactActiveClass?: string; 14 | scrollBehavior?: Function; 15 | fallback?: boolean; 16 | } 17 | export interface AppConfig { 18 | registerLoadingPage?:boolean; // 是否注册过渡加载页 +v2.0.6 19 | loadingPageStyle?: () => object; // 当前等待页面的样式 必须返回一个json 20 | loadingPageHook?: (view:any)=>void; // 刚刚打开页面处于等待状态,会触发此函数 21 | launchedHook?:()=>void; // 首次启动app完成 22 | animation?: startAnimationRule; // 页面切换动画 23 | } 24 | export interface appletConfig { 25 | animationDuration?:number; // 页面切换时间,有助于路由锁精准解锁 26 | } 27 | 28 | type hookRule=(args:Array, next:(args:Array)=>void, router:Router)=>void; 29 | export interface proxyHooksConfig{ 30 | onLaunch?:hookRule; 31 | onShow?:hookRule; 32 | onHide?:hookRule; 33 | onError?:hookRule; 34 | onInit?:hookRule; 35 | onLoad?:hookRule; 36 | onReady?:hookRule; 37 | onUnload?:hookRule; 38 | onResize?:hookRule; 39 | destroyed?:hookRule; 40 | created?:hookRule; 41 | beforeCreate?:hookRule; 42 | beforeMount?:hookRule; 43 | mounted?:hookRule; 44 | beforeDestroy?:hookRule; 45 | } 46 | 47 | export interface debuggerArrayConfig{ 48 | error?:boolean; 49 | warn?:boolean; 50 | log?:boolean; 51 | } 52 | 53 | export interface InstantiateConfig { 54 | [key:string]:any; 55 | keepUniOriginNav?:boolean; // 重写uni-app的跳转方法;关闭后使用uni-app的原始方法跳转和插件api跳转等同 56 | platform:platformRule; // 当前运行平台 57 | h5?: H5Config; 58 | APP?: AppConfig; 59 | applet?:appletConfig; 60 | beforeProxyHooks?:proxyHooksConfig; 61 | debugger?: debuggerConfig; // 是否处于开发阶段 设置为true则打印日志 62 | routerBeforeEach?: (to:navtoRule, from:navtoRule, next:(rule?: navtoRule|false)=>void) => void; // router 前置路由函数 每次触发跳转前先会触发此函数 63 | routerAfterEach?: (to:navtoRule, from:navtoRule, next?: Function) => void; // router 后置路由函数 每次触发跳转后会触发此函数 64 | routerErrorEach?: (error: navErrorRule, router:Router) => void; 65 | resolveQuery?:(jsonQuery:objectAny)=>objectAny; // 跳转之前把参数传递给此函数、返回最终的数据!有此函数不走默认方法 66 | parseQuery?:(jsonQuery:objectAny)=>objectAny; // 读取值之前把参数传递给此函数,返回最终的数据!有此函数不走默认方法 67 | detectBeforeLock?:(router:Router, to:string|number|totalNextRoute|navRoute, navType:NAVTYPE)=>void; // 在检测路由锁之前触发的函数 68 | routes: RoutesRule[]; 69 | } 70 | export interface LifeCycleConfig{ 71 | beforeHooks: hookListRule; 72 | afterHooks: hookListRule; 73 | routerBeforeHooks: hookListRule; 74 | routerAfterHooks: hookListRule; 75 | routerErrorHooks: Array<(error:navErrorRule, router:Router)=>void>; 76 | } 77 | 78 | -------------------------------------------------------------------------------- /src/public/beforeProxyHook.ts: -------------------------------------------------------------------------------- 1 | import { voidFun } from '../helpers/utils'; 2 | import { warn } from '../helpers/warn'; 3 | import { Router, vueHookNameRule } from '../options/base'; 4 | 5 | export function beforeProxyHook( 6 | Vim:any, 7 | router:Router 8 | ):boolean { 9 | const hookOptions = Vim.$options; 10 | const {beforeProxyHooks} = router.options; 11 | if (hookOptions == null) { 12 | return false; 13 | } 14 | if (beforeProxyHooks == null) { 15 | return false; 16 | } 17 | const keyArray = Object.keys(beforeProxyHooks) as Array; 18 | for (let i = 0; i < keyArray.length; i++) { 19 | const key = keyArray[i]; 20 | const hooksArray:Array|null = hookOptions[key]; 21 | if (hooksArray) { 22 | const beforeProxyFun = beforeProxyHooks[key]; 23 | for (let j = 0; j < hooksArray.length; j++) { 24 | const hookFun = hooksArray[j]; 25 | if (hookFun.toString().includes($npm_package_name)) { 26 | continue 27 | } 28 | const [oldHook] = hooksArray.splice(j, 1, function myReplace(this: any, ...args:Array) { 29 | const pluginMarkId = $npm_package_name; 30 | voidFun(pluginMarkId); 31 | 32 | if (beforeProxyFun) { 33 | beforeProxyFun.call(this, args, (options) => { 34 | oldHook.apply(this, options) 35 | }, router); 36 | } else { 37 | oldHook.apply(this, args) 38 | } 39 | }); 40 | } 41 | } else { 42 | warn(`beforeProxyHooks ===> 当前组件不适合${key},或者 hook: ${key} 不存在,已为你规避处理,可以忽略。`, router) 43 | } 44 | } 45 | return true 46 | } 47 | -------------------------------------------------------------------------------- /src/public/hooks.ts: -------------------------------------------------------------------------------- 1 | import { 2 | Router, 3 | hookListRule, 4 | navtoRule, 5 | reloadNavRule, 6 | totalNextRoute, 7 | hookToggle, 8 | NAVTYPE, 9 | navErrorRule, 10 | objectAny 11 | } from '../options/base'; 12 | import { 13 | routesForMapRoute, 14 | getDataType, 15 | forMatNextToFrom, 16 | getUniCachePage, 17 | voidFun 18 | } from '../helpers/utils' 19 | import { navjump } from './methods'; 20 | import { proxyH5Mount } from '../H5/proxyHook'; 21 | import { addKeepAliveInclude } from '../H5/patch'; 22 | import { tabIndexSelect } from '../app/appPatch'; 23 | 24 | export const ERRORHOOK:Array<(error:navErrorRule, router:Router)=>void> = [ 25 | (error, router) => router.lifeCycle.routerErrorHooks[0](error, router) 26 | ] 27 | export const HOOKLIST: hookListRule = [ 28 | (router, to, from, toRoute, next) => callHook(router.lifeCycle.routerBeforeHooks[0], to, from, router, next), 29 | (router, to, from, toRoute, next) => callBeforeRouteLeave(router, to, from, next), 30 | (router, to, from, toRoute, next) => callHook(router.lifeCycle.beforeHooks[0], to, from, router, next), 31 | (router, to, from, toRoute, next) => callHook(toRoute.beforeEnter, to, from, router, next), 32 | (router, to, from, toRoute, next) => callHook(router.lifeCycle.afterHooks[0], to, from, router, next, false), 33 | (router, to, from, toRoute, next) => { 34 | router.$lockStatus = false; 35 | if (router.options.platform === 'h5') { 36 | proxyH5Mount(router); 37 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/316 2021年12月10日14:30:13 38 | addKeepAliveInclude(router); 39 | } 40 | router.runId++; 41 | return callHook(router.lifeCycle.routerAfterHooks[0], to, from, router, next, false) 42 | } 43 | ]; 44 | 45 | export function callBeforeRouteLeave( 46 | router:Router, 47 | to:totalNextRoute, 48 | from:totalNextRoute, 49 | resolve:Function 50 | ):void { 51 | const page = getUniCachePage(0); 52 | let beforeRouteLeave; 53 | if (Object.keys(page).length > 0) { 54 | let leaveHooks:Array|undefined|Function; 55 | if (router.options.platform === 'h5') { 56 | leaveHooks = (page as objectAny).$options.beforeRouteLeave; 57 | } else { 58 | if ((page as objectAny).$vm != null) { 59 | leaveHooks = (page as objectAny).$vm.$options.beforeRouteLeave; 60 | } 61 | } 62 | switch (getDataType>((leaveHooks as Array))) { 63 | case '[object Array]': // h5端表现 64 | beforeRouteLeave = (leaveHooks as Array)[0]; 65 | beforeRouteLeave = beforeRouteLeave.bind(page) 66 | break; 67 | case '[object Function]': // 目前app端表现 68 | beforeRouteLeave = (leaveHooks as Function).bind((page as objectAny).$vm); 69 | break 70 | } 71 | } 72 | return callHook(beforeRouteLeave, to, from, router, resolve); 73 | } 74 | 75 | export function callHook( 76 | hook:Function|undefined, 77 | to:totalNextRoute, 78 | from: totalNextRoute, 79 | router:Router, 80 | resolve:Function, 81 | hookAwait:boolean|undefined = true 82 | ):void { 83 | if (hook != null && hook instanceof Function) { 84 | if (hookAwait === true) { 85 | hook(to, from, resolve, router, false); 86 | } else { 87 | hook(to, from, () => {}, router, false); 88 | resolve(); 89 | } 90 | } else { 91 | resolve(); 92 | } 93 | } 94 | 95 | export function onTriggerEachHook( 96 | to:totalNextRoute, 97 | from: totalNextRoute, 98 | router:Router, 99 | hookType:hookToggle, 100 | next:(rule?: navtoRule|false)=>void, 101 | ):void { 102 | let callHookList:hookListRule = []; 103 | switch (hookType) { 104 | case 'beforeEach': 105 | callHookList = HOOKLIST.slice(0, 3); 106 | break; 107 | case 'afterEach': 108 | callHookList = HOOKLIST.slice(4); 109 | break 110 | case 'beforeEnter': 111 | callHookList = HOOKLIST.slice(3, 4); 112 | break; 113 | } 114 | transitionTo(router, to, from, 'push', callHookList, next); 115 | } 116 | 117 | export function transitionTo( 118 | router:Router, 119 | to:totalNextRoute, 120 | from: totalNextRoute, 121 | navType:NAVTYPE, 122 | callHookList:hookListRule, 123 | hookCB:Function 124 | ) :void{ 125 | const {matTo, matFrom} = forMatNextToFrom(router, to, from); 126 | if (router.options.platform === 'h5') { 127 | loopCallHook(callHookList, 0, hookCB, router, matTo, matFrom, navType); 128 | } else { 129 | loopCallHook(callHookList.slice(0, 4), 0, () => { 130 | hookCB(() => { // 非H5端等他跳转完才触发最后两个生命周期 131 | loopCallHook(callHookList.slice(4), 0, voidFun, router, matTo, matFrom, navType); 132 | }); 133 | }, router, matTo, matFrom, navType); 134 | } 135 | } 136 | 137 | export function loopCallHook( 138 | hooks:hookListRule, 139 | index:number, 140 | next:Function, 141 | router:Router, 142 | matTo:totalNextRoute, 143 | matFrom: totalNextRoute, 144 | navType:NAVTYPE, 145 | ): void|Function { 146 | const toRoute = routesForMapRoute(router, matTo.path, ['finallyPathMap', 'pathMap']); 147 | if (hooks.length - 1 < index) { 148 | return next(); 149 | } 150 | const hook = hooks[index]; 151 | const errHook = ERRORHOOK[0]; 152 | hook(router, matTo, matFrom, toRoute, (nextTo:reloadNavRule) => { 153 | if (router.options.platform === 'app-plus') { 154 | if (nextTo === false || (typeof nextTo === 'string' || typeof nextTo === 'object')) { 155 | tabIndexSelect(matTo, matFrom); 156 | } 157 | } 158 | if (nextTo === false) { 159 | if (router.options.platform === 'h5') { 160 | next(false); 161 | } 162 | errHook({ type: 0, msg: '管道函数传递 false 导航被终止!', matTo, matFrom, nextTo }, router) 163 | } else if (typeof nextTo === 'string' || typeof nextTo === 'object') { 164 | let newNavType = navType; 165 | let newNextTo = nextTo; 166 | if (typeof nextTo === 'object') { 167 | const {NAVTYPE: type, ...moreTo} = nextTo; 168 | newNextTo = moreTo; 169 | if (type != null) { 170 | newNavType = type; 171 | } 172 | } 173 | navjump(newNextTo, router, newNavType, {from: matFrom, next}) 174 | } else if (nextTo == null) { 175 | index++; 176 | loopCallHook(hooks, index, next, router, matTo, matFrom, navType) 177 | } else { 178 | errHook({ type: 1, msg: '管道函数传递未知类型,无法被识别。导航被终止!', matTo, matFrom, nextTo }, router) 179 | } 180 | }); 181 | } 182 | -------------------------------------------------------------------------------- /src/public/methods.ts: -------------------------------------------------------------------------------- 1 | import { 2 | NAVTYPE, 3 | Router, 4 | totalNextRoute, 5 | objectAny, 6 | routeRule, 7 | reNavMethodRule, 8 | rewriteMethodToggle, 9 | navtypeToggle, 10 | navErrorRule, 11 | uniBackApiRule, 12 | uniBackRule, 13 | navRoute 14 | } from '../options/base' 15 | import { 16 | queryPageToMap, 17 | resolveQuery, 18 | parseQuery 19 | } from './query' 20 | import { 21 | voidFun, 22 | paramsToQuery, 23 | getUniCachePage, 24 | routesForMapRoute, 25 | copyData, 26 | lockDetectWarn, 27 | getDataType, 28 | notRouteTo404, 29 | deepDecodeQuery 30 | } from '../helpers/utils' 31 | import { transitionTo } from './hooks'; 32 | import {createFullPath, createToFrom} from '../public/page' 33 | import {HOOKLIST} from './hooks' 34 | 35 | export function lockNavjump( 36 | to:string|totalNextRoute|navRoute, 37 | router:Router, 38 | navType:NAVTYPE, 39 | forceNav?:boolean, 40 | animation?:uniBackApiRule|uniBackRule 41 | ):void{ 42 | lockDetectWarn(router, to, navType, () => { 43 | if (router.options.platform !== 'h5') { 44 | router.$lockStatus = true; 45 | } 46 | navjump(to as totalNextRoute, router, navType, undefined, forceNav, animation); 47 | }, animation); 48 | } 49 | export function navjump( 50 | to:string|totalNextRoute, 51 | router:Router, 52 | navType:NAVTYPE, 53 | nextCall?:{ 54 | from:totalNextRoute; 55 | next:Function; 56 | }, 57 | forceNav?:boolean, 58 | animation?:uniBackApiRule|uniBackRule, 59 | callHook:boolean|undefined = true 60 | ) :void|never|totalNextRoute { 61 | if (navType === 'back') { 62 | let level:number = 1; 63 | if (typeof to === 'string') { 64 | level = +to; 65 | } else { 66 | level = to.delta || 1; 67 | // 主要剥离事件函数 68 | animation = { 69 | ...animation || {}, 70 | ...(to as uniBackApiRule) 71 | } 72 | } 73 | if (router.options.platform === 'h5') { 74 | (router.$route as any).go(-level); 75 | 76 | // Fixe https://github.com/SilurianYang/uni-simple-router/issues/266 2021年6月3日11:14:38 77 | // @ts-ignore 78 | const success = (animation || {success: voidFun}).success || voidFun; 79 | // @ts-ignore 80 | const complete = (animation || {complete: voidFun}).complete || voidFun; 81 | success({errMsg: 'navigateBack:ok'}); 82 | complete({errMsg: 'navigateBack:ok'}); 83 | return; 84 | } else { 85 | to = backOptionsBuild(router, level, animation); 86 | } 87 | } 88 | const {rule} = queryPageToMap(to, router); 89 | rule.type = navtypeToggle[navType]; 90 | const toRule = paramsToQuery(router, rule); 91 | let parseToRule = resolveQuery(toRule as totalNextRoute, router); 92 | if (router.options.platform === 'h5') { 93 | if (navType !== 'push') { 94 | navType = 'replace'; 95 | } 96 | if (nextCall != null) { // next 管道函数拦截时 直接next即可 97 | nextCall.next({ 98 | replace: navType !== 'push', 99 | ...parseToRule 100 | }) 101 | } else { 102 | // Fixe https://github.com/SilurianYang/uni-simple-router/issues/240 2021年3月7日14:45:36 103 | if (navType === 'push' && Reflect.has(parseToRule, 'events')) { 104 | if (Reflect.has(parseToRule, 'name')) { 105 | throw new Error(`在h5端上使用 'push'、'navigateTo' 跳转时,如果包含 events 不允许使用 name 跳转,因为 name 实现了动态路由。请更换为 path 或者 url 跳转!`); 106 | } else { 107 | uni['navigateTo'](parseToRule, true, voidFun, forceNav); 108 | } 109 | } else { 110 | (router.$route as any)[navType](parseToRule, (parseToRule as totalNextRoute).success || voidFun, (parseToRule as totalNextRoute).fail || voidFun) 111 | } 112 | } 113 | } else { 114 | let from:totalNextRoute = {path: ''}; 115 | if (nextCall == null) { 116 | let toRoute = routesForMapRoute(router, parseToRule.path, ['finallyPathMap', 'pathMap']); 117 | toRoute = notRouteTo404(router, toRoute, parseToRule, navType); 118 | parseToRule = { ...toRoute, ...{params: {}}, ...parseToRule, ...{path: toRoute.path} } 119 | from = createToFrom(parseToRule, router); 120 | } else { 121 | from = nextCall.from; 122 | } 123 | createFullPath(parseToRule, from); 124 | if (callHook === false) { 125 | return parseToRule; 126 | } 127 | transitionTo(router, parseToRule, from, navType, HOOKLIST, function( 128 | callOkCb:Function 129 | ):void { 130 | uni[navtypeToggle[navType]](parseToRule, true, callOkCb, forceNav); 131 | }) 132 | } 133 | } 134 | 135 | export function backOptionsBuild( 136 | router:Router, 137 | level:number, 138 | animation:uniBackApiRule|uniBackRule|undefined = {}, 139 | ):totalNextRoute { 140 | const toRule = createRoute(router, level, undefined, {NAVTYPE: 'back', ...animation}); 141 | const navjumpRule:totalNextRoute = { 142 | ...animation, 143 | path: toRule.path, 144 | query: toRule.query, 145 | delta: level 146 | } 147 | if (getDataType(animation) === '[object Object]') { 148 | const {animationDuration, animationType} = (animation as uniBackApiRule) 149 | if (animationDuration != null) { 150 | navjumpRule.animationDuration = animationDuration; 151 | } 152 | if (animationType != null) { 153 | navjumpRule.animationType = animationType; 154 | } 155 | 156 | const {from} = (animation as uniBackRule) 157 | if (from != null) { 158 | navjumpRule.BACKTYPE = from; 159 | } 160 | } 161 | return navjumpRule; 162 | } 163 | 164 | export function forceGuardEach( 165 | router:Router, 166 | navType:NAVTYPE|undefined = 'replaceAll', 167 | forceNav:undefined|boolean = false 168 | ):void|never { 169 | if (router.options.platform === 'h5') { 170 | throw new Error(`在h5端上使用:forceGuardEach 是无意义的,目前 forceGuardEach 仅支持在非h5端上使用`); 171 | } 172 | const currentPage = getUniCachePage(0); 173 | if (Object.keys(currentPage).length === 0) { 174 | (router.options.routerErrorEach as (error: navErrorRule, router:Router) => void)({ 175 | type: 3, 176 | NAVTYPE: navType, 177 | uniActualData: {}, 178 | level: 0, 179 | msg: `不存在的页面栈,请确保有足够的页面可用,当前 level:0` 180 | }, router); 181 | } 182 | const {route, options} = currentPage as objectAny; 183 | lockNavjump({ 184 | path: `/${route}`, 185 | query: deepDecodeQuery(options || {}) 186 | }, router, navType, forceNav); 187 | } 188 | 189 | export function createRoute( 190 | router:Router, 191 | level:number|undefined = 0, 192 | orignRule?:totalNextRoute, 193 | uniActualData:objectAny|undefined = {}, 194 | ):routeRule|never { 195 | const route:routeRule = { 196 | name: '', 197 | meta: {}, 198 | path: '', 199 | fullPath: '', 200 | NAVTYPE: '', 201 | query: {}, 202 | params: {}, 203 | BACKTYPE: (orignRule || {BACKTYPE: ''}).BACKTYPE || '' // v2.0.5 + 204 | }; 205 | 206 | if (level === 19970806) { // 首次构建响应式 页面不存在 直接返回 207 | return route 208 | } 209 | if (router.options.platform === 'h5') { 210 | let vueRoute:totalNextRoute = {path: ''}; 211 | if (orignRule != null) { 212 | vueRoute = orignRule; 213 | } else { 214 | vueRoute = (router.$route as objectAny).currentRoute; 215 | } 216 | const matRouteParams = copyData(vueRoute.params as objectAny); 217 | delete matRouteParams.__id__; 218 | const toQuery = parseQuery({...matRouteParams, ...copyData(vueRoute.query as objectAny)}, router); 219 | vueRoute = {...vueRoute, query: toQuery} 220 | route.path = vueRoute.path; 221 | route.fullPath = vueRoute.fullPath || ''; 222 | route.query = deepDecodeQuery(vueRoute.query || {}); 223 | route.NAVTYPE = rewriteMethodToggle[vueRoute.type as reNavMethodRule || 'reLaunch']; 224 | } else { 225 | let appPage:objectAny = {}; 226 | if (orignRule != null) { 227 | appPage = {...orignRule, openType: orignRule.type}; 228 | } else { 229 | const page = getUniCachePage(level); 230 | if (Object.keys(page).length === 0) { 231 | const {NAVTYPE: _NAVTYPE, ..._args} = uniActualData; 232 | const errorMsg:string = `不存在的页面栈,请确保有足够的页面可用,当前 level:${level}`; 233 | (router.options.routerErrorEach as (error: navErrorRule, router:Router) => void)({ 234 | type: 3, 235 | msg: errorMsg, 236 | NAVTYPE: _NAVTYPE, 237 | level, 238 | uniActualData: _args 239 | }, router); 240 | throw new Error(errorMsg); 241 | } 242 | // Fixes: https://github.com/SilurianYang/uni-simple-router/issues/196 243 | const pageOptions:objectAny = (page as objectAny).options || {}; 244 | appPage = { 245 | ...(page as objectAny).$page || {}, 246 | query: deepDecodeQuery(pageOptions), 247 | fullPath: decodeURIComponent(((page as objectAny).$page || {}).fullPath || '/' + (page as objectAny).route) 248 | } 249 | if (router.options.platform !== 'app-plus') { 250 | appPage.path = `/${(page as objectAny).route}` 251 | } 252 | } 253 | const openType:reNavMethodRule|'navigateBack' = appPage.openType; 254 | route.query = appPage.query; 255 | route.path = appPage.path; 256 | route.fullPath = appPage.fullPath; 257 | route.NAVTYPE = rewriteMethodToggle[openType || 'reLaunch']; 258 | } 259 | const tableRoute = routesForMapRoute(router, route.path, ['finallyPathMap', 'pathMap']) 260 | const perfectRoute = { ...route, ...tableRoute}; 261 | perfectRoute.query = parseQuery(perfectRoute.query, router); 262 | return perfectRoute; 263 | } 264 | -------------------------------------------------------------------------------- /src/public/page.ts: -------------------------------------------------------------------------------- 1 | import { proxyHookName } from '../helpers/config'; 2 | import { getDataType, getUniCachePage, deepClone} from '../helpers/utils'; 3 | import { objectAny, pageTypeRule, Router, totalNextRoute, vueOptionRule } from '../options/base'; 4 | import {createRoute} from './methods' 5 | import { stringifyQuery } from './query'; 6 | 7 | export function createToFrom( 8 | to:totalNextRoute, 9 | router:Router, 10 | ):totalNextRoute { 11 | let fromRoute:totalNextRoute = {path: ''}; 12 | const page = getUniCachePage|objectAny>(0); 13 | if (getDataType|objectAny>(page) === '[object Array]') { 14 | fromRoute = deepClone(to) 15 | } else { 16 | fromRoute = createRoute(router) as totalNextRoute; 17 | } 18 | return fromRoute; 19 | } 20 | 21 | export function createFullPath( 22 | to:totalNextRoute, 23 | from:totalNextRoute 24 | ):void{ 25 | if (to.fullPath == null) { 26 | const strQuery = stringifyQuery(to.query as objectAny); 27 | to.fullPath = to.path + strQuery; 28 | } 29 | if (from.fullPath == null) { 30 | const strQuery = stringifyQuery(from.query as objectAny); 31 | from.fullPath = from.path + strQuery; 32 | } 33 | } 34 | 35 | export function proxyPageHook( 36 | vueVim:any, 37 | router:Router, 38 | pageType:pageTypeRule 39 | ):void { 40 | const hookDeps = router.proxyHookDeps; 41 | const pageHook:vueOptionRule = vueVim.$options; 42 | for (let i = 0; i < proxyHookName.length; i++) { 43 | const hookName = proxyHookName[i]; 44 | const hookList = pageHook[hookName]; 45 | if (hookList) { 46 | for (let k = 0; k < hookList.length; k++) { 47 | const originHook = hookList[k]; 48 | if (originHook.toString().includes($npm_package_name)) { 49 | continue 50 | } 51 | const resetIndex = Object.keys(hookDeps.hooks).length + 1 52 | const proxyHook = (...args:Array):void => { 53 | hookDeps.resetIndex.push(resetIndex); 54 | hookDeps.options[resetIndex] = args; 55 | } 56 | const [resetHook] = hookList.splice(k, 1, proxyHook); 57 | hookDeps.hooks[resetIndex] = { 58 | proxyHook, 59 | callHook: (enterPath:string) :void => { 60 | if (router.enterPath.replace(/^\//, '') !== enterPath.replace(/^\//, '') && pageType !== 'app') { 61 | return; 62 | } 63 | const options = hookDeps.options[resetIndex]; 64 | resetHook.apply(vueVim, options); 65 | }, 66 | resetHook: () :void => { 67 | hookList.splice(k, 1, resetHook) 68 | } 69 | }; 70 | } 71 | } 72 | } 73 | } 74 | export function resetAndCallPageHook( 75 | router:Router, 76 | enterPath:string, 77 | reset:boolean|undefined = true 78 | ):void{ 79 | // Fixe: https://github.com/SilurianYang/uni-simple-router/issues/206 80 | const pathInfo = enterPath.trim().match(/^(\/?[^\?\s]+)(\?[\s\S]*$)?$/); 81 | if (pathInfo == null) { 82 | throw new Error(`还原hook失败。请检查 【${enterPath}】 路径是否正确。`); 83 | } 84 | enterPath = pathInfo[1]; 85 | const proxyHookDeps = router.proxyHookDeps; 86 | const resetHooksArray = proxyHookDeps.resetIndex 87 | for (let i = 0; i < resetHooksArray.length; i++) { 88 | const index = resetHooksArray[i]; 89 | const {callHook} = proxyHookDeps.hooks[index]; 90 | callHook(enterPath); 91 | } 92 | if (reset) { 93 | resetPageHook(router); 94 | } 95 | } 96 | export function resetPageHook( 97 | router:Router 98 | ) { 99 | const proxyHookDeps = router.proxyHookDeps; 100 | for (const [, {resetHook}] of Object.entries(proxyHookDeps.hooks)) { 101 | resetHook(); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /src/public/query.ts: -------------------------------------------------------------------------------- 1 | import { 2 | objectAny, 3 | Router, 4 | routesMapRule, 5 | RoutesRule, 6 | totalNextRoute 7 | } from '../options/base'; 8 | import { 9 | getDataType, 10 | urlToJson, 11 | routesForMapRoute, 12 | getRoutePath, 13 | assertDeepObject, 14 | copyData, 15 | getWildcardRule, 16 | deepDecodeQuery 17 | } from '../helpers/utils' 18 | import {ERRORHOOK} from './hooks' 19 | import {warn} from '../helpers/warn' 20 | 21 | const encodeReserveRE = /[!'()*]/g 22 | const encodeReserveReplacer = (c:string) => '%' + c.charCodeAt(0).toString(16) 23 | const commaRE = /%2C/g 24 | 25 | const encode = (str:string) => 26 | encodeURIComponent(str) 27 | .replace(encodeReserveRE, encodeReserveReplacer) 28 | .replace(commaRE, ',') 29 | 30 | export function queryPageToMap( 31 | toRule:string|totalNextRoute, 32 | router:Router 33 | ) :{ 34 | rule:totalNextRoute; 35 | route:RoutesRule, 36 | query:objectAny 37 | } { 38 | let query:objectAny = {}; 39 | let route:RoutesRule|string = ''; 40 | let successCb = (toRule as totalNextRoute).success; 41 | let failCb = (toRule as totalNextRoute).fail; 42 | if (getDataType(toRule) === '[object Object]') { 43 | const objNavRule = (toRule as totalNextRoute); 44 | if (objNavRule.path != null) { 45 | const {path, query: newQuery} = urlToJson(objNavRule.path); 46 | route = routesForMapRoute(router, path, ['finallyPathList', 'pathMap']); 47 | query = {...newQuery, ...((toRule as totalNextRoute).query || {})}; 48 | objNavRule.path = path; 49 | objNavRule.query = query; 50 | delete (toRule as totalNextRoute).params; 51 | } else if (objNavRule.name != null) { 52 | route = (router.routesMap as routesMapRule).nameMap[objNavRule.name]; 53 | if (route == null) { 54 | route = getWildcardRule(router, { type: 2, msg: `命名路由为:${objNavRule.name} 的路由,无法在路由表中找到!`, toRule}); 55 | } else { 56 | query = (toRule as totalNextRoute).params || {}; 57 | delete (toRule as totalNextRoute).query; 58 | } 59 | } else { 60 | route = getWildcardRule(router, { type: 2, msg: `${toRule} 解析失败,请检测当前路由表下是否有包含。`, toRule}); 61 | } 62 | } else { 63 | toRule = urlToJson((toRule as string)) as totalNextRoute; 64 | route = routesForMapRoute(router, toRule.path, ['finallyPathList', 'pathMap']) 65 | query = toRule.query as objectAny; 66 | } 67 | if (router.options.platform === 'h5') { 68 | const {finallyPath} = getRoutePath(route as RoutesRule, router); 69 | if (finallyPath.includes(':') && (toRule as totalNextRoute).name == null) { 70 | ERRORHOOK[0]({ type: 2, msg: `当有设置 alias或者aliasPath 为动态路由时,不允许使用 path 跳转。请使用 name 跳转!`, route}, router) 71 | } 72 | const completeCb = (toRule as totalNextRoute).complete; 73 | const cacheSuccess = (toRule as totalNextRoute).success; 74 | const cacheFail = (toRule as totalNextRoute).fail; 75 | if (getDataType(completeCb) === '[object Function]') { 76 | const publicCb = function(this:any, args:Array, callHook:Function|undefined):void { 77 | if (getDataType(callHook) === '[object Function]') { 78 | (callHook as Function).apply(this, args); 79 | } 80 | (completeCb as Function).apply(this, args); 81 | } 82 | successCb = function(this:any, ...args:any):void{ 83 | publicCb.call(this, args, cacheSuccess); 84 | }; 85 | failCb = function(this:any, ...args:any):void{ 86 | publicCb.call(this, args, cacheFail); 87 | }; 88 | } 89 | } 90 | const rule = (toRule as totalNextRoute); 91 | if (getDataType(rule.success) === '[object Function]') { 92 | rule.success = successCb; 93 | } 94 | if (getDataType(rule.fail) === '[object Function]') { 95 | rule.fail = failCb; 96 | } 97 | return { 98 | rule, 99 | route: (route as RoutesRule), 100 | query 101 | } 102 | } 103 | 104 | export function resolveQuery( 105 | toRule:totalNextRoute, 106 | router:Router 107 | ):totalNextRoute { 108 | let queryKey:'params'|'query' = 'query'; 109 | if (toRule.params as objectAny != null) { 110 | queryKey = 'params'; 111 | } 112 | if (toRule.query as objectAny != null) { 113 | queryKey = 'query'; 114 | } 115 | const query = copyData(toRule[queryKey] || {}); 116 | const {resolveQuery: userResolveQuery} = router.options; 117 | if (userResolveQuery) { 118 | const jsonQuery = userResolveQuery(query); 119 | if (getDataType(jsonQuery) !== '[object Object]') { 120 | warn('请按格式返回参数: resolveQuery?:(jsonQuery:{[propName: string]: any;})=>{[propName: string]: any;}', router) 121 | } else { 122 | toRule[queryKey] = jsonQuery; 123 | } 124 | } else { 125 | const deepObj = assertDeepObject(query as objectAny); 126 | if (!deepObj) { 127 | return toRule; 128 | } 129 | const encode = JSON.stringify(query); 130 | toRule[queryKey] = { 131 | query: encode 132 | } 133 | } 134 | return toRule 135 | } 136 | 137 | export function parseQuery( 138 | query:objectAny, 139 | router:Router, 140 | ):objectAny { 141 | const {parseQuery: userParseQuery} = router.options; 142 | if (userParseQuery) { 143 | query = userParseQuery(copyData(query)); 144 | if (getDataType(query) !== '[object Object]') { 145 | warn('请按格式返回参数: parseQuery?:(jsonQuery:{[propName: string]: any;})=>{[propName: string]: any;}', router) 146 | } 147 | } else { 148 | if (Reflect.get(query, 'query')) { // 验证一下是不是深度对象 149 | let deepQuery = Reflect.get(query, 'query'); 150 | if (typeof deepQuery === 'string') { 151 | try { 152 | deepQuery = JSON.parse(deepQuery); 153 | } catch (error) { 154 | warn('尝试解析深度对象失败,按原样输出。' + error, router) 155 | } 156 | } 157 | if (typeof deepQuery === 'object') { 158 | return deepDecodeQuery(deepQuery); 159 | } 160 | } 161 | } 162 | return query 163 | } 164 | 165 | export function stringifyQuery(obj:objectAny): string { 166 | const res = obj 167 | ? Object.keys(obj) 168 | .map(key => { 169 | const val = obj[key] 170 | 171 | if (val === undefined) { 172 | return '' 173 | } 174 | 175 | if (val === null) { 176 | return encode(key) 177 | } 178 | 179 | if (Array.isArray(val)) { 180 | const result:Array = [] 181 | val.forEach(val2 => { 182 | if (val2 === undefined) { 183 | return 184 | } 185 | if (val2 === null) { 186 | result.push(encode(key)) 187 | } else { 188 | result.push(encode(key) + '=' + encode(val2)) 189 | } 190 | }) 191 | return result.join('&') 192 | } 193 | 194 | return encode(key) + '=' + encode(val) 195 | }) 196 | .filter(x => x.length > 0) 197 | .join('&') 198 | : null 199 | return res ? `?${res}` : '' 200 | } 201 | -------------------------------------------------------------------------------- /src/public/rewrite.ts: -------------------------------------------------------------------------------- 1 | import { 2 | uniNavApiRule, 3 | reNavMethodRule, 4 | reNotNavMethodRule, 5 | Router, 6 | rewriteMethodToggle, 7 | uniBackRule, 8 | uniBackApiRule, 9 | navtoRule, 10 | totalNextRoute, 11 | originMixins, 12 | objectAny 13 | } from '../options/base' 14 | 15 | import { 16 | routesForMapRoute, 17 | getRoutePath, 18 | getDataType, 19 | notDeepClearNull, 20 | resolveAbsolutePath, 21 | getUniCachePage, 22 | timeOut 23 | } from '../helpers/utils' 24 | 25 | import { 26 | warn 27 | } from '../helpers/warn' 28 | 29 | import {uniOriginJump} from './uniOrigin' 30 | import { HomeNvueSwitchTab } from '../app/appPatch'; 31 | 32 | const rewrite: Array = [ 33 | 'navigateTo', 34 | 'redirectTo', 35 | 'reLaunch', 36 | 'switchTab', 37 | 'navigateBack' 38 | ]; 39 | const cacheOldMethod:{ 40 | navigateTo:Function; 41 | redirectTo:Function; 42 | reLaunch:Function; 43 | switchTab:Function; 44 | navigateBack:Function; 45 | } = { 46 | navigateTo: () => {}, 47 | redirectTo: () => {}, 48 | reLaunch: () => {}, 49 | switchTab: () => {}, 50 | navigateBack: () => {} 51 | } 52 | 53 | export function rewriteMethod( 54 | router:Router 55 | ): void { 56 | if (router.options.keepUniOriginNav === false) { 57 | rewrite.forEach(name => { 58 | const oldMethod: Function = uni[name]; 59 | cacheOldMethod[name] = oldMethod; 60 | uni[name] = async function( 61 | params:originMixins|{from:string}|navtoRule, 62 | originCall:boolean = false, 63 | callOkCb?:Function, 64 | forceNav?:boolean 65 | ):Promise { 66 | 67 | if (originCall) { 68 | if (router.options.platform === 'app-plus') { 69 | await HomeNvueSwitchTab(router, (params as navtoRule), cacheOldMethod['reLaunch']); 70 | } 71 | uniOriginJump(router, oldMethod, name, params as originMixins, callOkCb, forceNav) 72 | 73 | } else { 74 | if (router.options.platform === 'app-plus') { 75 | if (Object.keys(router.appMain).length === 0) { 76 | router.appMain = { 77 | NAVTYPE: name, 78 | path: (params as uniNavApiRule).url 79 | } 80 | } 81 | } 82 | callRouterMethod(params as uniNavApiRule, name, router); 83 | } 84 | 85 | 86 | }; 87 | }) 88 | } 89 | } 90 | function callRouterMethod( 91 | option: uniNavApiRule|uniBackRule|uniBackApiRule, 92 | funName:reNavMethodRule|reNotNavMethodRule, 93 | router:Router 94 | ): void { 95 | if (router.options.platform === 'app-plus') { 96 | let openType = null; 97 | if (option) { 98 | openType = (option as uniNavApiRule).openType; 99 | } 100 | if (openType != null && openType === 'appLaunch') { 101 | funName = 'reLaunch' 102 | } 103 | } 104 | if (funName === 'reLaunch' && JSON.stringify(option) === '{"url":"/"}') { 105 | warn( 106 | `uni-app 原生方法:reLaunch({url:'/'}) 默认被重写啦!你可以使用 this.$Router.replaceAll() 或者 uni.reLaunch({url:'/?xxx=xxx'})`, 107 | router 108 | ); 109 | funName = 'navigateBack'; 110 | option = { 111 | from: 'backbutton' 112 | } 113 | } 114 | if (funName === 'navigateBack') { 115 | let level:number = 1; 116 | if (option == null) { 117 | option = {delta: 1}; 118 | } 119 | if (getDataType((option as uniBackApiRule).delta) === '[object Number]') { 120 | level = ((option as uniBackApiRule).delta as number); 121 | } 122 | router.back(level, (option as uniBackRule|uniBackApiRule)); 123 | } else { 124 | const routerMethodName = rewriteMethodToggle[(funName as reNavMethodRule)] 125 | let path = (option as uniNavApiRule).url; 126 | if (!path.startsWith('/')) { 127 | const absolutePath = resolveAbsolutePath(path, router); 128 | path = absolutePath; 129 | (option as uniNavApiRule).url = absolutePath; 130 | } 131 | if (funName === 'switchTab') { 132 | const route = routesForMapRoute(router, path, ['pathMap', 'finallyPathList']) 133 | const {finallyPath} = getRoutePath(route, router); 134 | if (getDataType(finallyPath) === '[object Array]') { 135 | warn( 136 | `uni-app 原生方法跳转路径为:${path}。此路为是tab页面时,不允许设置 alias 为数组的情况,并且不能为动态路由!当然你可以通过通配符*解决!`, 137 | router 138 | ); 139 | } 140 | if ((finallyPath as string) === '*') { 141 | warn( 142 | `uni-app 原生方法跳转路径为:${path}。在路由表中找不到相关路由表!当然你可以通过通配符*解决!`, 143 | router 144 | ); 145 | } 146 | // Fixe h5 端无法触发 onTabItemTap hook 2021年6月3日17:26:47 147 | if (router.options.platform === 'h5') { 148 | const {success: userSuccess} = option as uniNavApiRule; 149 | (option as uniNavApiRule).success = (...args:Array) => { 150 | userSuccess?.apply(null, args); 151 | timeOut(150).then(() => { 152 | const cbArgs = (option as uniNavApiRule).detail || {}; 153 | if (Object.keys(cbArgs).length > 0 && Reflect.has(cbArgs, 'index')) { 154 | const cachePage = getUniCachePage(0); 155 | if (Object.keys(cachePage).length === 0) { 156 | return false 157 | } 158 | const page = cachePage as objectAny; 159 | const hooks = page.$options.onTabItemTap; 160 | if (hooks) { 161 | for (let j = 0; j < hooks.length; j++) { 162 | hooks[j].call(page, cbArgs) 163 | } 164 | } 165 | } 166 | }); 167 | } 168 | } 169 | path = (finallyPath as string); 170 | } 171 | const {events, success, fail, complete, animationType, animationDuration} = option as uniNavApiRule; 172 | const jumpOptions:totalNextRoute = {path, events, success, fail, complete, animationDuration, animationType}; 173 | router[routerMethodName]( 174 | notDeepClearNull(jumpOptions) 175 | ) 176 | } 177 | } 178 | -------------------------------------------------------------------------------- /src/public/router.ts: -------------------------------------------------------------------------------- 1 | import {PromiseResolve, Router, uniBackApiRule, uniBackRule} from '../options/base'; 2 | import {InstantiateConfig, LifeCycleConfig} from '../options/config'; 3 | import { lifeCycle, proxyHookDeps} from '../helpers/config'; 4 | import {assertNewOptions, def, getDataType} from '../helpers/utils'; 5 | import {registerRouterHooks, registerEachHooks} from '../helpers/lifeCycle'; 6 | import {initMixins} from '../helpers/mixins' 7 | import {lockNavjump, forceGuardEach, createRoute} from '../public/methods' 8 | import {rewriteMethod} from '../public/rewrite' 9 | 10 | let AppReadyResolve:PromiseResolve = () => {}; 11 | const AppReady:Promise = new Promise(resolve => (AppReadyResolve = resolve)); 12 | 13 | function createRouter( 14 | params: InstantiateConfig 15 | ):Router { 16 | const options = assertNewOptions(params); 17 | const router:Router = { 18 | options, 19 | mount: [], 20 | runId: 0, 21 | Vue: null, 22 | proxyHookDeps: proxyHookDeps, 23 | appMain: {}, 24 | enterPath: '', 25 | $route: null, 26 | $lockStatus: false, 27 | routesMap: {}, 28 | lifeCycle: registerRouterHooks(lifeCycle, options), 29 | push(to) { 30 | lockNavjump(to, router, 'push'); 31 | }, 32 | replace(to) { 33 | lockNavjump(to, router, 'replace'); 34 | }, 35 | replaceAll(to) { 36 | lockNavjump(to, router, 'replaceAll'); 37 | }, 38 | pushTab(to) { 39 | lockNavjump(to, router, 'pushTab'); 40 | }, 41 | back(level = 1, animation) { 42 | if (getDataType(animation) !== '[object Object]') { 43 | const backRule:uniBackRule = { 44 | from: 'navigateBack' 45 | } 46 | animation = backRule; 47 | } else { 48 | if (!Reflect.has((animation as uniBackRule | uniBackApiRule), 'from')) { 49 | animation = { 50 | ...animation, 51 | from: 'navigateBack' 52 | }; 53 | } 54 | } 55 | lockNavjump(level + '', router, 'back', undefined, animation) 56 | }, 57 | forceGuardEach(navType, forceNav) { 58 | forceGuardEach(router, navType, forceNav) 59 | }, 60 | beforeEach(userGuard):void { 61 | registerEachHooks(router, 'beforeHooks', userGuard); 62 | }, 63 | afterEach(userGuard):void { 64 | registerEachHooks(router, 'afterHooks', userGuard); 65 | }, 66 | install(Vue:any):void{ 67 | router.Vue = Vue; 68 | rewriteMethod(this); 69 | initMixins(Vue, this); 70 | Object.defineProperty(Vue.prototype, '$Router', { 71 | get() { 72 | const actualData = router; 73 | 74 | Object.defineProperty(this, '$Router', { 75 | value: actualData, 76 | writable: false, 77 | configurable: false, 78 | enumerable: false 79 | }); 80 | 81 | return Object.seal(actualData); 82 | } 83 | }); 84 | Object.defineProperty(Vue.prototype, '$Route', { 85 | get() { 86 | return createRoute(router); 87 | } 88 | }); 89 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254 90 | Object.defineProperty(Vue.prototype, '$AppReady', { 91 | get() { 92 | if (router.options.platform === 'h5') { 93 | return Promise.resolve(); 94 | } 95 | return AppReady; 96 | }, 97 | set(value:boolean) { 98 | if (value === true) { 99 | AppReadyResolve(); 100 | } 101 | } 102 | }); 103 | } 104 | } 105 | def(router, 'currentRoute', () => createRoute(router)); 106 | 107 | router.beforeEach((to, from, next) => next()); 108 | router.afterEach(() => {}); 109 | return router; 110 | } 111 | 112 | function RouterMount( 113 | Vim:any, 114 | router:Router, 115 | el:string | undefined = '#app' 116 | ) :void|never { 117 | if (getDataType>(router.mount) === '[object Array]') { 118 | router.mount.push({ 119 | app: Vim, 120 | el 121 | }) 122 | } else { 123 | throw new Error(`挂载路由失败,router.app 应该为数组类型。当前类型:${typeof router.mount}`); 124 | } 125 | if (router.options.platform === 'h5') { 126 | const vueRouter = (router.$route as any); 127 | vueRouter.replace({ 128 | path: vueRouter.currentRoute.fullPath 129 | }); 130 | } // 其他端目前不需要做啥 131 | } 132 | 133 | export { 134 | RouterMount, 135 | createRouter 136 | } 137 | -------------------------------------------------------------------------------- /src/public/uniOrigin.ts: -------------------------------------------------------------------------------- 1 | import { originMixins, reNavMethodRule, reNotNavMethodRule, Router, startAnimationRule, uniNavApiRule } from '../options/base'; 2 | import { stringifyQuery } from './query'; 3 | import {notDeepClearNull, timeOut} from '../helpers/utils' 4 | import { mpPlatformReg } from '../helpers/config'; 5 | import { resetAndCallPageHook, resetPageHook } from './page'; 6 | 7 | let routerNavCount:number = 0; 8 | let lastNavType:reNavMethodRule|reNotNavMethodRule = 'reLaunch' 9 | 10 | export function uniOriginJump( 11 | router:Router, 12 | originMethod:Function, 13 | funName:reNavMethodRule|reNotNavMethodRule, 14 | options: originMixins, 15 | callOkCb?:Function, 16 | forceNav?:boolean 17 | ):void { 18 | const {complete, ...originRule} = formatOriginURLQuery(router, options, funName); 19 | const platform = router.options.platform; 20 | if (forceNav != null && forceNav === false) { 21 | if (routerNavCount === 0) { 22 | routerNavCount++ 23 | if (platform !== 'h5') { 24 | resetAndCallPageHook(router, originRule.url) // 还原及执行app.vue及首页下已经重写后的生命周期 25 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254 26 | // 在小程序端 next 直接放行会执行这个 27 | router.Vue.prototype.$AppReady = true; 28 | } 29 | } 30 | complete && complete.apply(null, {msg: 'forceGuardEach强制触发并且不执行跳转'}); 31 | callOkCb && callOkCb.apply(null, {msg: 'forceGuardEach强制触发并且不执行跳转'}) 32 | } else { 33 | if (routerNavCount === 0) { 34 | if (platform === 'app-plus') { 35 | resetAndCallPageHook(router, originRule.url) // 还原及执行app.vue下已经重写后的生命周期 36 | } else { 37 | if (new RegExp(mpPlatformReg, 'g').test(platform)) { 38 | // 其他就是在小程序下,首次启动发生跳转会走这里 39 | // 我们先将app.vue的生命周期执行 40 | resetAndCallPageHook(router, originRule.url, false) 41 | } 42 | } 43 | } 44 | originMethod({ 45 | ...originRule, 46 | from: options.BACKTYPE, 47 | complete: async function(...args:Array) { 48 | if (routerNavCount === 0) { 49 | routerNavCount++ 50 | 51 | if (platform !== 'h5') { 52 | if (new RegExp(mpPlatformReg, 'g').test(platform)) { // 跳转完成后小程序下还原生命周期 53 | resetPageHook(router); 54 | } 55 | // 【Fixe】 https://github.com/SilurianYang/uni-simple-router/issues/254 56 | // 在小程序端 第一次 next 做跳转 会触发这个 、在app端首次必定会触发这个 57 | router.Vue.prototype.$AppReady = true; 58 | 59 | if (platform === 'app-plus') { 60 | const waitPage = plus.nativeObj.View.getViewById('router-loadding'); 61 | waitPage && waitPage.close(); 62 | const launchedHook = router.options.APP?.launchedHook; 63 | launchedHook && launchedHook(); 64 | } 65 | } 66 | } 67 | let time:number = 0; 68 | if (new RegExp(mpPlatformReg, 'g').test(platform)) { 69 | time = (router.options.applet?.animationDuration) as number 70 | } else if (platform === 'app-plus') { 71 | if (funName === 'navigateBack' && lastNavType === 'navigateTo') { 72 | time = (router.options.APP?.animation?.animationDuration) as number 73 | } 74 | } 75 | if (funName === 'navigateTo' || funName === 'navigateBack') { 76 | if (time !== 0) { 77 | await timeOut(time); 78 | } 79 | } 80 | lastNavType = funName; 81 | complete && complete.apply(null, args); 82 | callOkCb && callOkCb.apply(null, args) 83 | } 84 | }); 85 | } 86 | } 87 | export function formatOriginURLQuery( 88 | router:Router, 89 | options:uniNavApiRule, 90 | funName:reNavMethodRule|reNotNavMethodRule 91 | ):uniNavApiRule { 92 | const {url, path, query, animationType, animationDuration, events, success, fail, complete, delta, animation} = options; 93 | const strQuery = stringifyQuery(query || {}); 94 | const queryURL = strQuery === '' ? (path || url) : (path || url) + strQuery; 95 | let animationRule:startAnimationRule = {}; 96 | if (router.options.platform === 'app-plus') { 97 | if (funName !== 'navigateBack') { 98 | animationRule = router.options.APP?.animation || {}; 99 | animationRule = {...animationRule, ...animation || {}}; 100 | } 101 | } 102 | return notDeepClearNull({ 103 | delta, 104 | url: queryURL, 105 | animationType: animationType || animationRule.animationType, 106 | animationDuration: animationDuration || animationRule.animationDuration, 107 | events, 108 | success, 109 | fail, 110 | complete 111 | }) 112 | } 113 | -------------------------------------------------------------------------------- /tea.yaml: -------------------------------------------------------------------------------- 1 | # https://tea.xyz/what-is-this-file 2 | --- 3 | version: 1.0.0 4 | codeOwners: 5 | - '0x6D4379082b9CEa0D5E074c0a27EBBcE41EEe47DE' 6 | quorum: 1 7 | -------------------------------------------------------------------------------- /test/path-to-regexp.spec.ts: -------------------------------------------------------------------------------- 1 | import {createRouter, routesMapKeysRule} from '../src/index'; 2 | 3 | import {routesForMapRoute} from '../src/helpers/utils'; 4 | 5 | const routes = [ 6 | {path: '/pages/login/login', name: 'login', aliasPath: '/'}, 7 | {path: '/pages/page2/page2', name: 'page2', aliasPath: '/page2/:id'}, 8 | {path: '/pages/page3/page3', aliasPath: '/:name/page3/:id'}, 9 | {path: '/pages/animation/animation', aliasPath: '/an-(\\d+)-on'}, 10 | {path: '/static/1/1', aliasPath: '/static/(.*)'}, 11 | {path: '/dynamic/1/1', aliasPath: '/dynamic-*'}, 12 | {path: '/dynamic/3/3', aliasPath: '/dynamic3'}, 13 | {path: '*'} 14 | ]; 15 | 16 | const router = createRouter({ 17 | platform: 'app-plus', 18 | keepUniOriginNav: true, 19 | routes, 20 | }); 21 | 22 | const Vue = function () {}; 23 | Vue.mixin = () => {}; 24 | 25 | router.install(Vue); 26 | 27 | const rules: routesMapKeysRule[] = ['finallyPathMap', 'pathMap']; 28 | 29 | it('别名路径匹配',()=>{ 30 | const toRoute1 = routesForMapRoute(router, '/dynamic3', rules); 31 | expect(toRoute1).toEqual(routes[6]); 32 | 33 | const toRoute2 = routesForMapRoute(router, '/dynamic/3/3', rules); 34 | expect(toRoute2).toEqual(routes[6]); 35 | }) 36 | 37 | it('全局匹配', () => { 38 | const toRoute1 = routesForMapRoute(router, '/pages/login/login', rules); 39 | expect(toRoute1).toEqual(routes[0]); 40 | 41 | const toRoute2 = routesForMapRoute(router,'/pages/login/login?id=666',rules); 42 | expect(toRoute2).toEqual(routes[0]); 43 | 44 | const toRoute3 = routesForMapRoute(router, '/page2/6666', rules); 45 | expect(toRoute3).toEqual(routes[1]); 46 | 47 | const toRoute4 = routesForMapRoute(router, '/page2/6666?id=555', rules); 48 | expect(toRoute4).toEqual(routes[1]); 49 | 50 | const toRoute5 = routesForMapRoute(router, '/pages/page3/page3', rules); 51 | expect(toRoute5).toEqual(routes[2]); 52 | 53 | const toRoute6 = routesForMapRoute(router, '/test/page3/123', rules); 54 | expect(toRoute6).toEqual(routes[2]); 55 | 56 | const toRoute7 = routesForMapRoute(router, '/an-123-on', rules); 57 | expect(toRoute7).toEqual(routes[3]); 58 | 59 | const toRoute8 = routesForMapRoute(router, '/static/aaa/bbb?id=1444&name=999', rules); 60 | expect(toRoute8).toEqual(routes[4]); 61 | 62 | const toRoute9 = routesForMapRoute(router, '/dynamic-6666-5555', rules); 63 | expect(toRoute9).toEqual(routes[5]); 64 | 65 | const toRoute10 = routesForMapRoute(router, '/aaaaaa', rules); 66 | expect(toRoute10).toEqual(routes[7]); 67 | 68 | const toRoute11 = routesForMapRoute(router, '---48848--14545', rules); 69 | expect(toRoute11).toEqual(routes[7]); 70 | }); 71 | -------------------------------------------------------------------------------- /test/query-toggle.spec.ts: -------------------------------------------------------------------------------- 1 | import {deepDecodeQuery} from '../src/helpers/utils'; 2 | 3 | 4 | it('编码回转',()=>{ 5 | const query={ 6 | str:'%E7%9A%84%E6%8C%A5%E6%B4%92U%E7%9B%BE%E5%A5%BD%E6%92%92%E7%AC%AC%E4%B8%89%E5%A4%A7%E5%8E%A6%E5%8F%91%E7%9A%84%E6%92%92321312%2a%EF%BC%88%EF%BF%A5%23%254' 7 | } 8 | const result = deepDecodeQuery(query); 9 | expect(JSON.stringify(result)).toEqual(JSON.stringify({ 10 | str:'的挥洒U盾好撒第三大厦发的撒321312*(¥#%4' 11 | })) 12 | }) 13 | 14 | it('一些乱码字符',()=>{ 15 | const query={ 16 | str:`~!@#$%^&*()_+-,./|][]` 17 | } 18 | const result = deepDecodeQuery(query); 19 | expect(JSON.stringify(result)).toEqual(JSON.stringify({ 20 | str:`~!@#$%^&*()_+-,./|][]` 21 | })) 22 | }) 23 | 24 | it('单个加密参数',()=>{ 25 | const query={ 26 | name:'%7B%22status%22%3Atrue%2C%22list%22%3A%5B%7B%22id%22%3A1%7D%5D%7D' 27 | } 28 | const result = deepDecodeQuery(query); 29 | expect(JSON.stringify(result)).toEqual(JSON.stringify({ 30 | name:{ 31 | status:true, 32 | list:[ 33 | { 34 | id:1 35 | }, 36 | ] 37 | } 38 | })); 39 | }) 40 | 41 | it('单个普通参数',()=>{ 42 | const query={ 43 | name:'hhyang', 44 | ages:22, 45 | open:true 46 | } 47 | const result = deepDecodeQuery(query); 48 | 49 | expect(JSON.stringify(result)).toEqual(JSON.stringify(query)); 50 | }) 51 | 52 | it('单个普通参数2',()=>{ 53 | const query={ 54 | name:'wyb', 55 | ages:22, 56 | open:true 57 | } 58 | const result = deepDecodeQuery(query); 59 | 60 | expect(JSON.stringify(result)).toEqual(JSON.stringify(query)); 61 | }) 62 | 63 | it('深度参数加混乱',()=>{ 64 | const query={ 65 | list:[ 66 | 1,'2',true,encodeURIComponent(JSON.stringify({name:111})),{ 67 | name:'hhyang', 68 | strObj:encodeURIComponent(JSON.stringify({name:222})) 69 | } 70 | ], 71 | obj:{ 72 | strObj2:encodeURIComponent(JSON.stringify({name:333})), 73 | number:1, 74 | boolean:false, 75 | }, 76 | str4:encodeURIComponent(JSON.stringify({name:444})) 77 | } 78 | const result = deepDecodeQuery(query); 79 | 80 | expect(JSON.stringify(result)).toEqual(JSON.stringify({ 81 | list:[ 82 | 1,'2',true,{name:111},{ 83 | name:'hhyang', 84 | strObj:{name:222} 85 | } 86 | ], 87 | obj:{ 88 | strObj2:{name:333}, 89 | number:1, 90 | boolean:false, 91 | }, 92 | str4:{name:444} 93 | })); 94 | }) -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "CommonJS", 5 | "strict": true, 6 | "esModuleInterop": true, 7 | "skipLibCheck": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "lib": ["ES2015", "DOM"], 10 | "outDir": "dist/src", 11 | "declaration": true, 12 | "declarationMap": false, 13 | "rootDir": "./src", 14 | "baseUrl": ".", 15 | "paths": {} 16 | }, 17 | "include": ["./src/global.d.ts", "./src/*"], 18 | "exclude": ["node_modules", "**/*.spec.ts"] 19 | } 20 | -------------------------------------------------------------------------------- /webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | const CopyPlugin = require('copy-webpack-plugin'); 3 | const webpack =require('webpack'); 4 | const cmd = require('node-cmd'); 5 | 6 | 7 | const {data:versions,err} = cmd.runSync('npm v uni-simple-router versions'); 8 | 9 | if(err){ 10 | console.log('获取线上版本失败,无法继续打包。。。') 11 | process.exit(1); 12 | } 13 | 14 | let lastVersion=''; 15 | const list=JSON.parse(versions.replace(/'/g,'"')).reverse(); 16 | for(let i=0;i { 55 | return JSON.stringify(process.env.npm_package_name.toLocaleUpperCase()) 56 | }, true ), 57 | $npm_package_version: webpack.DefinePlugin.runtimeValue(() => { 58 | return JSON.stringify(process.env.npm_package_version.toLocaleUpperCase()) 59 | }, true ), 60 | $npm_package_last_version: webpack.DefinePlugin.runtimeValue(() => { 61 | return JSON.stringify(lastVersion) 62 | }, true ), 63 | }) 64 | ], 65 | }; 66 | -------------------------------------------------------------------------------- /webpack/webpack.dev.js: -------------------------------------------------------------------------------- 1 | const {merge} = require('webpack-merge'); 2 | const {resolve} = require('path'); 3 | const common = require('./webpack.common.js'); 4 | const CopyPlugin = require('copy-webpack-plugin'); 5 | 6 | const output = resolve(__dirname, '../examples/uni-simple-router2.0/dist'); 7 | 8 | module.exports = merge(common, { 9 | mode: 'development', 10 | devtool: 'source-map', 11 | output: { 12 | path: output, 13 | filename: 'uni-simple-router.js', 14 | }, 15 | plugins: [ 16 | new CopyPlugin([ 17 | { 18 | force: true, 19 | from: resolve(__dirname, '../src/component'), 20 | to: output, 21 | } 22 | ]), 23 | ], 24 | }); 25 | -------------------------------------------------------------------------------- /webpack/webpack.prod.js: -------------------------------------------------------------------------------- 1 | const {resolve} = require('path'); 2 | const {merge} = require("webpack-merge"); 3 | const common = require("./webpack.common.js"); 4 | const rimraf = require('rimraf'); 5 | 6 | 7 | function resolvePath(dir) { 8 | return resolve(__dirname, '../', dir) 9 | } 10 | 11 | rimraf('dist', () => {}); 12 | 13 | module.exports = merge(common, { 14 | mode: "production", 15 | output: { 16 | path: resolvePath('dist'), 17 | filename: 'uni-simple-router.js', 18 | }, 19 | }) --------------------------------------------------------------------------------