├── .editorconfig ├── .github └── workflows │ └── nodejs.yml ├── .gitignore ├── .travis.yml ├── CNAME ├── LICENSE ├── README.md ├── cache ├── README.md ├── package.json ├── src │ ├── cache-operator.ts │ ├── cache.ts │ └── index.ts └── tsconfig.json ├── cancel-token ├── README.md ├── index.ts ├── package.json └── tsconfig.json ├── downloader ├── README.md ├── package.json ├── src │ ├── downloader.ts │ ├── index.ts │ └── transform.ts └── tsconfig.json ├── fetch ├── README.md ├── fetch.ts └── package.json ├── lerna.json ├── life-cycle ├── README.md ├── package.json ├── src │ ├── configuration.ts │ ├── ensure-online.ts │ ├── index.ts │ ├── life-cycle.ts │ └── listeners.ts └── tsconfig.json ├── network-utils ├── README.md ├── package.json ├── src │ ├── index.ts │ └── promise.finally.ts └── tsconfig.json ├── network ├── README.md ├── package.json ├── src │ ├── cache.ts │ ├── index.ts │ └── set-config.ts ├── test │ ├── config.ts │ └── methods.ts └── tsconfig.json ├── package-lock.json ├── package.json ├── promise ├── README.md ├── cancelable-promise.ts ├── index.ts ├── package.json ├── promise.finally.ts └── promisify.ts ├── queue ├── README.md ├── package.json ├── src │ └── index.ts └── tsconfig.json ├── request ├── README.md ├── package.json ├── src │ ├── http.ts │ ├── index.ts │ └── transform.ts ├── test │ ├── cancel.ts │ ├── config.ts │ ├── type.ts │ └── url.ts └── tsconfig.json ├── socket ├── README.md └── package.json ├── tsconfig.json ├── tslint.json └── uploader ├── README.md ├── package.json ├── src ├── index.ts ├── transform.ts └── uploader.ts └── tsconfig.json /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | indent_style = space 6 | charset = utf-8 7 | 8 | [src/**] 9 | indent_size = 4 -------------------------------------------------------------------------------- /.github/workflows/nodejs.yml: -------------------------------------------------------------------------------- 1 | name: Node CI 2 | 3 | on: [push] 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | 10 | strategy: 11 | matrix: 12 | node-version: [8.x, 10.x, 12.x] 13 | 14 | steps: 15 | - uses: actions/checkout@v1 16 | - name: Use Node.js ${{ matrix.node-version }} 17 | uses: actions/setup-node@v1 18 | with: 19 | node-version: ${{ matrix.node-version }} 20 | - name: npm install 21 | run: npm install 22 | env: 23 | CI: true 24 | - name: npm test 25 | run: npm test 26 | env: 27 | CI: true 28 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | */dist/* 63 | */types/* 64 | */es/* 65 | 66 | */package-lock.json 67 | 68 | .changelog -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # https://docs.travis-ci.com/user/languages/javascript-with-nodejs/ 2 | 3 | sudo: false 4 | language: node_js 5 | node_js: 6 | - "8" 7 | - "10" 8 | - stable 9 | 10 | git: 11 | depth: 1 -------------------------------------------------------------------------------- /CNAME: -------------------------------------------------------------------------------- 1 | miniprogram-network.newfuture.cc -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [miniprogram-network](https://github.com/NewFuture/miniprogram-network) 2 | 3 | > 小程序网络库,提供完整`代码自动完成` 和 `类型检查`,支持`Promise`、自动`重试`、`缓存`、`取消`、`自定义超时`、`自动暂停恢复`、全局`拦截`、和`事件监听`等…… 4 | > 5 | > Redefine the network API of Wechat MiniProgram, including full `IntelliSense` and `Type Checking`, with `Promise`,`retry`,`Cache`,`CancelToken`,`timeout`,`ensureOnline`, global `interceptors`, `event listeners` and more. 6 | 7 | ## Features 主要功能 8 | 9 | * [x] Promise泛型Promise支持 10 | * [x] Retry 网络错误自动重试 11 | * [x] Cache 底层缓存支持(包括并发请求合并) 12 | * [x] CancelToken 可取消操作 13 | * [x] Timeout 自定义超时时间 14 | * [x] 确保网络可用时发送请求,离线和切换到后台时暂停发送请求 15 | * [x] timestamp 记录耗时统计(需开启配置) 16 | * [x] 每个请求的原生回调接口支持(`onHeadersReceived`事件)和(`onProgressUpdate`事件) 17 | * [x] Interceptors 拦截器 `transformSend`/ `transformRresponse`自定义数据拦截 18 | * [x] Listeners 全局事件监听`onSend`,`onResponse`,`onRejected`,`onAbort`,`onComplete` 19 | * [x] 支持全局默认配置和每个请求单独配置 20 | * [x] 类型推断和代码自动提示(TypeScript) 21 | 22 | ## Examples 示例 23 | 24 | ### 安装 25 | 26 | ```sh 27 | npm i miniprogram-network 28 | ``` 29 | 30 | ### JavaScript 31 | 32 | > es5 兼容 33 | 34 | ```js 35 | const Network = require('miniprogram-network'); 36 | // 也可使用 es6 import 写法 37 | 38 | // setConfig设置所有网络请求的全局默认配置,一次定义,所有文件中使用均生效 39 | Network.setConfig('baseURL','https://miniprogram-network.newfuture.cc/') 40 | // 也可Network.REQUEST.Defaults,Network.DOWNLOAD.Defaults,Network.UPLOAD.Defaults 分别设置不同默认配置 41 | Network.REQUEST.Defaults.transformResponse = Network.transformRequestResponseOkData 42 | 43 | Network.get('index.html') 44 | .then(res=>console.log(res)) 45 | .finally(()=>{console.info('done')}) //支持 finally操作 46 | 47 | Network.patch('items/{id}',{dataKey:'dataValue'},{ 48 | params: {id:123456}, // 绑定模板参数 49 | retry:3,// 重试3次 50 | }).then((item)=>console.log(item)) 51 | 52 | Network.download('network/','lcoalpath',{ 53 | onProgressUpdate:progressUpdateCallBack,//进度回调 54 | transformResponse: Network.transformDownloadResponseOkData, //状态码2xx成功,返回本地路径 55 | }).then(path=>console.log(path)) 56 | .catch(console.error) 57 | ``` 58 | 59 | ### TypeScript 60 | 61 | > 装完即用,无需额外配置和类型声明 62 | 63 | ```ts 64 | import {setConfig,REQUEST,download,transformRequestResponseOkData,transformDownloadResponseOkData, delayRetry} from 'miniprogram-network'; 65 | 66 | // setConfig设置所有网络请求的全局默认配置,一次定义,所有文件中使用均生效 67 | setConfig('baseURL', 'https://miniprogram-network.newfuture.cc/'); 68 | // 也可通过 REQUEST.Defaults,DOWNLOAD.Defaults,UPLOAD.Defaults 分别设置不同默认配置 69 | REQUEST.Defaults.transformResponse = transformRequestResponseOkData; 70 | // 请求发送失败时, 间隔1s再次重试,最多重试2次 71 | REQUEST.Defaults.retry = delayRetry(1000,2); 72 | 73 | REQUEST.get('index.html') 74 | .then(res=>console.log(res)) 75 | .finally(()=>{console.info('done')}); //支持 finally操作 76 | 77 | REQUEST.patch('items/{id}',{dataKey:'dataValue'},{ 78 | params: {id:123456}, // 绑定参数 79 | retry:3,// 重试3次 80 | }).then((item:Item)=>console.log(item)); 81 | 82 | download('network/','lcoalpath',{ 83 | onProgressUpdate: (res)=>console.log(res), //进度回调 84 | transformResponse: transformDownloadResponseOkData, //状态码2xx成功,返回本地路径 85 | }).then((path:string)=>console.log(path)) 86 | .catch(console.error); 87 | ``` 88 | 89 | `miniprogram-network`对网络操作做了统一封装,详细操作和用法可查看完整[miniprogram-network 完整文档](network) 90 | 91 | * 如果只需要微信request的相关的HTTP操作API可只使用[miniprogram-request(文档🔗)](request) 92 | * 如果只需要微信downloadFile的相关下载API可只使用[miniprogram-downloader(文档🔗)](downloader) 93 | * 如果只需要微信uploadFile的相关上传API可只使用[miniprogram-uploader(文档🔗)](uploader) 94 | 95 | 96 | ### IntelliSense & Types 代码提示和类型检查 97 | 98 | 包含完整的类型定义,结合编辑器(VScode)等,能提供完整的代码提示和自动完成功能。 99 | 100 | ![Intelligent code completion](https://user-images.githubusercontent.com/6290356/50153198-b569bd80-0300-11e9-859c-5742d070434a.png) 101 | 102 | 对于TypeScript提供泛型支持,可完整的进行静态类型检查。 103 | 104 | 105 | ## Main Packages 所有包 [![Build Status](https://travis-ci.com/NewFuture/miniprogram-network.svg?branch=master)](https://travis-ci.com/NewFuture/miniprogram-network) 106 | 107 | > 如果担心包依赖多,可使用[miniprogram-build](https://github.com/NewFuture/miniprogram-build) 打包小程序rollup 精简为单文件。 108 | 109 | 110 | 111 | tips: 自`miniprogram-network` >= 5.0.0 底层默认不在直接使用`miniprogram-queue`进行队列封装, 如果有需要可自行引用, 或直接使用 v4.x 112 | 113 | > * wx.request 自基础库 1.4.0 (2017.07) 小程序开始支持队列 114 | > * wx.downloadFile 自基础库 1.4.0 (2017.07) 小程序开始支持队列 115 | > * wx.uploadFile 自基础库 2.4.1 (2018.11) 小程序开始支持队列 116 | 117 | ![network-dependencies-graph](https://user-images.githubusercontent.com/6290356/58758745-6f24b580-8552-11e9-890d-02c4559eb400.png) 118 | 119 | * [miniprogram-network](network) All in one 小程序网络库库合集[![npm version](https://badge.fury.io/js/miniprogram-network.svg)](https://npmjs.com/package/miniprogram-network) 120 | * `Request` from `miniprogram-request` 121 | * `Upload` from `miniprogram-uploader` 122 | * `Download` from `miniprogram-downloader` 123 | * 网络缓存和请求合并 124 | * [miniprogram-request](request) 小程序请求库 [![npm version](https://badge.fury.io/js/miniprogram-request.svg)](https://npmjs.com/package/miniprogram-request) 125 | * [x] Promise支持+finally+泛型 126 | * [x] CancelToken 取消支持 127 | * [x] retry 网络错误重试 128 | * [x] Interceptors 自定义请求/响应拦截 129 | * [x] Listeners 事件监听 130 | * [x] OnHeadersReceived 响应头回调 131 | * [x] axios like API 132 | * [miniprogram-uploader](uploader) 小程序上传库 [![npm version](https://badge.fury.io/js/miniprogram-uploader.svg)](https://npmjs.com/package/miniprogram-uploader) 133 | * [x] Promise支持+finally+泛型 134 | * [x] Cancelable 可取消 135 | * [x] OnProgressUpdate 进度回调 136 | * [x] OnHeadersReceived 响应头回调 137 | * [x] retry 网络错误重试 138 | * [x] Interceptors 自定义请求/响应拦截 139 | * [x] Listeners 事件监听 140 | * [miniprogram-downloader](downloader) 小程序下载库 [![npm version](https://badge.fury.io/js/miniprogram-downloader.svg)](https://npmjs.com/package/miniprogram-downloader) 141 | * [x] Promise支持+finally+泛型 142 | * [x] Cancelable 可取消 143 | * [x] retry 网络错误重试 144 | * [x] OnProgressUpdate 进度回调 145 | * [x] OnHeadersReceived 响应头回调 146 | * [x] Interceptors 自定义请求/响应拦截 147 | * [x] Listeners 事件监听 148 | * [miniprogram-network-life-cycle](life-cycle) 网络操作流程和事件 149 | * [x] 事件周期监听器 150 | * [x] 事件周期拦截器 151 | * [x] 自定义超时 152 | * [x] 时间戳打点 153 | ![life-cycle](https://user-images.githubusercontent.com/6290356/49631309-6bddc080-fa2c-11e8-9a41-88fb50b2a1b7.png) 154 | * [miniprogram-fetch](fetch) 小程序中使用[Fetch API](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch) [![npm version](https://badge.fury.io/js/miniprogram-fetch.svg)](https://npmjs.com/package/miniprogram-fetch) 155 | * [x] 自动队列支持 156 | * [miniprogram-queue](queue) 自定义队列封装 [![npm version](https://badge.fury.io/js/miniprogram-queue.svg)](https://npmjs.com/package/miniprogram-queue) 157 | * [x] 可自动注入/手动管理 158 | * [x] 支持取消操作(`abort`) 159 | * [x] OnProgressUpdate 进度回调 160 | * [x] OnHeadersReceived 响应头回调 161 | * [x] 支持插队 162 | * [x] 支持自定义超时 163 | -------------------------------------------------------------------------------- /cache/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-network-cache [![npm version](https://badge.fury.io/js/miniprogram-network-cache.svg)](https://npmjs.com/package/miniprogram-network-cache) 2 | 3 | 4 | > The cache lib of [miniprogram-network](https://npmjs.com/package/miniprogram-network) 5 | 6 | ## 请求缓存 7 | ```js 8 | import {CacheOperator} from 'miniprogram-network-cache'; 9 | const cacheRequest = CacheOperator.createHandler(wx.request); 10 | cacheRequest({url:'https://miniprogram-network.newfuture.cc'}) 11 | cacheRequest({url:'https://miniprogram-network.newfuture.cc'}) // 请求将被合并 12 | ``` 13 | 14 | ```js 15 | import {CacheOperator} from 'miniprogram-network-cache'; 16 | const cacheRequest = new CacheOperator(wx.request,{ 17 | /** 18 | * 缓存时间 19 | */ 20 | expire: number; 21 | /** 22 | * 结果缓存条件 23 | * @param res 结果 24 | */ 25 | resultCondition:(res: TRes)=>boolean; 26 | /** 27 | * 参数缓存条件,无则全部缓存 28 | * @param options request/downloadFile参数 29 | * @returns 返回string键值,无返回值时不进行缓存和请求合并 30 | */ 31 | keyBuilder?(options: TOptions): string | void | null; 32 | }); 33 | 34 | ``` 35 | 36 | ## 配置 37 | 38 | * `expire` 缓存过期时间单位ms,为0时不缓存只合并请求 39 | * `resultCondition` 结果缓存条件, 默认条件 `isOkResult` 40 | * `keyBuilder` 自定义缓存键, 返回`非真值`则不缓存, 默认使用`defaultKeyBuilder` 41 | 42 | ## export 43 | 44 | * `defaultKeyBuilder(opts)` 45 | * `isOkResult(res)` 46 | * `CacheOperator` -------------------------------------------------------------------------------- /cache/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-network-cache", 3 | "version": "5.3.0-alpha.0", 4 | "description": "minprogram network cache ", 5 | "keywords": [ 6 | "netork-cache", 7 | "miniprogram", 8 | "小程序", 9 | "缓存" 10 | ], 11 | "homepage": "https://miniprogram-network.newfuture.cc/cache/", 12 | "bugs": { 13 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "NewFuture", 21 | "files": [ 22 | "dist/", 23 | "src/", 24 | "es/" 25 | ], 26 | "main": "dist/index.js", 27 | "module": "es/index.js", 28 | "scripts": { 29 | "build": "npm run build:es5 && npm run build:es6", 30 | "build:es5": "tsc", 31 | "build:es6": "tsc -m es6 --target es6 --outDir es", 32 | "clean": "rimraf -rf es dist types ", 33 | "lint": "tslint -p . -c ../tslint.json", 34 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 35 | "test": "echo \"Error: no test specified\" && exit 1" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cache/src/cache-operator.ts: -------------------------------------------------------------------------------- 1 | import { Cache } from './cache'; 2 | 3 | /** 4 | * 删除数组中的元素 5 | * @param array 数组 6 | * @param value 值 7 | */ 8 | function arrayRemove(array: T[], value: T): void { 9 | const index = array.indexOf(value); 10 | if (index >= 0) { 11 | array.splice(index, 1); 12 | } 13 | } 14 | 15 | /** 16 | * 是否为数组中的唯一元素 17 | * @param array 数组 18 | * @param value 值 19 | */ 20 | function isEmptyOrOnly(array: T[], value: T): boolean { 21 | return array.length === 0 || (array.length === 1 && array[0] === value); 22 | } 23 | /** 24 | * 默认缓存索引生成函数, 25 | * 使用 `url`,`method`,`responseType`,`dataType`,`filePath`,`name`参数 + `data`或`formData`构建缓存Key 26 | * 请求的`header` 默认会被忽略 27 | * @param opts 请求参数对象 28 | * @template TOptions 完整请求参数 29 | * @returns string 30 | */ 31 | export function defaultKeyBuilder(opts: TOptions): string { 32 | /** 33 | * 缓存的请求字段 34 | * https://developers.weixin.qq.com/miniprogram/dev/api/wx.request.html 35 | */ 36 | const CACHE_FIELDS = [ 37 | 'url', // all 38 | 'method', // request 39 | 'responseType', // request 40 | 'dataType', // request 41 | 'filePath', // download 42 | 'name'// upload 43 | // 'header' 44 | // 'data' 45 | ]; 46 | const data = (opts as { data?: any }).data || (opts as { formData?: any }).formData; 47 | return JSON.stringify(opts, CACHE_FIELDS) + (data ? JSON.stringify(data) : ''); 48 | } 49 | 50 | /** 51 | * 是否为2xx数据 52 | * @param res 完整返回数据 53 | */ 54 | export function isOkResult(res: BaseSuccessRes): boolean { 55 | return res && res.statusCode >= 200 && res.statusCode < 300; 56 | } 57 | 58 | /** 59 | * 缓存操作, 60 | * 维护缓存结果,自动合并同样请求的并发操作 61 | * @template TRes 操作结果回调数据类型 62 | * @template TOptions 参数数据类型 63 | * @template TTask 微信任务类型 64 | */ 65 | export class CacheOperator< 66 | TRes extends BaseSuccessRes = BaseSuccessRes, 67 | TOptions extends WxOptions = WxOptions, // 微信操作函数 68 | TTask extends WxTask = WxTask, // 微信操作的任务类型 69 | > { 70 | /** 71 | * 缓存配置 72 | */ 73 | public readonly config: Configuration; 74 | 75 | private readonly op: (option: TOptions) => TTask; 76 | private readonly cache: Cache = new Cache(); 77 | /** 78 | * 正在处理的回调 79 | */ 80 | private readonly callbackListMap: { [key: string]: { success: Function[]; fail: Function[]; complete: Function[]; task: WxTask } } = {}; 81 | /** 82 | * 处理完的回调,待删除 83 | */ 84 | private readonly completeMap: { [key: string]: Function[] } = {}; 85 | 86 | /** 87 | * @param operator 底层操作 88 | * @param config 默认配置 89 | */ 90 | constructor(operator: (option: TOptions) => TTask, config?: Configuration) { 91 | this.op = operator; 92 | this.config = config || { 93 | expire: 15 * 60 * 1000, 94 | resultCondition: isOkResult 95 | }; 96 | } 97 | 98 | /** 99 | * 快速创建一个 100 | */ 101 | public static createHandler< 102 | TRes extends BaseSuccessRes = BaseSuccessRes, 103 | TOptions extends WxOptions = WxOptions, // 微信操作函数 104 | TTask extends WxTask = WxTask, // 微信操作的任务类型 105 | >(operator: (option: TOptions) => TTask, config?: Configuration): CacheOperator['handle'] { 106 | const cacheOperator = new CacheOperator(operator, config); 107 | return cacheOperator.handle.bind(cacheOperator); 108 | } 109 | 110 | /** 111 | * 缓存处理 112 | * @param options - 参数 113 | */ 114 | public handle(options: TOptions & Partial>): TTask { 115 | const keyBuilder = options.keyBuilder || this.config.keyBuilder || defaultKeyBuilder; 116 | const key = keyBuilder(options); 117 | if (!key) { 118 | // 不缓存 119 | return this.op(options); 120 | } 121 | const result = this.cache.get(key); 122 | if (result) { 123 | // 缓存命中 124 | result.cache = (result.cache || 0) + 1; 125 | try { 126 | if (options.success) { options.success(result); } 127 | } catch (error) { 128 | this.cache.delete(key); 129 | } 130 | if (options.complete) { options.complete(result); } 131 | } else if (this.callbackListMap[key]) { 132 | // 请求已发送过 133 | const callback = this.callbackListMap[key]; 134 | if (options.success) { callback.success.push(options.success); } 135 | if (options.fail) { callback.fail.push(options.fail); } 136 | if (options.complete) { callback.complete.push(options.complete); } 137 | } else { 138 | // 请求未发送过 139 | 140 | const data = { 141 | ...options, 142 | success: (res: TRes) => { 143 | const expire = options.expire === undefined ? this.config.expire : options.expire; 144 | // 过期时间为0不缓存,但是会合并请求 145 | if (expire > 0 && (options.resultCondition || this.config.resultCondition)(res)) { 146 | // 缓存请求结果 147 | this.cache.set(key, res, expire); 148 | } 149 | this._getMapBeforeComplete(key).success 150 | .forEach((v) => { v(res); }); 151 | }, 152 | fail: (res: { errMsg: string }) => { 153 | // fail 回调异步化 (微信实现可能是同步调用) 154 | // tslint:disable-next-line: no-floating-promises 155 | Promise.resolve(this._getMapBeforeComplete(key).fail) 156 | .then(f => { f.forEach((v) => { v(res); }); }); 157 | }, 158 | complete: (res: TRes) => { 159 | this.completeMap[key].forEach((v) => { v(res); }); 160 | // tslint:disable-next-line: no-dynamic-delete 161 | delete this.completeMap[key]; 162 | } 163 | }; 164 | this.callbackListMap[key] = { 165 | success: options.success ? [options.success] : [], 166 | fail: options.fail ? [options.fail] : [], 167 | complete: options.complete ? [options.complete] : [], 168 | task: {} as WxTask 169 | }; 170 | // 微信task同步创建异步调用 171 | // 防止同步执行fail时 this.callbackListMap[key] 还未赋值 172 | // 先赋值 this.callbackListMap[key] 再 执行 this.op(data)) 173 | return (this.callbackListMap[key].task = this.op(data)); 174 | } 175 | // tslint:disable-next-line: no-object-literal-type-assertion 176 | return { 177 | abort: () => { 178 | const cbMap = this.callbackListMap[key]; 179 | if (cbMap) { 180 | if ( 181 | isEmptyOrOnly(cbMap.success, options.success) 182 | && isEmptyOrOnly(cbMap.fail, options.fail) 183 | && isEmptyOrOnly(cbMap.complete, options.complete) 184 | ) { 185 | cbMap.task.abort(); 186 | } else { 187 | if (options.success) { 188 | arrayRemove(cbMap.success, options.success); 189 | const callbackList = []; 190 | if (options.fail) { 191 | arrayRemove(cbMap.fail, options.fail); 192 | callbackList.push(options.fail); 193 | } 194 | if (options.complete) { 195 | arrayRemove(cbMap.complete, options.complete); 196 | callbackList.push(options.complete); 197 | } 198 | const res = { errMsg: 'request:fail abort', source: CacheOperator.name }; 199 | callbackList.forEach(f => { f(res); }); 200 | } 201 | } 202 | } 203 | }, 204 | onHeadersReceived: (f) => { 205 | if (this.callbackListMap[key]) { 206 | this.callbackListMap[key].task.onHeadersReceived(f); 207 | } else { 208 | f(this.cache.get(key) || {}); 209 | } 210 | }, 211 | onProgressUpdate: (f) => { 212 | if (this.callbackListMap[key]) { 213 | const task = this.callbackListMap[key].task; 214 | if (task.onProgressUpdate) { 215 | task.onProgressUpdate(f); 216 | } 217 | } else { 218 | f({ progress: 100 }); 219 | } 220 | } 221 | } as TTask; 222 | } 223 | 224 | /** 225 | * fixed #10 226 | * 在回调中再次发起操作前清除任务 227 | * @param key cacheKey 228 | */ 229 | private _getMapBeforeComplete(key: string): { success: Function[]; fail: Function[]; complete: Function[] } { 230 | // remove the MapList from the `callbackMapList` 231 | const list = this.callbackListMap[key]; 232 | // tslint:disable-next-line: no-dynamic-delete 233 | delete this.callbackListMap[key]; 234 | this.completeMap[key] = list.complete; 235 | return list; 236 | } 237 | } 238 | 239 | interface CacheRes { 240 | /** 241 | * 缓存命中次数 242 | */ 243 | cache?: number; 244 | } 245 | export interface Configuration { 246 | /** 247 | * 缓存时间 248 | */ 249 | expire: number; 250 | /** 251 | * 结果缓存条件 252 | * @param res 结果 253 | */ 254 | resultCondition(res: TRes): boolean; 255 | /** 256 | * 参数缓存条件,无则全部缓存 257 | * @param options request/downloadFile参数 258 | * @returns 返回string键值,无返回值时不进行缓存和请求合并 259 | */ 260 | keyBuilder?(options: TOptions): string | void | null | false; 261 | } 262 | 263 | interface WxTask { 264 | abort(): void; 265 | /** HTTP Response Header 事件的回调函数 */ 266 | onHeadersReceived(callback: (result: any) => void): void; 267 | /** 下载进度变化事件的回调函数 */ 268 | onProgressUpdate?(callback: (res: any) => any): void; 269 | } 270 | 271 | interface WxOptions { 272 | /** 开发者服务器接口地址 */ 273 | url: string; 274 | /** 275 | * HTTP 请求 Header 276 | */ 277 | header?: object; 278 | /** 接口调用结束的回调函数(调用成功、失败都会执行) */ 279 | complete?(res: { errMsg: string }): void; 280 | /** 接口调用失败的回调函数 */ 281 | fail?(res: { errMsg: string }): void; 282 | /** 接口调用成功的回调函数 */ 283 | success?(res: any): any; 284 | } 285 | 286 | interface BaseSuccessRes { 287 | errMsg: string; 288 | statusCode: number; 289 | } 290 | -------------------------------------------------------------------------------- /cache/src/cache.ts: -------------------------------------------------------------------------------- 1 | /** 缓存管理 */ 2 | export class Cache { 3 | private readonly map: Map = new Map(); 4 | /** 5 | * 设置缓存 6 | * @param key - 键 7 | * @param value - 值 8 | * @param expire - 有效期(毫秒) 9 | */ 10 | // tslint:disable-next-line: no-reserved-keywords 11 | public set(key: string, value: T, expire: number) { 12 | this.map.set(key, [value, expire > 0 ? Date.now() + expire : 0]); 13 | } 14 | /** 15 | * 获取缓存,不存在返回undefined 16 | * @param key - 键 17 | */ 18 | // tslint:disable-next-line: no-reserved-keywords 19 | public get(key: string): T | undefined { 20 | if (this.map.has(key)) { 21 | const [value, expireTime] = this.map.get(key)!; 22 | if (expireTime >= Date.now()) { 23 | return value; 24 | } else { 25 | this.map.delete(key); 26 | } 27 | } 28 | return undefined; 29 | } 30 | /** 31 | * 删除缓存,返回是否删除成功 32 | * @param key - 键 33 | */ 34 | // tslint:disable-next-line: no-reserved-keywords 35 | public delete(key: string): boolean { 36 | return this.map.delete(key); 37 | } 38 | /** 39 | * 缓存是否存在 40 | * @param key - 键 41 | */ 42 | public has(key: string): boolean { 43 | if (this.map.has(key)) { 44 | if (this.map.get(key)![1] > Date.now()) { 45 | return true; 46 | } 47 | this.map.delete(key); 48 | } 49 | return false; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /cache/src/index.ts: -------------------------------------------------------------------------------- 1 | export { CacheOperator, Configuration, defaultKeyBuilder, isOkResult } from './cache-operator'; 2 | -------------------------------------------------------------------------------- /cache/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015", 7 | "es2015.promise" 8 | ] 9 | }, 10 | "files": [ 11 | "src/index.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /cancel-token/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-cancel-token [![npm version](https://badge.fury.io/js/miniprogram-cancel-token.svg)](https://npmjs.com/package/miniprogram-cancel-token) 2 | 3 | > Cancle Token for Promise in MiniProgram 4 | 5 | ## APT 6 | 7 | ### CancelTokenSource 外部调用(暴露出的API接口) 8 | 9 | * `cancel(reason?)` 取消操作 10 | * `token` 调用cancel时所取消的 CancelToken 11 | 12 | ### CancelToken 响应cancel操作(API内部实现) 13 | 14 | * `#source()` 静态方法创建一个CancelTokenSource 15 | * `promise` 调用cancel时所触发的promise; 16 | * `isCancelled()` 判断是否已经取消过; 17 | * `throwIfRequested()` 取消则抛出异常; 18 | 19 | ## usage 20 | 21 | ```js 22 | function doFoo(foo, cancelToken) { 23 | return new Promise((resolve, reject) => { 24 | cancelToken.throwIfRequested(); 25 | cancelToken.promise.then(()=>{ 26 | // do somethig to cancel 27 | // like xhr.abort() 28 | foo.abort(); 29 | }) 30 | resolve('something'); 31 | }); 32 | } 33 | ``` 34 | 35 | ```js 36 | import CancelToken from 'miniprogram-cancel-token' 37 | //create CancelToken Source 38 | const cts = CancelToken.scoure(); 39 | //use canceltoken 40 | doFoo(foo,stc.token).then(console.log); 41 | // cancle it 42 | cts.cancel(); 43 | 44 | ``` 45 | 46 | ## references 47 | * API inspired by -------------------------------------------------------------------------------- /cancel-token/index.ts: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * ICancelTokenSource 4 | */ 5 | export interface ICancelTokenSource { 6 | /** 7 | * token 8 | */ 9 | readonly token: CancelToken; 10 | /** 11 | * 取消函数 12 | */ 13 | cancel(reason?: T): void; 14 | } 15 | 16 | type CancelResolver = (res: { errMsg: string; cancel?: boolean }) => void; 17 | 18 | /** 19 | * 为异步Promise和async/await 提供取消接口 20 | * @example `const cts = CancleToken.source(); cts.cancle()` 21 | */ 22 | export class CancelToken { 23 | /** 24 | * Promise of CancelToken 25 | */ 26 | public readonly promise: Promise<{ errMsg: string }>; 27 | private reason?: string | { errMsg: string }; 28 | 29 | /** 30 | * 生成CancelToken 31 | * @param executor - callback 32 | */ 33 | private constructor(executor: (cancel: ICancelTokenSource['cancel']) => void) { 34 | let resolve: CancelResolver; 35 | // tslint:disable-next-line:promise-must-complete 36 | this.promise = new Promise<{ errMsg: string }>((res: CancelResolver) => { resolve = res; }); 37 | executor((reason?: string) => { 38 | if (this.reason === undefined) { // 防止重复执行 39 | this.reason = reason === undefined ? 'abort' : reason; 40 | resolve({ errMsg: this.reason, cancel: true }); 41 | } 42 | }); 43 | } 44 | 45 | /** 46 | * Create TokenSoure 47 | * @returns 生成一个CancelTokenSource 48 | */ 49 | public static source(): ICancelTokenSource { 50 | let cancel: ICancelTokenSource['cancel']; 51 | const token = new CancelToken((c) => { 52 | cancel = c; 53 | }); 54 | 55 | ///@ts-ignore 56 | return { token, cancel }; 57 | } 58 | 59 | /** 60 | * 是否已取消 61 | */ 62 | public isCancelled(): boolean { 63 | return this.reason !== undefined; 64 | } 65 | 66 | /** 67 | * 如果已取消,抛出异常 68 | * 防止二次取消 69 | * @throws { errMsg: string } 70 | */ 71 | public throwIfRequested(): void | never { 72 | if (this.reason !== undefined) { 73 | throw typeof this.reason === 'string' ? { errMsg: this.reason, cancel: true, source: CancelToken.name } : this.reason; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /cancel-token/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-cancel-token", 3 | "version": "5.1.2", 4 | "description": "cancel-token", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 8 | }, 9 | "license": "Apache-2.0", 10 | "author": "New Future", 11 | "files": [ 12 | "dist/", 13 | "src/", 14 | "es/" 15 | ], 16 | "main": "dist/index.js", 17 | "module": "es/index.js", 18 | "scripts": { 19 | "build": "npm run build:es5 && npm run build:es6", 20 | "build:es5": "tsc", 21 | "build:es6": "tsc -m es6 --target es6 --outDir es", 22 | "clean": "rimraf -rf es dist types ", 23 | "lint": "tslint -p . -c ../tslint.json", 24 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 25 | "test": "echo \"Error: no test specified\" && exit 1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /cancel-token/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015" 7 | ] 8 | }, 9 | "files": [ 10 | "index.ts" 11 | ] 12 | } -------------------------------------------------------------------------------- /downloader/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-downloader [![npm version](https://badge.fury.io/js/miniprogram-downloader.svg)](https://npmjs.com/package/miniprogram-downloader) 2 | 3 | > An axios API like `Download` package for MiniProgram 4 | > 5 | > 小程序下载封装 6 | > 小程序网络库[miniprogram-network](https://github.com/NewFuture/miniprogram-network) 核心库之一 7 | 8 | 9 | ## API 10 | 11 | ### methods: 12 | 13 | * `download(options: DownloadOption): Promise`; 14 | * `download(url: string, filePath?: string, options?): Promise` 15 | 16 | ### options 17 | 18 | * [x] `url` 地址 **required** (_只能请求时设置for single request_) 19 | * [x] `filePath` 保存地址 (_只能请求时设置for single request_) 20 | * [x] `cancelToken` 取消 (_只能请求时设置for single request_) 21 | * [x] `onProgressUpdate` 下载进度响应 (_只能请求时设置for single request_) 22 | * [x] `onHeadersReceived` 接收头响应 (_只能请求时设置for single request_) 23 | * [x] `timeout` 自定义超时时间ms (_只能请求时设置for single request_) 24 | * [x] `headers` 请求头 25 | * [x] `params` URL参数 26 | * [x] `baseURL` 根URL 27 | * [x] `retry` 重试次数 28 | * [x] `timestamp` 是否记录发送和响应时间戳 29 | * [x] `transformSend` 输入转换函数 30 | * [x] `transformResponse` 输出转换函数 31 | 32 | ### Global Listeners 33 | 34 | * [x] `onSend` (before request data send & after request data transformed) 35 | * [x] `onResponse` (after request response data transformed) 36 | * [x] `onRejected` (before `catch` of Promise) 37 | * [x] `onAbort` 38 | * [x] `onComplete` 39 | 40 | ## Usage 41 | 42 | ### install 43 | 44 | ``` 45 | npm i miniprogram-downloader 46 | ``` 47 | 48 | 49 | ### quick start 50 | 51 | ```js 52 | import {Download} from 'miniprogram-downloder'; 53 | Download.download('item/1.jpg') 54 | .then(applyFunction) // 返回数据 55 | .catch(err=>wx.showToast({title:'下载失败'})); 56 | 57 | Download.download({url:'item/1.jpg'}) 58 | .then(applyFunction) // 返回数据 59 | .catch(err=>wx.showToast({title:'下载失败'})); 60 | ``` 61 | 62 | 63 | ### 直接返回保存位置 64 | 65 | ```js 66 | import {DOWNLOAD,transformDownloadResponseOkData} from 'miniprogram-downloder'; 67 | // 根据状态码,直接返回保存地址 68 | //默认配置全局有效 69 | DOWNLOAD.Defaults.transformResponse=transformDownloadResponseOkData; 70 | 71 | //js 72 | DOWNLOAD.download('item/1.jpg').then(console.log);//打印字符串,保存地址 73 | //TS 74 | DOWNLOAD.download('item/1.jpg') 75 | .then(path=>{ 76 | console.log(path)//path 为保存路径 77 | }) 78 | 79 | 80 | //返回完整数据 对当前下载有效 81 | DOWNLOAD.download(url:'item/1.jpg',null,{transformResponse:(res,o)=>res}) 82 | .then(console.log) //打印 返回的Object 83 | ``` 84 | 85 | 86 | 87 | ### CancelToken (abort) 88 | 89 | 可通过cancel token 方式取消请求 90 | ```js 91 | import { DOWNLOAD, CancelToken } from 'miniprogram-downloader'; 92 | 93 | // 创建一个 tokensource 94 | const source = CancelToken.source(); 95 | 96 | DOWNLOAD.download('items','tempfile' , { 97 | // 配置 cancelToken 98 | cancelToken: source.token 99 | }); 100 | 101 | // 需要取消操作时 102 | source.cancel('cancel the download'); 103 | ``` 104 | -------------------------------------------------------------------------------- /downloader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-downloader", 3 | "version": "5.3.0-alpha.0", 4 | "description": "minprogram download package ", 5 | "keywords": [ 6 | "miniprogram", 7 | "wx.downloadFile", 8 | "小程序", 9 | "下载" 10 | ], 11 | "homepage": "https://miniprogram-network.newfuture.cc/downloader/", 12 | "bugs": { 13 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "NewFuture", 21 | "files": [ 22 | "dist/", 23 | "src/", 24 | "es/" 25 | ], 26 | "main": "dist/index.js", 27 | "module": "es/index.js", 28 | "scripts": { 29 | "build": "npm run build:es5 && npm run build:es6", 30 | "build:es5": "tsc", 31 | "build:es6": "tsc -m es6 --target es6 --outDir es", 32 | "clean": "rimraf -rf es dist types ", 33 | "lint": "tslint -p . -c ../tslint.json", 34 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 35 | "test": "echo \"Error: no test specified\" && exit 1" 36 | }, 37 | "dependencies": { 38 | "miniprogram-network-life-cycle": "^5.3.0-alpha.0", 39 | "miniprogram-network-utils": "^5.3.0-alpha.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /downloader/src/downloader.ts: -------------------------------------------------------------------------------- 1 | import { BaseConfiguration, ExtraConfiguration, LifeCycle, SuccessParam } from 'miniprogram-network-life-cycle'; 2 | import { GeneralCallbackResult, ParamsType } from 'miniprogram-network-utils'; 3 | import { transfomDownloadSendDefault } from './transform'; 4 | 5 | /** 6 | * 默认配置信息 7 | */ 8 | export type DownloadInit = BaseConfiguration, T & wx.DownloadFileOption, TReturn>; 9 | interface BaseDownloadOption { 10 | /** 11 | * 下载地址 12 | * 如果URL以`http://`或者`https://`开头将忽略 baseURL 13 | */ 14 | url: NonNullable; 15 | /** 16 | * 本地路径可缓存 17 | */ 18 | filePath?: string; 19 | } 20 | 21 | /** 22 | * 全部配置信息 23 | */ 24 | export interface FullDownloadOption 25 | extends DownloadInit, ExtraConfiguration, BaseDownloadOption { 26 | /** 27 | * 下载进度回调函数 28 | */ 29 | onProgressUpdate?: wx.DownloadTaskOnProgressUpdateCallback; 30 | } 31 | 32 | /** 33 | * 下载的额外配置 34 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 35 | * @template TExt 额外的扩展属性 36 | */ 37 | type DownloadConfig< 38 | TParams = ParamsType, 39 | TExt extends {} = {}, 40 | TReturn = any, 41 | > = Partial & Partial & ExtraConfiguration> & { 42 | /** 43 | * 路径参数 44 | * URL Path Params 45 | * the path parameters to be replace in path 46 | * Must be a plain `object` or `array` 47 | * @example 48 | * url = "/{ID}/status" 49 | * param = {ID: 12345} 50 | * request url will be /1234/status 51 | */ 52 | params?: TParams; 53 | 54 | /** 55 | * 下载进度回调函数 56 | */ 57 | onProgressUpdate?: wx.DownloadTaskOnProgressUpdateCallback; 58 | }; 59 | 60 | /** 61 | * 下载的全部配置项 62 | * @template TData patch data参数格式类型,默认 any 63 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 64 | */ 65 | export type DownloadOption< 66 | TParams = ParamsType, 67 | TExt extends {} = {}, 68 | TReturn = any, 69 | > 70 | = DownloadConfig & BaseDownloadOption; 71 | 72 | /** 73 | * 下载封装 74 | * @template T 扩展参数类型 75 | */ 76 | export class Downloader 77 | 78 | extends LifeCycle< 79 | T & wx.DownloadFileOption, 80 | wx.DownloadTask, 81 | DownloadInit, 82 | FullDownloadOption 83 | > { 84 | /** 85 | * 新建 Http实列 86 | * @param config 全局默认配置 87 | * @param downloader 下载处理方法,默认使用下载队列处理 88 | * @param listeners 下载事件监听回调 89 | */ 90 | public constructor( 91 | config?: DownloadInit, 92 | downloader?: (o: T & wx.DownloadFileOption) => wx.DownloadTask, 93 | listeners?: Downloader['Listeners'] 94 | ) { 95 | super( 96 | // tslint:disable-next-line: no-use-before-declare 97 | downloader || wx.downloadFile, 98 | // tslint:disable-next-line: no-object-literal-type-assertion 99 | config || { transformSend: transfomDownloadSendDefault } as DownloadInit, 100 | listeners 101 | ); 102 | } 103 | 104 | /** 105 | * 使用自定义参数下载 106 | * @param options - 完整下载参数 107 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 108 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 109 | */ 110 | public download< 111 | TReturn = SuccessParam, 112 | TParams = ParamsType, // 参数类型 113 | >(options: DownloadOption): Promise; 114 | /** 115 | * 快速下载 116 | * @param url 下载地址 117 | * @param filePath 本地文件路径 118 | * @param config 其他参数 119 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 120 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 121 | */ 122 | public download< 123 | TReturn = SuccessParam, 124 | TParams = ParamsType, // 参数类型 125 | >( 126 | url: string, 127 | filePath?: string, 128 | config?: DownloadConfig 129 | ): Promise; 130 | public download(): Promise { 131 | const isMultiParam = typeof arguments[0] === 'string'; 132 | // tslint:disable-next-line: no-unsafe-any 133 | const options: FullDownloadOption = isMultiParam ? (arguments[2] || {}) : arguments[0] as FullDownloadOption; 134 | if (isMultiParam) { 135 | options.url = arguments[0] as string; 136 | options.filePath = arguments[1] as string; 137 | } 138 | return this.process(options); 139 | } 140 | } 141 | 142 | export declare namespace wx { 143 | function downloadFile(options: DownloadFileOption): DownloadTask; 144 | type DownloadTaskOnHeadersReceivedCallback = (result?: { header: object }) => void; 145 | type DownloadTaskOnProgressUpdateCallback = ( 146 | result: { 147 | /** 下载进度百分比 */ 148 | progress: number; 149 | /** 预期需要下载的数据总长度,单位 Bytes */ 150 | totalBytesExpectedToWrite: number; 151 | /** 已经下载的数据长度,单位 Bytes */ 152 | totalBytesWritten: number; 153 | } 154 | ) => void; 155 | 156 | interface DownloadFileOption { 157 | /** 下载资源的 url */ 158 | url: string; 159 | /** 指定文件下载后存储的路径 160 | * 161 | * 最低基础库: `1.8.0` 162 | */ 163 | filePath?: string; 164 | /** HTTP 请求的 Header,Header 中不能设置 Referer */ 165 | header?: object; 166 | /** 接口调用结束的回调函数(调用成功、失败都会执行) */ 167 | complete?(res: { errMsg: string }): void; 168 | /** 接口调用失败的回调函数 */ 169 | fail?(res: { errMsg: string }): void; 170 | /** 接口调用成功的回调函数 */ 171 | success?(result: DownloaderReponse): void; 172 | } 173 | interface DownloadTask { 174 | /** 175 | * 176 | * 中断下载任务 177 | * 178 | * 最低基础库: `1.4.0` 179 | */ 180 | abort(): void; 181 | 182 | onHeadersReceived( 183 | /** HTTP Response Header 事件的回调函数 */ 184 | callback: DownloadTaskOnHeadersReceivedCallback 185 | ): void; 186 | /** [DownloadTask.onProgressUpdate(function callback)](DownloadTask.onProgressUpdate.md) 187 | * 188 | * 监听下载进度变化事件 189 | * 190 | * 最低基础库: `1.4.0` 191 | */ 192 | onProgressUpdate( 193 | /** 下载进度变化事件的回调函数 */ 194 | callback: DownloadTaskOnProgressUpdateCallback 195 | ): void; 196 | } 197 | } 198 | 199 | export interface DownloaderReponse extends GeneralCallbackResult { 200 | /** 开发者服务器返回的 HTTP 状态码 */ 201 | statusCode: number; 202 | /** 临时文件路径。如果没传入 filePath 指定文件存储路径,则下载后的文件会存储到一个临时文件 */ 203 | tempFilePath: string; 204 | } 205 | -------------------------------------------------------------------------------- /downloader/src/index.ts: -------------------------------------------------------------------------------- 1 | export { CancelToken, ICancelTokenSource } from 'miniprogram-network-life-cycle'; 2 | export { Downloader, DownloadInit, DownloadOption, DownloaderReponse } from './downloader'; 3 | export { 4 | DownloadParams, 5 | transfomDownloadSendDefault, 6 | transformDownloadResponseOkData 7 | } from './transform'; 8 | 9 | import { Downloader } from './downloader'; 10 | /** 11 | * 预定义全局Download 12 | */ 13 | // tslint:disable-next-line: export-name 14 | export const DOWNLOAD = /*#__PURE__*/ new Downloader(); 15 | -------------------------------------------------------------------------------- /downloader/src/transform.ts: -------------------------------------------------------------------------------- 1 | import { buildParams, getCommonOptions, Omit } from 'miniprogram-network-utils'; 2 | import { FullDownloadOption, wx } from './downloader'; 3 | 4 | /** 5 | * 微信请求参数 (不包含回调函数) 6 | */ 7 | export type DownloadParams = Omit; 8 | 9 | /** 10 | * 默认下载请求参数构建方法 11 | * @param data - 完整配置参数 12 | */ 13 | export function transfomDownloadSendDefault(data: T & FullDownloadOption): DownloadParams { 14 | return getCommonOptions( 15 | { 16 | url: buildParams(data.url, data.params, data.baseURL), 17 | header: data.headers 18 | }, 19 | data, 20 | ['filePath'] 21 | ); 22 | } 23 | 24 | /** 25 | * 正确返回返回数据处理方式 26 | * @param res - 返回结果 27 | * @param config - 完整参数 28 | */ 29 | export function transformDownloadResponseOkData( 30 | res: Parameters>[0], 31 | options: FullDownloadOption 32 | ) 33 | : string { 34 | if (res.statusCode >= 200 && res.statusCode < 300) { 35 | return res.tempFilePath; 36 | } 37 | throw res; 38 | } 39 | -------------------------------------------------------------------------------- /downloader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015", 7 | "es2015.promise" 8 | ] 9 | }, 10 | "files": [ 11 | "src/index.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /fetch/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-fetch [![npm version](https://badge.fury.io/js/miniprogram-fetch.svg)](https://npmjs.com/package/miniprogram-fetch) 2 | 3 | > MiniProgram Fetch API poly fill 4 | > 在小程序中使用[Fetch API](https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API/Using_Fetch) 5 | 6 | # Feathers: 7 | 8 | * [x] 请求队列管理 9 | * [x] text 10 | * [x] json 11 | * [x] formData 12 | * [ ] arrayBuffer 13 | * [ ] blob 14 | -------------------------------------------------------------------------------- /fetch/fetch.ts: -------------------------------------------------------------------------------- 1 | import { WxQueue } from "miniprogram-queue"; 2 | 3 | /// 4 | 5 | //@ts-ignore 6 | const RequestMQ = new WxQueue(wx.request); 7 | 8 | /** 9 | * fetch 封装 10 | * @see https://developer.mozilla.org/zh-CN/docs/Web/API/Fetch_API 11 | * @param url 请求URL或者Request对象 12 | * @param init 请求参数 13 | */ 14 | export function fetch(url: RequestInfo, init?: RequestInit): Promise { 15 | 16 | init = init || {} 17 | 18 | const param: any = ((typeof url) !== 'object') ? 19 | { 20 | url, 21 | data: init.body, 22 | header: init.headers || {}, 23 | method: init.method, 24 | dataType: 'text' 25 | } : { 26 | url: (url as Request).url, 27 | data: (url as Request).json() || (url as Request).text() || (url as Request).body, 28 | method: init.method || (url as Request).method, 29 | header: Object.assign({}, (url as Request).headers, init.headers), 30 | dataType: 'text', 31 | 32 | }; 33 | 34 | return new Promise((resolve, reject) => { 35 | param.success = function (res: any) { 36 | var response = new wxHttpResponse(res); 37 | resolve(response); 38 | }; 39 | param.fail = reject; 40 | param.complete = init['complete']; 41 | RequestMQ.push(param); 42 | }) 43 | } 44 | 45 | 46 | class wxHttpResponse implements Response { 47 | headers: Headers; 48 | ok: boolean; 49 | redirected: boolean; 50 | status: number; 51 | statusText: string; 52 | trailer: Promise; 53 | type: ResponseType; 54 | url: string; 55 | body: ReadableStream | any; 56 | bodyUsed: boolean; 57 | constructor(res: { data: any, statusCode: number, header: Headers }) { 58 | this.status = res.statusCode; 59 | this.headers = res.header; 60 | this.body = res.data; 61 | this.ok = res.statusCode >= 200 && res.statusCode < 300; 62 | this.bodyUsed = false 63 | } 64 | /** 65 | * Copy 66 | */ 67 | clone(): Response { 68 | return Object.assign(this); 69 | } 70 | arrayBuffer(): Promise { 71 | throw new Error("Method not implemented."); 72 | } 73 | blob(): Promise { 74 | throw new Error("Method not implemented."); 75 | } 76 | formData(): Promise { 77 | return new Promise((resolve, reject) => { 78 | let data = this.body; 79 | try { 80 | if (typeof data === "string") { 81 | data = JSON.parse(data); 82 | } 83 | const formData = new FormData(); 84 | for (let key in data) { 85 | formData.append(key, data[key]); 86 | } 87 | resolve(formData); 88 | this.bodyUsed = true; 89 | } catch (error) { 90 | reject(error); 91 | } 92 | }); 93 | } 94 | /** 95 | * to json as promise 96 | */ 97 | json(): Promise { 98 | return new Promise((resolve, reject) => { 99 | try { 100 | resolve(JSON.parse(this.body)); 101 | this.bodyUsed = true; 102 | } catch (error) { 103 | reject(error); 104 | } 105 | }) 106 | } 107 | /** 108 | * to string as promise 109 | */ 110 | text(): Promise { 111 | return new Promise((resolve, reject) => { 112 | try { 113 | resolve(this.body); 114 | this.bodyUsed = true; 115 | } catch (error) { 116 | reject(error); 117 | } 118 | }) 119 | } 120 | } -------------------------------------------------------------------------------- /fetch/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-fetch", 3 | "version": "4.5.1", 4 | "description": "Fetch API polyfill for Wechat MiniProgram", 5 | "keywords": [ 6 | "fetch", 7 | "miniprogram", 8 | "promise", 9 | "request" 10 | ], 11 | "license": "Apache-2.0", 12 | "author": "NewFuture", 13 | "main": "dist/fetch.js", 14 | "module": "es/fetch.js", 15 | "typings": "dist/fetch.d.ts", 16 | "scripts": { 17 | "build": "tsc fetch.ts --moduleResolution node --target es6 --outDir dist -d", 18 | "build:es5": "tsc fetch.ts --moduleResolution node --target es5 --outDir dist -d", 19 | "build:es6": "tsc fetch.ts fetch.ts --moduleResolution node -m es6 --target es6 --outDir es" 20 | }, 21 | "dependencies": { 22 | "miniprogram-queue": "^4.5.1" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /lerna.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": [ 3 | "cache", 4 | "cancel-token", 5 | "downloader", 6 | "life-cycle", 7 | "network", 8 | "network-utils", 9 | "request", 10 | "uploader" 11 | ], 12 | "command": { 13 | "bootstrap": { 14 | "npmClientArgs": [ 15 | "--no-package-lock" 16 | ] 17 | }, 18 | "version": { 19 | "allowBranch": "master" 20 | }, 21 | "publish": { 22 | "message": "chore(release): publish %v" 23 | } 24 | }, 25 | "version": "5.3.0", 26 | "changelog": { 27 | "cacheDir": ".changelog" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /life-cycle/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-network-life-cycle [![npm version](https://badge.fury.io/js/miniprogram-network-life-cycle.svg)](https://npmjs.com/package/miniprogram-network-life-cycle) 2 | 3 | ## 网络请求生命周期 4 | 5 | [miniprogram-network](https://miniprogram-network.newfuture.cc) 底层数据处理流程 6 | 7 | * [x] Promise 8 | * [x] Cancelable 9 | * [x] Interceptors in Lifecycle (only one,modify data or status) 10 | * [x] transform request data 11 | * [x] transform response data 12 | * [x] Global Listeners 13 | * [x] On Send (before request data send & after request data transformed) 14 | * [x] On Response (after request response data transformed) 15 | * [x] On rejected (when `catch` of Promise) 16 | * [x] On abort 17 | * [x] On complete 18 | * [ ] ~~On resolved? (before `then` of Promise)~~ 19 | 20 | ## API Properties 21 | 22 | * `Defaults` 全局配置项目 23 | * `transformSend(options: Exclude) => wx.Options | Promise` 发送前转换参数 24 | * `transformResponse(res: wx.SuccessCallbackResult, config: TFullOptions) => any | Promise` 数据返回转换参数 25 | * `Listeners` 全局事件监听列表 26 | * `[onSend(options: RequestOptions) => any]`; 发送前事件监听列表 27 | * `[onResponse(res: wx.RequestSuccessCallbackResult, options: RequestOptions) => any]`; 收到服务器响应事件监听列表 28 | * `[onComplete(res: wx.GeneralCallbackResult, options: RequestOptions) => any]`;每个操作完成事件响应 29 | * `[onReject(res: any | wx.GeneralCallbackResult, options: RequestOptions) => any]`;操作最终失败响应事件 30 | * `[onAbort(reason: any, options: RequestOptions) => any]`;取消操作响应事件 31 | * `handle` 底层处理函数 32 | 33 | 34 | 35 | ## LifeCycle 36 | 37 | ![Request Life Cycle](https://user-images.githubusercontent.com/6290356/49631309-6bddc080-fa2c-11e8-9a41-88fb50b2a1b7.png) 38 | 39 | -------------------------------------------------------------------------------- /life-cycle/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-network-life-cycle", 3 | "version": "5.3.0-alpha.0", 4 | "description": "miniprogram-network lifecycle", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 8 | }, 9 | "homepage": "https://miniprogram-network.newfuture.cc/life-cycle/", 10 | "license": "Apache-2.0", 11 | "author": "New Future", 12 | "files": [ 13 | "dist/", 14 | "src/", 15 | "es/" 16 | ], 17 | "main": "dist/index.js", 18 | "module": "es/index.js", 19 | "scripts": { 20 | "build": "npm run build:es5 && npm run build:es6", 21 | "build:es5": "tsc", 22 | "build:es6": "tsc -m es6 --target es6 --outDir es", 23 | "clean": "rimraf -rf es dist types ", 24 | "lint": "tslint -p . -c ../tslint.json", 25 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 26 | "test": "echo \"Error: no test specified\" && exit 1" 27 | }, 28 | "dependencies": { 29 | "miniprogram-cancel-token": "^5.1.2", 30 | "miniprogram-network-utils": "^5.3.0-alpha.0" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /life-cycle/src/configuration.ts: -------------------------------------------------------------------------------- 1 | import { CancelToken } from 'miniprogram-cancel-token'; 2 | import { GeneralCallbackResult, Omit, ParamsType } from 'miniprogram-network-utils'; 3 | 4 | type KeyBasicValuePair = Record; 5 | type PromiseOrValue = T | PromiseLike; 6 | 7 | export interface WxTask { 8 | abort(): void; 9 | /** HTTP Response Header 事件的回调函数 */ 10 | onHeadersReceived(callback: ExtraConfiguration['onHeadersReceived']): void; 11 | /** 下载进度变化事件的回调函数 */ 12 | onProgressUpdate?(callback: ExtraConfiguration['onProgressUpdate']): void; 13 | } 14 | 15 | export interface WxOptions { 16 | /** 开发者服务器接口地址 */ 17 | url: string; 18 | /** http headers */ 19 | header?: object; 20 | /** 接口调用结束的回调函数(调用成功、失败都会执行) */ 21 | complete?: Function; 22 | /** 接口调用失败的回调函数 */ 23 | fail?: Function; 24 | /** 接口调用成功的回调函数 */ 25 | success?(res: any): any; 26 | } 27 | 28 | export type SuccessParam = Parameters>[0]; 29 | /** 30 | * 所有网络请求的集成类型,可全局配置 31 | */ 32 | export interface BaseConfiguration< 33 | TFullOptions extends BaseConfiguration, //完整配置 34 | TWxOptions extends WxOptions, // 微信请求参数类型 35 | TRetrun = any, 36 | > { 37 | /** 38 | * 请求的根目录 39 | * The root URL for request 40 | * 如果URL以`http://`或者`https://`开头将被忽略 41 | */ 42 | baseURL?: string; 43 | 44 | /** 45 | * 自定义请求头 46 | * user defined HTTP headers 47 | */ 48 | headers?: KeyBasicValuePair; 49 | 50 | /** 51 | * 路径参数 52 | * URL Path Params 53 | * the path parameters to be replace in path 54 | * Must be a plain `object` or `array` 55 | * @example 56 | * url = "/{ID}/status" 57 | * param = {ID: 12345} 58 | * request url will be /1234/status 59 | */ 60 | params?: ParamsType; 61 | 62 | /** 63 | * 重试`次数`或重试`回调函数` 64 | * 处理函数(`this`指向整个参数),参数为`发送完整数据`,`失败原因`,返回`重试数据`支持异步操作(返回Promise) 65 | * retry times or retry callback when fail 66 | * sync or asynchronous 67 | */ 68 | retry?: number | ( 69 | /** 70 | * 自定义重试函数 71 | * @this TFullOptions 可访问整个Options对象 72 | * @param data request/downloadFile等完整参数 73 | * @param reason 本次请求发送失败的原因 74 | * @returns 返回新的request/downloadFile数据 75 | */ 76 | (this: TFullOptions, data: TWxOptions, reason?: GeneralCallbackResult) => PromiseOrValue 77 | ); 78 | 79 | /** 80 | * 是否记录时间戳 81 | * 如果传入为object 则记录时间戳于此object中 82 | */ 83 | timestamp?: boolean | { send?: number; response?: number }; 84 | 85 | /** 86 | * 禁用在线检查功能,离线状态也强制发送请求 87 | * 默认,会检查在线状态,离线或者后台时暂停发送 88 | */ 89 | disableOnline?: boolean; 90 | 91 | /** 92 | * 请求参数预处理 93 | * 修改数据或者头;返回 wx.request, wx.downloadFile,wx.uploadFile参数 (不包括回调函数) 94 | * 支持异步返回promise 95 | * You can modify the data or headers object before it is sent. 96 | * @param options 不包含转换函数的所有配置内容 97 | * @returns the params to call wechat API without callback functions. 98 | */ 99 | transformSend(options: TFullOptions): 100 | PromiseOrValue>; 101 | 102 | /** 103 | * 返回数据修改,返回值作为then的输入, throw exception 抛给catch 104 | * 异步返回Promise 105 | * allows changes to the response data to be made before it is passed to then/catch 106 | * @example `res=>res.data` 107 | * @param res wechat API result 108 | * @param options full options of the API 109 | * @return the data to resolve 110 | */ 111 | transformResponse?(res: SuccessParam, options: TFullOptions): PromiseOrValue; 112 | } 113 | 114 | /** 115 | * 每个操包含的额外配置参数 116 | */ 117 | export interface ExtraConfiguration { 118 | /** 119 | * 取消操作的 CancelToken 120 | * `CancelToken.source()`可生成tokenSource 121 | */ 122 | cancelToken?: CancelToken; 123 | 124 | /** 125 | * 自定义超时时间,单位`ms` 126 | * 取值`>0` 时有有效 127 | */ 128 | timeout?: number; 129 | 130 | /** 131 | * 进度回调 132 | */ 133 | onProgressUpdate?: Function; 134 | 135 | /** 136 | * 接收到响应头回调 137 | */ 138 | onHeadersReceived?(result: { header: object }): void; 139 | } 140 | 141 | /** 142 | * 合并配置 143 | * @param customize 自定义配置,未定义的将被设置为默认值 144 | * @param defaults 默认值 145 | */ 146 | export function mergeConfig, T2 extends { [key: string]: any }>( 147 | customize: T1, 148 | defaults: T2 149 | ): T1 { 150 | const config = { ...defaults, ...customize }; 151 | if (defaults.headers && customize.headers) { 152 | // 合并headers 153 | (config as any as { headers: object }).headers = { ...defaults.headers, ...customize.headers }; 154 | } 155 | return config; 156 | } 157 | -------------------------------------------------------------------------------- /life-cycle/src/ensure-online.ts: -------------------------------------------------------------------------------- 1 | import { CancelToken } from 'miniprogram-cancel-token'; 2 | // tslint:disable: no-use-before-declare 3 | 4 | /** 5 | * 网络是否联接 6 | */ 7 | let isConnected: boolean = false; 8 | /** 9 | * 是否隐藏在后台 10 | */ 11 | let isHidden: boolean = false; 12 | 13 | const callbackPools: (() => void)[] = []; 14 | 15 | function checkCallbacks() { 16 | if (isConnected && !isHidden) { 17 | while (callbackPools.length > 0) { 18 | callbackPools.shift()!(); 19 | } 20 | } 21 | } 22 | wx.onAppHide(() => { isHidden = true; }); 23 | wx.onAppShow(() => { 24 | isHidden = false; 25 | checkCallbacks(); 26 | }); 27 | wx.onNetworkStatusChange((res) => { 28 | isConnected = res.isConnected; 29 | checkCallbacks(); 30 | }); 31 | wx.getNetworkType({ 32 | success(res) { 33 | isConnected = res.networkType !== 'none'; 34 | checkCallbacks(); 35 | } 36 | }); 37 | 38 | /** 39 | * 确保在线时执行 40 | * 网络掉线或者切换到后台的情况暂停发送 41 | * @param callback 回调 42 | * @param cancelToken 取消操作 43 | */ 44 | export function ensureOnline(callback: () => void, cancelToken?: CancelToken): void { 45 | if (isConnected && !isHidden) { 46 | callback(); 47 | } else { 48 | callbackPools.push(callback); 49 | if (cancelToken) { 50 | // tslint:disable-next-line: no-floating-promises 51 | cancelToken.promise.then(() => { 52 | const index = callbackPools.indexOf(callback); 53 | if (index !== -1) { 54 | callbackPools.splice(index, 1); 55 | } 56 | }); 57 | } 58 | } 59 | } 60 | 61 | declare namespace wx { 62 | interface OnNetworkStatusChangeCallbackResult { 63 | /** 当前是否有网络连接 */ 64 | isConnected: boolean; 65 | /** 网络类型 66 | * 67 | * 可选值: 68 | * - 'wifi': wifi 网络; 69 | * - '2g': 2g 网络; 70 | * - '3g': 3g 网络; 71 | * - '4g': 4g 网络; 72 | * - 'unknown': Android 下不常见的网络类型; 73 | * - 'none': 无网络; 74 | */ 75 | networkType: 'wifi' | '2g' | '3g' | '4g' | 'unknown' | 'none'; 76 | } 77 | 78 | function getNetworkType(option?: { success?(result: OnNetworkStatusChangeCallbackResult): void }): void; 79 | /** 网络状态变化事件的回调函数 */ 80 | function onNetworkStatusChange(callback: (result: OnNetworkStatusChangeCallbackResult) => void): void; 81 | /** 小程序切后台事件的回调函数 */ 82 | function onAppHide(callback: () => void): void; 83 | /** 小程序切前台事件的回调函数 */ 84 | function onAppShow(callback: () => void): void; 85 | } 86 | -------------------------------------------------------------------------------- /life-cycle/src/index.ts: -------------------------------------------------------------------------------- 1 | export { 2 | BaseConfiguration, 3 | ExtraConfiguration, 4 | SuccessParam 5 | } from './configuration'; 6 | export { Listeners } from './listeners'; 7 | export { LifeCycle } from './life-cycle'; 8 | export { CancelToken, ICancelTokenSource } from 'miniprogram-cancel-token'; 9 | -------------------------------------------------------------------------------- /life-cycle/src/life-cycle.ts: -------------------------------------------------------------------------------- 1 | import { GeneralCallbackResult, Omit } from 'miniprogram-network-utils'; 2 | import { 3 | BaseConfiguration, 4 | ExtraConfiguration, 5 | mergeConfig, 6 | SuccessParam, 7 | WxOptions, 8 | WxTask 9 | } from './configuration'; 10 | import { ensureOnline } from './ensure-online'; 11 | import { Listeners } from './listeners'; 12 | 13 | /** 14 | * 在结果中主人timeout 标记 15 | * @param res 原始结果 16 | */ 17 | function timeoutMsg(res: GeneralCallbackResult, time?: number) { 18 | res.errMsg = res.errMsg ? res.errMsg.replace(':fail abort', `:fail timeout ${time}`) : `network:fail timeout ${time}`; 19 | res.timeout = true; 20 | return res; 21 | } 22 | 23 | /** 24 | * 网络请求的完整生命周期 25 | * @template TWxOptions 微信操作函数参数类型 // 微信操作函数 26 | * @template TWxTask 微信操作函数返回值类型 // 微信操作的任务类型 27 | * @template TInitConfig LifeCycle的初始默认配置项(Defaults)类型 //初始化配置项 28 | * @template TFullOptions 一个操作完整配置项(全部可接收参数)类型 29 | */ 30 | export abstract class LifeCycle< 31 | TWxOptions extends WxOptions, 32 | TWxTask extends WxTask, 33 | TInitConfig extends BaseConfiguration, 34 | TFullOptions extends TInitConfig & ExtraConfiguration = TInitConfig & ExtraConfiguration, //完整配置项 35 | > { 36 | /** 37 | * 默认全局配置 38 | * 对此实例的每个请求生效,优先级低于每个请求的自定义配置 39 | * 除`header`属性会将请求自定义配置和默认配置进行合并 40 | * 其余设置会直接覆盖 41 | */ 42 | // tslint:disable-next-line:variable-name 43 | public readonly Defaults: TInitConfig; 44 | 45 | /** 46 | * 全局事件监听列表 Listeners 47 | * 每次触发特定类型事件会通知对应的全部listeners 48 | * 包括 `onResponse`,`onResponse`,`onComplete`,`onRejected`,`onAbort` 49 | * 原则上不应该在事件回调中修改数据 50 | */ 51 | // tslint:disable-next-line:variable-name 52 | public readonly Listeners: Readonly>>; 53 | 54 | /** 55 | * 微信操作接口 56 | * @param option 参数 57 | */ 58 | public readonly handle: (option: TWxOptions) => TWxTask; 59 | 60 | /** 61 | * 新建实列 62 | * @param operator 操作 63 | * @param config 全局默认配置 64 | * @param listeners 事件监听 65 | */ 66 | protected constructor( 67 | operator: (option: TWxOptions) => TWxTask, 68 | config: TInitConfig, 69 | listeners: Readonly>> = new Listeners() 70 | ) { 71 | this.handle = operator; 72 | this.Defaults = config; 73 | this.Listeners = listeners; 74 | if (config.retry === undefined) { 75 | this.Defaults.retry = 1; 76 | } 77 | if (!config.headers) { 78 | this.Defaults.headers = {}; 79 | } 80 | } 81 | 82 | /** 83 | * 处理请求 84 | * @param options - 请求参数,不包括默认参数 85 | */ 86 | protected process>(options: TFullOptions): Promise { 87 | // tslint:disable-next-line: no-parameter-reassignment 88 | options = mergeConfig(options, this.Defaults); 89 | return this._onSend(options) 90 | .then((param) => { 91 | // 记录发送时间戳 92 | if (options.timestamp) { 93 | if (typeof options.timestamp === 'object') { 94 | // 记录于传入的参数中 95 | options.timestamp.send = Date.now(); 96 | } else { 97 | (options as any).__sendTime = Date.now(); 98 | } 99 | } 100 | return this._send(param as TWxOptions, options); 101 | }); 102 | } 103 | 104 | /** 105 | * 请求发送之前处理数据 106 | * @param options - 完整参数 107 | */ 108 | private _onSend(options: TFullOptions): Promise> { 109 | this.Listeners.onSend.forEach(f => { f(options); }); 110 | return Promise.resolve(options) 111 | .then(options.transformSend); 112 | } 113 | 114 | /** 115 | * 发送网络请求,并自动重试 116 | * @param data - 发送微信参数 117 | * @param options - 全部配置 118 | */ 119 | private _send(data: TWxOptions, options: TFullOptions): Promise { 120 | return new Promise((resolve, reject) => { 121 | /** 122 | * 是否结束 123 | */ 124 | let completed = false; 125 | /** 126 | * 超时定时器 127 | * * undefined 表示未启用 128 | * * 0 表示已经触发超时 129 | * * 正数 表示真在计时中(未超时) 130 | */ 131 | let timeoutHandle: number | undefined; 132 | const cancelToken = options.cancelToken; 133 | if (cancelToken) { 134 | cancelToken.throwIfRequested(); 135 | } 136 | data.success = (res: SuccessParam) => { 137 | completed = true; 138 | this._response(res, options) 139 | .then(resolve, reject); 140 | }; 141 | // retry on fail 142 | data.fail = (res: GeneralCallbackResult): void => { 143 | if (timeoutHandle === 0) { 144 | timeoutMsg(res, options.timeout); // 触发自定义超时,注入timeout 145 | } 146 | 147 | if (cancelToken && cancelToken.isCancelled()) { 148 | // 用户主动取消,直接结束不再重试 149 | res.cancel = true; 150 | } else if (typeof options.retry === 'function') { 151 | // 自定义retry 函数 152 | Promise.resolve() 153 | .then(() => (options.retry! as Function)(data, res)) 154 | .then( 155 | // 继续重试 156 | (retryData: TWxOptions) => { 157 | this._send(retryData, options) 158 | .then(resolve, reject); 159 | }, 160 | // 放弃重试 161 | (reason: GeneralCallbackResult) => { 162 | this._onFail(reason, options) 163 | .then(reject, reject); 164 | this._complete(reason, options); 165 | } 166 | ); 167 | return; 168 | } else if ((options.retry as number)-- > 0) { 169 | // 还有重试次数 170 | this._send(data, options) 171 | .then(resolve, reject); 172 | return; 173 | } 174 | // 结束请求 175 | completed = true; 176 | this._onFail(res, options) 177 | .then(reject, reject); 178 | }; 179 | data.complete = (res: GeneralCallbackResult & ExtraCompleteRes) => { 180 | if (timeoutHandle) { 181 | // 清理计时器 182 | clearTimeout(timeoutHandle); 183 | timeoutHandle = undefined; // 置空 184 | } else if (timeoutHandle === 0 && !res.timeout) { 185 | // 触发过自定义超时,并且尚未注入timeout 186 | timeoutMsg(res, options.timeout); 187 | } 188 | if (completed) { 189 | // 结束 190 | this._complete(res, options); 191 | } 192 | }; 193 | const run = () => { 194 | const task = this.handle(data); 195 | if (options.timeout! > 0) { 196 | // 计时器 自定义超时 197 | // 超时触发 计时器标志置0, 终止操作 198 | timeoutHandle = setTimeout(() => { timeoutHandle = 0; task.abort(); }, options.timeout!); 199 | } 200 | if (options.onHeadersReceived) { 201 | task.onHeadersReceived(options.onHeadersReceived); // 响应头回调 202 | } 203 | if (options.onProgressUpdate && task.onProgressUpdate) { 204 | task.onProgressUpdate(options.onProgressUpdate); // 进度回调 205 | } 206 | if (cancelToken) { 207 | cancelToken.promise 208 | .then(reason => { task.abort(); this._onAbort(reason, options); }, reject); 209 | } 210 | }; 211 | if (options.disableOnline) { 212 | run(); 213 | } else { 214 | ensureOnline(run, cancelToken); 215 | } 216 | }); 217 | } 218 | 219 | /** 220 | * 处理服务器返回数据 221 | * @param res - 返回参数 222 | * @param options - 全部配置 223 | */ 224 | private _response(res: SuccessParam, options: TFullOptions): Promise { 225 | this.Listeners.onResponse.forEach(f => { f(res, options); }); 226 | if (options.transformResponse) { 227 | return Promise 228 | .resolve(res) 229 | .then( 230 | // tslint:disable-next-line: no-unsafe-any 231 | (result) => options.transformResponse!(result, options) 232 | ) 233 | .catch((reason: GeneralCallbackResult) => this._onFail(reason, options)); 234 | } else { 235 | return Promise.resolve(res); 236 | } 237 | } 238 | 239 | /** 240 | * complete 结束操作 按需注入时间 241 | * @param res - result 242 | * @param options - all options 243 | */ 244 | private _complete(res: GeneralCallbackResult & ExtraCompleteRes, options: TFullOptions): void { 245 | if (options.timestamp) { 246 | //记录时间戳 247 | if (typeof options.timestamp === 'object') { 248 | options.timestamp.response = Date.now(); 249 | res.time = options.timestamp; 250 | } else { 251 | res.time = { 252 | send: (options as any).__sendTime as number, 253 | response: Date.now() 254 | }; 255 | } 256 | } 257 | this._onComplete(res as any, options); 258 | 259 | } 260 | /** 261 | * 请求发送失败 262 | * @param res - 返回参数 263 | * @param options - 全部配置 264 | */ 265 | private _onFail(res: GeneralCallbackResult, options: TFullOptions): Promise { 266 | this.Listeners.onRejected.forEach(f => { f(res, options); }); 267 | return Promise.reject(res); 268 | } 269 | 270 | /** 271 | * 请求完成 272 | * @param res - 返回参数 273 | * @param options - 全部配置 274 | */ 275 | private _onComplete(res: Partial> & GeneralCallbackResult & ExtraCompleteRes, options: TFullOptions) { 276 | this.Listeners.onComplete.forEach(f => { f(res as any, options); }); 277 | } 278 | 279 | /** 280 | * 请求完成 281 | * @param res - 返回参数 282 | * @param options - 全部配置 283 | */ 284 | private _onAbort(reason: any, options: TFullOptions): void { 285 | // tslint:disable-next-line: no-unsafe-any 286 | this.Listeners.onAbort.forEach(f => { f(reason, options); }); 287 | } 288 | } 289 | 290 | interface TimeRecorder { 291 | send?: number; 292 | response?: number; 293 | } 294 | 295 | interface ExtraCompleteRes { 296 | /** 297 | * 请求时间戳 298 | */ 299 | time?: TimeRecorder; 300 | } 301 | 302 | /** 303 | * 取消由 setTimeout 设置的定时器。 304 | * @param timeoutID - 要取消的定时器的 305 | */ 306 | declare function clearTimeout( 307 | timeoutID: number 308 | ): void; 309 | 310 | /** 311 | * 设定一个定时器。在定时到期以后执行注册的回调函数 312 | * @param callback - 回调操作 313 | * @param delay - 延迟的时间,函数的调用会在该延迟之后发生,单位 ms。 314 | * @param rest - param1, param2, ..., paramN 等附加参数,它们会作为参数传递给回调函数。 315 | */ 316 | declare function setTimeout( 317 | callback: Function, 318 | delay?: number, 319 | rest?: any 320 | ): number; 321 | -------------------------------------------------------------------------------- /life-cycle/src/listeners.ts: -------------------------------------------------------------------------------- 1 | import { GeneralCallbackResult } from 'miniprogram-network-utils'; 2 | /** 3 | * 监听事件列表 4 | */ 5 | export class Listeners { 6 | /** 7 | * 发送之前事件监听列表 8 | * 回调函数参数为`完整配置`(只读,不应修改) 9 | */ 10 | public onSend: OnSendListener[] = []; 11 | /** 12 | * 收到数据响应后事件监听列表 13 | * 回调函数参数为`返回结果`和`完整配置`(只读,不应修改) 14 | */ 15 | public onResponse: OnResponseListener[] = []; 16 | /** 17 | * 请求完成时事件监听列表 18 | * 回调函数参数为`操作结果`和`完整配置`(只读,不应修改) 19 | */ 20 | public onComplete: OnCompleteListener[] = []; 21 | /** 22 | * 处理失败事件监听列表 23 | * 回调函数参数为`失败原因`和`完整配置`(只读,不应修改) 24 | */ 25 | public onRejected: OnRejectListener[] = []; 26 | /** 27 | * 请求取消事件监听列表 28 | * 回调函数参数为`取消原因`和`完整配置`(只读,不应修改) 29 | */ 30 | public onAbort: OnAbortListener[] = []; 31 | } 32 | 33 | /** 34 | * 发送前监听 35 | */ 36 | type OnSendListener = (options: Readonly) => any; 37 | /** 38 | * 发送时监听 39 | */ 40 | type OnResponseListener = (res: Readonly, options: Readonly) => any; 41 | /** 42 | * 操作完成时 43 | */ 44 | type OnCompleteListener = ( 45 | res: Readonly & CommonCompleteResult>, 46 | options: Readonly 47 | ) => any; 48 | /** 49 | * 失败 50 | */ 51 | type OnRejectListener = (res: Readonly<{ errMsg: string } | any>, options: Readonly) => any; 52 | /** 53 | * 操作取消时 54 | */ 55 | type OnAbortListener = (reason: Readonly, options: Readonly) => any; 56 | 57 | interface CommonCompleteResult extends GeneralCallbackResult { 58 | /** 59 | * 时间戳记录, 通过发送并且timestamp设置为true 60 | */ 61 | time?: { 62 | /** 63 | * 发送时间戳 64 | */ 65 | send: number; 66 | /** 67 | * 结束时间戳 68 | */ 69 | response: number; 70 | }; 71 | } 72 | -------------------------------------------------------------------------------- /life-cycle/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015" 7 | ] 8 | }, 9 | "files": [ 10 | "src/index.ts" 11 | ] 12 | } -------------------------------------------------------------------------------- /network-utils/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-network-utils [![npm version](https://badge.fury.io/js/miniprogram-network-utils.svg)](https://npmjs.com/package/miniprogram-network-utils) 2 | 3 | 网络库底层公用函数封装 -------------------------------------------------------------------------------- /network-utils/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-network-utils", 3 | "version": "5.3.0-alpha.0", 4 | "description": "", 5 | "repository": { 6 | "type": "git", 7 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 8 | }, 9 | "license": "Apache-2.0", 10 | "author": "New Future", 11 | "files": [ 12 | "dist/", 13 | "src/", 14 | "es/" 15 | ], 16 | "main": "dist/index.js", 17 | "module": "es/index.js", 18 | "scripts": { 19 | "build": "npm run build:es5 && npm run build:es6", 20 | "build:es5": "tsc", 21 | "build:es6": "tsc -m es6 --target es6 --outDir es", 22 | "clean": "rimraf -rf es dist types", 23 | "lint": "tslint -p . -c ../tslint.json", 24 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 25 | "test": "echo \"Error: no test specified\" && exit 1" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /network-utils/src/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | // tslint:disable-next-line:no-import-side-effect 4 | import './promise.finally'; 5 | export type Omit = Pick>; 6 | interface ParamObject { 7 | [key: string]: string | number | boolean | null | undefined; 8 | [key: number]: string | number | boolean | null | undefined; 9 | } 10 | 11 | /** 12 | * 参数类型 13 | */ 14 | export type ParamsType = 15 | | ParamObject 16 | | ((string | number | boolean)[]); 17 | 18 | /** 19 | * 构建url参数 20 | * /users/{id} ==> /users/123 21 | * @param url - url 相对地址或者绝对地址 22 | * @param params - Obejct 键值对 替换的参数列表 23 | * @param baseUrl - 根目录,当url以https://或者http://开头忽略此参数 24 | * @returns 完整参数URL 25 | */ 26 | export function buildParams( 27 | url: string, 28 | params?: ParamsType, 29 | baseUrl?: string 30 | ): string { 31 | if (url && params) { 32 | Object.keys(params) 33 | .forEach((key) => { 34 | // tslint:disable-next-line:no-parameter-reassignment prefer-type-cast 35 | url = url.replace(new RegExp(`{${key}}`, 'g'), (params as ParamObject)[key] as string); 36 | }); 37 | } 38 | // tslint:disable-next-line:no-http-string 39 | if (url && (url.startsWith('https://') || url.startsWith('http://'))) { 40 | return url; 41 | } else { 42 | return (baseUrl || '') + url; 43 | } 44 | } 45 | 46 | /** 47 | * 合并公共配置 48 | * @param data - new configuration for wechat operation 49 | * @param options - default global configuration 50 | * @param extendKeys - key need copy to data 51 | */ 52 | export function getCommonOptions( 53 | data: T, 54 | options: { [key: string]: any }, 55 | extendKeys: (keyof T)[] 56 | ): T { 57 | (['expire', ...extendKeys] as (keyof typeof options)[]).forEach((v) => { 58 | if (options[v] !== undefined) { 59 | // tslint:disable-next-line: no-unsafe-any 60 | data[v as keyof T] = options[v]; 61 | } 62 | }); 63 | 64 | return data; 65 | } 66 | 67 | export interface GeneralCallbackResult { 68 | /** 69 | * 微信回调消息 70 | */ 71 | errMsg: string; 72 | 73 | /** 74 | * 网络请求过程中一些调试信息 75 | */ 76 | profile?: Profile; 77 | 78 | /** 79 | * 是否触发了自定义超时 80 | */ 81 | timeout?: boolean; 82 | 83 | /** 84 | * 是否是主动取消 85 | */ 86 | cancel?: boolean; 87 | 88 | /** 89 | * 触发来源 90 | */ 91 | source?: string; 92 | } 93 | 94 | /** 95 | * https://developers.weixin.qq.com/miniprogram/dev/framework/performance/network.html 96 | */ 97 | export interface Profile { 98 | /** 99 | * 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0 100 | */ 101 | redirectStart: number; 102 | /** 103 | * 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0 104 | */ 105 | redirectEnd: number; 106 | /** 107 | * 组件准备好使用 HTTP 请求抓取资源的时间,这发生在检查本地缓存之前 108 | */ 109 | fetchStart: number; 110 | /** 111 | * DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 112 | */ 113 | domainLookupStart: number; 114 | /** 115 | * DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等 116 | */ 117 | domainLookupEnd: number; 118 | /** 119 | * HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间 120 | */ 121 | connectStart: number; 122 | /** 123 | * HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等。注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间。注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过 124 | */ 125 | connectEnd: number; 126 | /** 127 | * SSL建立连接的时间,如果不是安全连接,则值为 0 128 | */ 129 | SSLconnectionStart: number; 130 | /** 131 | * SSL建立完成的时间,如果不是安全连接,则值为 0 132 | */ 133 | SSLconnectionEnd: number; 134 | /** 135 | * HTTP请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存。连接错误重连时,这里显示的也是新建立连接的时间 136 | */ 137 | requestStart: number; 138 | /** 139 | * HTTP请求读取真实文档结束的时间 140 | */ 141 | requestEnd: number; 142 | /** 143 | * HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存 144 | */ 145 | responseStart: number; 146 | /** 147 | * HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存 148 | */ 149 | responseEnd: number; 150 | /** 151 | * 当次请求连接过程中实时 rtt 152 | */ 153 | rtt: number; 154 | /** 155 | * 评估的网络状态 unknown, offline, slow 2g, 2g, 3g, 4g, last/0, 1, 2, 3, 4, 5, 6 156 | */ 157 | estimate_nettype: number; 158 | /** 159 | * 协议层根据多个请求评估当前网络的 rtt(仅供参考) 160 | */ 161 | httpRttEstimate: number; 162 | /** 163 | * 传输层根据多个请求评估的当前网络的 rtt(仅供参考) 164 | */ 165 | transportRttEstimate: number; 166 | /** 167 | * 评估当前网络下载的kbps 168 | */ 169 | downstreamThroughputKbpsEstimate: number; 170 | /** 171 | * 当前网络的实际下载kbps 172 | */ 173 | throughputKbps: number; 174 | /** 175 | * 当前请求的IP 176 | */ 177 | peerIP: string; 178 | /** 179 | * 当前请求的端口 180 | */ 181 | port: number; 182 | /** 183 | * 是否复用连接 184 | */ 185 | socketReused: boolean; 186 | /** 187 | * 发送的字节数 188 | */ 189 | sendBytesCount: number; 190 | /** 191 | * 收到字节数 192 | */ 193 | receivedBytedCount: number; 194 | /** 195 | * 使用协议类型,有效值:http1.1, h2, quic, unknown 196 | */ 197 | protocol: string; 198 | } 199 | -------------------------------------------------------------------------------- /network-utils/src/promise.finally.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | interface Promise { 4 | // tslint:disable-next-line: no-reserved-keywords 5 | readonly constructor: PromiseConstructor; 6 | } 7 | 8 | if (!Promise.prototype.finally) { 9 | Promise.prototype.finally = function (this: Promise, onfinally?: (() => void) | undefined | null): Promise { 10 | if (onfinally) { 11 | const P = this.constructor; 12 | // tslint:disable 13 | return this.then( 14 | value => P.resolve(onfinally()).then(() => value), 15 | reason => P.resolve(onfinally()).then(() => { throw reason; }) 16 | ); 17 | } else { 18 | return this; 19 | } 20 | }; 21 | } 22 | -------------------------------------------------------------------------------- /network-utils/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015" 7 | ] 8 | }, 9 | "files": [ 10 | "src/index.ts" 11 | ] 12 | } -------------------------------------------------------------------------------- /network/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-network [![npm version](https://badge.fury.io/js/miniprogram-network.svg)](https://npmjs.com/package/miniprogram-network) 2 | 3 | > A Network package for MiniProgram 4 | > 5 | > 小程序底层网络库封装 6 | 7 | 8 | ## Integration 9 | * [x] [request](https://www.npmjs.com/package/miniprogram-request) 10 | * [x] [upload](https://www.npmjs.com/package/miniprogram-uploader) 11 | * [x] [download](https://www.npmjs.com/package/miniprogram-downloader) 12 | * [x] cache 13 | * [ ] websocket 14 | 15 | 16 | ## 网络库封装 17 | 18 | * [x] Promise泛型Promise 19 | * [x] CancelToken 可取消操作 20 | * [x] Retry 网络错误自动重试 21 | * [x] Cache 底层缓存支持(包括并发请求合并) 22 | * [x] Timeout 自定义超时时间 23 | * [x] timestamp 记录请求时间戳 24 | * [x] 每个请求的原生回调接口支持(`onHeadersReceived`事件)和(`onProgressUpdate`事件) 25 | * [x] Interceptors 拦截器 transform send data / transform response data 26 | * [x] Listeners 全局事件监听`onSend`,`onResponse`,`onRejected`,`onAbort`,`onComplete` 27 | * [x] 支持全局配置和每个请求单独配置 28 | 29 | tips: `miniprogram-network` >= 5.0.0 底层默认不在直接使用`miniprogram-queue`进行队列封装, 如果有需要可自行引用, 或直接使用 v4.x 30 | 31 | > * wx.request 自基础库 1.4.0 (2017.07) 开始支持队列 32 | > * wx.downloadFile 自基础库 1.4.4 (2017.07) 开始支持队列 33 | > * wx.uploadFile 自基础库 2.4.1 (2018.11) 开始支持队列 34 | 35 | ## 安装 36 | ``` 37 | npm i miniprogram-network 38 | ``` 39 | ```ts 40 | import {post} from 'miniprogram-network'; 41 | post('xxx',data).then(console.log) 42 | ``` 43 | ## 配置 44 | 45 | ### 通用配置 46 | 47 | * [x] `headers` 请求头 48 | * [x] `params` URL替换参数 49 | * [x] `baseURL` 根URL 50 | * [x] `retry` 重试次数/自定义重试机制 51 | * [x] `timestamp` 是否记录发送和响应时间戳 52 | * [x] `transformSend` 输入转换函数(Request,Download,Upload需分别设置) 53 | * [x] `transformResponse` 输出转换函数 (Request,Download,Upload需分别设置) 54 | 55 | ### 不同网络请求单独配置项 56 | 57 | * [x] `timeout` 请求超时时间 58 | * [x] `cancelToken` 取消请求的Token 59 | * [x] `onHeadersReceived` header 接受回调 60 | * [x] `onProgressUpdate` 进度回调 61 | * [Request配置项](../request#options) 62 | * [Download配置项](../downloader#options) 63 | * [Upload配置项](../uploader#options) 64 | 65 | ### 缓存配置 66 | 67 | 全局缓存策略 `cacheConfig` 68 | 69 | * [x] `cacheConfig.expire` 缓存时间单位`ms` 默认 `10分钟` 70 | * [x] `cacheConfig.excludeMethod` string[] 不缓存的操作,默认`['POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT']` 71 | * [x] `cacheConfig.resultCondition`结果缓存条件,默认`isOkResult`(2xx) 72 | * [x] `cacheConfig.keyBuilder` 缓存key生成规则,修改后`excludeMethod`将失效 73 | 74 | 单个请求设置缓存 75 | 76 | * [x] `expire` 缓存时间单位`ms` 默认 使用全局配置 77 | 78 | 79 | ### transform 数据转换 80 | 81 | 默认的`transformResponse`直接返回小程序原始的返回数据{statusCode,...} 82 | 83 | 同时提供了根据状态码返回对应数据的转换方式 84 | 85 | ```js 86 | import { 87 | REQUEST, transformRequestResponseOkData, 88 | DOWNLOAD, transformDownloadResponseOkData, 89 | UPLOAD, transformUploadResponseOkData, 90 | } from 'miniprogram-network'; 91 | 92 | // Request的默认响应拦设为成transformRequestResponseOkData, 93 | // 正常2xx返回data部分,否则rejected 94 | REQUEST.Defaults.transformResponse = transformRequestResponseOkData; 95 | // Download的默认响应拦设为transformDownloadResponseOkData, 96 | // 正常2xx返回string,否则rejected 97 | DOWNLOAD.Defaults.transformResponse = transformDownloadResponseOkData; 98 | // Upload默认响应拦截transformUploadResponseOkData, 99 | // 与小程序wx.uploadFile 不同之处会尝试进行JSON.parse反序列化字符串 100 | // 正常2xx返回data,否则rejected 101 | UPLOAD.Defaults.transformResponse = transformUploadResponseOkData; 102 | 103 | DOWNLOAD.download('url') 104 | .then(path=>{ 105 | console.log(path);//string 106 | }).catch(res=>{ 107 | console.error(res);//objct 108 | }); 109 | 110 | // 参数绑定 111 | Network.put('items/{id}',data,{ 112 | params: {id:123456}, // Object绑定模板参数 113 | }).then(console.log) 114 | Network.put('items/{0}',data,{ 115 | params: [123456], // 数组绑定模板参数 116 | }).then(console.log) 117 | ``` 118 | 119 | ### 取消操作 CancelToken (abort) 120 | 121 | 可通过cancel token 方式取消请求 122 | ```js 123 | import { get, CancelToken } from 'miniprogram-network'; 124 | 125 | // 创建一个 tokensource 126 | const source = CancelToken.source(); 127 | 128 | get('items', { skip: 100 }, { 129 | // 配置 cancelToken 130 | cancelToken: source.token 131 | }); 132 | 133 | // 需要取消操作时 134 | source.cancel('cancel the get'); 135 | ``` 136 | 137 | ## 快速配置 setConfig 138 | 139 | ```ts 140 | import { setConfig } from 'miniprogram-network'; 141 | 142 | //将Request,Upload,Download的默认baseURL设置为'https://api.newfuture.cc' 143 | setConfig('baseURL', 'https://api.newfuture.cc'); 144 | 145 | //等效方式 146 | setConfig({ 147 | baseURL:'https://api.newfuture.cc' 148 | }) 149 | 150 | ``` 151 | 152 | ### 延时重试 delayRetry 153 | 154 | ```ts 155 | import { delayRetry,REQUEST } from 'miniprogram-network'; 156 | 157 | // 间隔1s再次重试,最多重试2次 158 | REQUEST.Defaults.retry = delayRetry(1000,2); 159 | 160 | ``` 161 | 162 | ## Cache 缓存 163 | 164 | * `cacheRequest`,`cacheGet`与`REQUEST`公用默认配置 165 | * `cacheDownload`与`DOWNLOAD`公用默认配置 166 | 167 | 168 | ```js 169 | import { 170 | cacheConfig, // 缓存配置 171 | cacheGet, // 与get方法一致,自动使用cache 172 | cacheDownload, // 与download方法一致,自动使用cache 173 | cacheRequest, // 与request方法一致,自动使用cache 174 | } from 'miniprogram-network'; 175 | 176 | cacheConfig.expire = 10*60*1000;//设置缓存有效时间 177 | // 设置缓存条件,默认响应状态为2xx缓存数据 178 | cacheConfig.resultCondition = function(res){ 179 | return res.statusCode === 200; 180 | } 181 | 182 | //cacheGet 与 Request共用配置 183 | cacheGet('xxx').then(resolve); 184 | cacheGet('xxx').then(resolve); 185 | 186 | // cacheDownload 与 Download共用配置 187 | cacheDownload('xxx').then(); 188 | 189 | ``` 190 | 191 | 192 | ## LifeCycle 193 | 194 | 详情说明[miniprogram-network-life-cycle](../life-cycle/) 195 | ![](https://user-images.githubusercontent.com/6290356/49631309-6bddc080-fa2c-11e8-9a41-88fb50b2a1b7.png) 196 | -------------------------------------------------------------------------------- /network/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-network", 3 | "version": "5.3.0", 4 | "description": "Redefine the network API of Wechat MiniProgram", 5 | "keywords": [ 6 | "miniprogram", 7 | "network", 8 | "wx.downloadFile", 9 | "wx.request", 10 | "wx.uploadFile", 11 | "axios", 12 | "request", 13 | "小程序", 14 | "网络" 15 | ], 16 | "homepage": "https://miniprogram-network.newfuture.cc/", 17 | "bugs": { 18 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 19 | }, 20 | "repository": { 21 | "type": "git", 22 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 23 | }, 24 | "license": "Apache-2.0", 25 | "author": "NewFuture", 26 | "files": [ 27 | "dist/", 28 | "src/", 29 | "es/" 30 | ], 31 | "main": "dist/index.js", 32 | "module": "es/index.js", 33 | "scripts": { 34 | "build": "npm run build:es5 && npm run build:es6", 35 | "build:es5": "tsc", 36 | "build:es6": "tsc -m es6 --target es6 --outDir es", 37 | "lint": "tslint -p . -c ../tslint.json", 38 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 39 | "test": "echo \"Error: no test specified\" && exit 1" 40 | }, 41 | "dependencies": { 42 | "miniprogram-downloader": "^5.3.0-alpha.0", 43 | "miniprogram-network-cache": "^5.3.0-alpha.0", 44 | "miniprogram-network-utils": "^5.3.0-alpha.0", 45 | "miniprogram-request": "^5.3.0", 46 | "miniprogram-uploader": "^5.3.0-alpha.0" 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /network/src/cache.ts: -------------------------------------------------------------------------------- 1 | import { DOWNLOAD, Downloader } from 'miniprogram-downloader'; 2 | import { CacheOperator, Configuration, defaultKeyBuilder, isOkResult } from 'miniprogram-network-cache'; 3 | import { Http, REQUEST } from 'miniprogram-request'; 4 | 5 | /** 缓存配置 */ 6 | export const config: Configuration & { 7 | /** 不缓存方法 */ 8 | excludeMethod: MethodParam['method'][]; 9 | } = /*#__PURE__*/ { 10 | /** 11 | * 默认缓存时间 12 | */ 13 | expire: 10 * 60 * 1000, 14 | /** 15 | * GET,HEAD,OPTIONS默认缓存 16 | */ 17 | excludeMethod: ['POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT'], 18 | /** 19 | * 结果判断条件 20 | */ 21 | resultCondition: isOkResult, 22 | /** 23 | * 缓存的键构建方式, 24 | * 默认将不存在于`excludeMethod`采用`defaultKeyBuilder`进行构建(请求header不影响缓存) 25 | * 修改后`excludeMethod`将失效 26 | */ 27 | keyBuilder: (param) => ((config.excludeMethod.indexOf((param as MethodParam).method) === -1) && defaultKeyBuilder(param)) 28 | }; 29 | 30 | interface CacheOptions { 31 | /** 32 | * 缓存时间,单位毫秒 33 | */ 34 | expire?: number; 35 | } 36 | 37 | /** 38 | * 网络缓存 39 | */ 40 | export const cacheHttp = /*#__PURE__*/ new Http( 41 | REQUEST.Defaults, 42 | /*#__PURE__*/ 43 | CacheOperator.createHandler(REQUEST.handle, config), 44 | /*#__PURE__*/ 45 | REQUEST.Listeners 46 | ); 47 | /** 48 | * 下载缓存 49 | */ 50 | export const cacheDownloader = /*#__PURE__*/ new Downloader( 51 | DOWNLOAD.Defaults, 52 | /*#__PURE__*/ 53 | CacheOperator.createHandler(DOWNLOAD.handle, config), 54 | /*#__PURE__*/ 55 | DOWNLOAD.Listeners 56 | ); 57 | 58 | /** 59 | * request 缓存 60 | */ 61 | export const request: typeof cacheHttp['request'] = 62 | /*#__PURE__*/ 63 | cacheHttp.request.bind(cacheHttp); 64 | /** 65 | * GET 缓存 66 | */ 67 | // tslint:disable-next-line: no-reserved-keywords 68 | export const get: typeof cacheHttp['get'] = 69 | /*#__PURE__*/ 70 | cacheHttp.get.bind(cacheHttp); 71 | /** 72 | * 下载缓存 73 | */ 74 | export const download: typeof cacheDownloader['download'] = 75 | /*#__PURE__*/ 76 | cacheDownloader.download.bind(cacheDownloader); 77 | 78 | interface MethodParam { 79 | method?: 'OPTIONS' | 'GET' | 'HEAD' | 'POST' | 'PUT' | 'DELETE' | 'TRACE' | 'CONNECT'; 80 | } 81 | -------------------------------------------------------------------------------- /network/src/index.ts: -------------------------------------------------------------------------------- 1 | import { 2 | DOWNLOAD, 3 | DownloadInit as NetworkDownloadInit, 4 | DownloadOption as NetworkDownloadOption, 5 | DownloadParams as NetworkDownloadParams 6 | } from 'miniprogram-downloader'; 7 | import { GeneralCallbackResult } from 'miniprogram-network-utils'; 8 | import { 9 | REQUEST, 10 | RequestConfig as NetworkRequestConfig, 11 | RequestInit as NetworkRequestInit, 12 | RequestOption as NetworkRequestOption, 13 | RequestParams as NetworkRequestParams 14 | } from 'miniprogram-request'; 15 | import { 16 | UPLOAD, 17 | UploadInit as NetworkUploadInit, 18 | UploadOption as NetworkUploadOption, 19 | UploadParams as NetworkUploadParams 20 | } from 'miniprogram-uploader'; 21 | 22 | export { 23 | Http, 24 | REQUEST, 25 | transformRequestResponseOkData, 26 | transformRequestSendDefault, 27 | CancelToken, 28 | ICancelTokenSource 29 | } from 'miniprogram-request'; 30 | export { 31 | Uploader, 32 | UPLOAD, 33 | transformUploadResponseOkData, 34 | transformUploadSendDefault 35 | } from 'miniprogram-uploader'; 36 | export { 37 | Downloader, 38 | DOWNLOAD, 39 | transformDownloadResponseOkData, 40 | transfomDownloadSendDefault 41 | } from 'miniprogram-downloader'; 42 | export { 43 | defaultKeyBuilder 44 | } from 'miniprogram-network-cache'; 45 | export { 46 | cacheHttp, 47 | get as cacheGet, 48 | request as cacheRequest, 49 | download as cacheDownload, 50 | config as cacheConfig 51 | } from './cache'; 52 | export { setConfig, delayRetry } from './set-config'; 53 | 54 | // ShortLink for Request 55 | /** 56 | * REQUEST.request 57 | */ 58 | export const request: typeof REQUEST['request'] = 59 | /*#__PURE__*/ 60 | REQUEST.request.bind(REQUEST); 61 | /** 62 | * REQUEST.get 63 | */ 64 | // tslint:disable-next-line: no-reserved-keywords 65 | export const get: typeof REQUEST['get'] = 66 | /*#__PURE__*/ 67 | REQUEST.get.bind(REQUEST); 68 | /** 69 | * REQUEST.post 70 | */ 71 | export const post: typeof REQUEST['post'] = 72 | /*#__PURE__*/ 73 | REQUEST.post.bind(REQUEST); 74 | /** 75 | * REQUEST.put 76 | */ 77 | export const put: typeof REQUEST['put'] = 78 | /*#__PURE__*/ 79 | REQUEST.put.bind(REQUEST); 80 | /** 81 | * REQUEST.delete 82 | */ 83 | export const del: typeof REQUEST['delete'] = 84 | /*#__PURE__*/ 85 | REQUEST.delete.bind(REQUEST); 86 | /** 87 | * REQUEST.patch 88 | */ 89 | export const patch: typeof REQUEST['patch'] = 90 | /*#__PURE__*/ 91 | REQUEST.patch.bind(REQUEST); 92 | /** 93 | * REQUEST.head 94 | */ 95 | export const head: typeof REQUEST['head'] = 96 | /*#__PURE__*/ 97 | REQUEST.head.bind(REQUEST); 98 | 99 | // Short Link for Download 100 | /** 101 | * DOWNLOAD.download 102 | */ 103 | export const download: typeof DOWNLOAD['download'] = 104 | /*#__PURE__*/ 105 | DOWNLOAD.download.bind(DOWNLOAD); 106 | 107 | // ShortLink for Upload 108 | /** 109 | * UPLOAD.upload 110 | */ 111 | export const upload: typeof UPLOAD['upload'] = 112 | /*#__PURE__*/ 113 | UPLOAD.upload.bind(UPLOAD); 114 | 115 | export declare namespace Network { 116 | /** 117 | * Full Options for send a Request 118 | */ 119 | type RequestOption = NetworkRequestOption; 120 | /** 121 | * Extra Request Config for each Request 122 | */ 123 | type RequestConfig = NetworkRequestConfig; 124 | /** 125 | * Request Defaults Config to init a HTTP 126 | */ 127 | type RequestInit = NetworkRequestInit; 128 | /** 129 | * return type for Request TransformSend 130 | */ 131 | type RequestParams = NetworkRequestParams; 132 | /** 133 | * Full Options for download 134 | */ 135 | type DownloadOption = NetworkDownloadOption; 136 | /** 137 | * Defaults Config to init a Downloader 138 | */ 139 | type DownloadInit = NetworkDownloadInit; 140 | /** 141 | * return type for Download TransformSend 142 | */ 143 | type DownloadParams = NetworkDownloadParams; 144 | /** 145 | * Full Options for upload 146 | */ 147 | type UploadOption = NetworkUploadOption; 148 | /** 149 | * Defaults Config to init a Uploader 150 | */ 151 | type UploadInit = NetworkUploadInit; 152 | /** 153 | * return type for Upload TransformSend 154 | */ 155 | type UploadParams = NetworkUploadParams; 156 | 157 | /** 158 | * general result for fail/complete 159 | */ 160 | type GeneralResult = GeneralCallbackResult; 161 | } 162 | -------------------------------------------------------------------------------- /network/src/set-config.ts: -------------------------------------------------------------------------------- 1 | import { DOWNLOAD, DownloadInit } from 'miniprogram-downloader'; 2 | import { GeneralCallbackResult, Omit } from 'miniprogram-network-utils'; 3 | import { REQUEST, RequestInit } from 'miniprogram-request'; 4 | import { UPLOAD, UploadInit } from 'miniprogram-uploader'; 5 | 6 | /** 7 | * 公共配置 8 | */ 9 | type CommonConfig = Partial>; 10 | 11 | /** 12 | * 设置所有网络请求基本配置 13 | * @param config 公共配置项 14 | */ 15 | function setConfig(config: CommonConfig): void; 16 | /** 17 | * 设置所有网络请求公共配置 18 | * @example setConfig<'retry'>('retry',3); 19 | * @param key - 配置字段 20 | * @param value - 配置值 21 | */ 22 | function setConfig( 23 | key: T, 24 | value: (CommonConfig & { 'retry'?: number | ((data: object, reason?: GeneralCallbackResult) => Promise) })[T] 25 | ): void; 26 | function setConfig(): void { 27 | if (arguments.length === 2) { 28 | const key: keyof CommonConfig = arguments[0] as keyof CommonConfig; 29 | const value = arguments[1]; 30 | REQUEST.Defaults[key] = value; 31 | DOWNLOAD.Defaults[key] = value; 32 | UPLOAD.Defaults[key] = value; 33 | } else if (typeof arguments[0] === 'object') { 34 | const config: CommonConfig = arguments[0] as CommonConfig; 35 | Object.keys(config) 36 | .forEach((key) => { 37 | REQUEST.Defaults[key as keyof CommonConfig] = config[key as keyof CommonConfig] as any; 38 | DOWNLOAD.Defaults[key as keyof CommonConfig] = config[key as keyof CommonConfig] as any; 39 | UPLOAD.Defaults[key as keyof CommonConfig] = config[key as keyof CommonConfig] as any; 40 | }); 41 | } 42 | } 43 | 44 | /** 45 | * 延迟重试 46 | * 会在 options.__failure 记录失败的次数 47 | * @param delay 延时时间 单位ms 48 | * @param retryTimes 重试次数 49 | */ 50 | function delayRetry(delay: number, retryTimes: number = 1): 51 | (data: TWxOptions, reason?: GeneralCallbackResult) => Promise { 52 | return function (this: { __failure: number }, data, reason) { 53 | this.__failure = (this.__failure || 0) + 1; 54 | return new Promise((resolve, reject) => { 55 | if (this.__failure > retryTimes) { 56 | reject(reason); 57 | } else { 58 | setTimeout(resolve, delay, data); // tslint:disable-line: no-string-based-set-timeout 59 | } 60 | }); 61 | }; 62 | } 63 | 64 | export { 65 | setConfig, 66 | delayRetry 67 | }; 68 | 69 | /** 70 | * 设定一个定时器。在定时到期以后执行注册的回调函数 71 | * @param callback - 回调操作 72 | * @param delay - 延迟的时间,函数的调用会在该延迟之后发生,单位 ms。 73 | * @param rest - param1, param2, ..., paramN 等附加参数,它们会作为参数传递给回调函数。 74 | */ 75 | declare function setTimeout( 76 | callback: Function, 77 | delay?: number, 78 | rest?: any 79 | ): number; 80 | -------------------------------------------------------------------------------- /network/test/config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | REQUEST, transformRequestResponseOkData, 3 | DOWNLOAD, transformDownloadResponseOkData, 4 | UPLOAD, transformUploadResponseOkData, setConfig, delayRetry, 5 | } from '../src/index' 6 | 7 | // Request的默认响应拦设为成transformRequestResponseOkData,正常返回data部分 8 | REQUEST.Defaults.transformResponse = transformRequestResponseOkData; 9 | // Download的默认响应拦设为transformDownloadResponseOkData,正常返回string 10 | DOWNLOAD.Defaults.transformResponse = transformDownloadResponseOkData; 11 | // Upload默认响应拦截transformUploadResponseOkData设置正常返回data 12 | UPLOAD.Defaults.transformResponse = transformUploadResponseOkData; 13 | 14 | DOWNLOAD.download('url') 15 | .then(path=>{ 16 | console.log(path);//string 17 | }).catch(res=>{ 18 | console.error(res);//objct 19 | }); 20 | 21 | setConfig({ 22 | baseURL:'', 23 | }); 24 | setConfig('baseURL','https://api.xyz'); 25 | const retry = delayRetry(100,10); 26 | setConfig('retry', retry ) 27 | 28 | REQUEST.Defaults.retry=delayRetry(1000,2); -------------------------------------------------------------------------------- /network/test/methods.ts: -------------------------------------------------------------------------------- 1 | 2 | import { Network, get, del, upload, download, request } from "../index"; 3 | 4 | get('test').then((r) => r); 5 | 6 | get('s') 7 | del('ss').then(console.log) 8 | 9 | upload('ss', 'testname'); 10 | 11 | download('ss'); 12 | download({ 13 | url: 'sss', 14 | onHeadersReceived: function (res) { 15 | console.log(res.header) 16 | } 17 | } as Network.DownloadOption).then(res => res.statusCode); 18 | -------------------------------------------------------------------------------- /network/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015" 7 | ] 8 | }, 9 | "files": [ 10 | "src/index.ts" 11 | ] 12 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.0.0", 3 | "description": "Fetch API polyfill for Wechat MiniProgram", 4 | "keywords": [ 5 | "fetch", 6 | "miniprogram", 7 | "promise", 8 | "request" 9 | ], 10 | "homepage": "https://miniprogram-network.newfuture.cc/", 11 | "bugs": { 12 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 13 | }, 14 | "repository": { 15 | "type": "git", 16 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 17 | }, 18 | "license": "Apache-2.0", 19 | "author": { 20 | "name": "NewFuture", 21 | "email": "npm@newfuture.cc", 22 | "url": "https://newfuture.cc" 23 | }, 24 | "main": "dist/index.js", 25 | "scripts": { 26 | "build": "npm run build:ts && npm run lint:fix", 27 | "build:ts": "lerna run build", 28 | "clean": "lerna run clean", 29 | "start": "npm run rm:module && lerna bootstrap && lerna run build", 30 | "prepub": "npm run clean && npm run build:ts && npm run lint", 31 | "pub": "lerna publish", 32 | "rm:module": "lerna clean --yes", 33 | "lint": "lerna run lint", 34 | "lint:fix": "lerna run lint:fix", 35 | "pretest": "lerna bootstrap --no-ci", 36 | "test": "npm run lint && lerna run build" 37 | }, 38 | "devDependencies": { 39 | "lerna": "^3.22.1", 40 | "rimraf": "^2.7.1", 41 | "tslint": "^5.20.1", 42 | "tslint-microsoft-contrib": "^6.2.0", 43 | "typescript": "^3.9.7" 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /promise/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-promise [![npm version](https://badge.fury.io/js/miniprogram-promise.svg)](https://npmjs.com/package/miniprogram-promise) 2 | 3 | > MiniProgram Promise Asynchronous API poly fill 4 | > 在小程序中异步API Promise封装 5 | 6 | ## Feathers: 7 | * [x] finally Promise (支持Finally) 8 | * [x] cancelable/abort (可取消的Promise) 9 | 10 | ## Usage 11 | 12 | ```js 13 | import {cancelablePromisify} from 'miniprogram-promise'; 14 | 15 | request = cancelablePromisify(wx.request); 16 | 17 | request({url:'xxx'}) 18 | .then((res)=>{/**/}) 19 | .catch((errr)=>{}) 20 | .finally(()=>{}); 21 | 22 | 23 | request({url:'xxx'}).then().cancel() 24 | ``` 25 | 26 | ## inspired by 27 | * [minapp-wx](https://github.com/wx-minapp/minapp-wx) 28 | * [promise-cancelable](https://github.com/joaogranado/promise-cancelable) 29 | -------------------------------------------------------------------------------- /promise/cancelable-promise.ts: -------------------------------------------------------------------------------- 1 | const handleCallback = (resolve, reject, callback, r) => { 2 | try { 3 | resolve(callback(r)); 4 | } catch (e) { 5 | reject(e); 6 | } 7 | }; 8 | 9 | /** 10 | * CancelablePromise 11 | */ 12 | export class CancelablePromise implements PromiseLike { 13 | private readonly promise: Promise; 14 | private isCancelled: boolean = false; 15 | private cancelFunction: Function | void = undefined; 16 | 17 | /** 18 | * Creates a new CancelablePromise. 19 | * @param executor A callback used to initialize the Cancelable promise. This callback is passed two arguments: 20 | * a resolve callback used to resolve the promise with a value or the result of another promise, 21 | * and a reject callback used to reject the promise with a provided reason or error. 22 | * @param canceller a cancel handler used to cancel the promise with a provided reason. 23 | */ 24 | constructor(executor: ( 25 | resolve: (value?: T | PromiseLike) => void, 26 | reject: (reason?: any) => void 27 | ) => any, canceller?: undefined | ((result?: any) => any)) { 28 | this.promise = new Promise((resolve, reject) => { 29 | const result = executor(resolve, reject); 30 | if (canceller) { 31 | this.cancelFunction = () => canceller(result); 32 | } 33 | }); 34 | } 35 | 36 | /** 37 | * Creates a Promise that is resolved with an array of results when all of the provided Promises 38 | * resolve, or rejected when any Promise is rejected. 39 | * @param values An array of Promises. 40 | * @returns A new CancelablePromise. 41 | */ 42 | static all(values: Array>): CancelablePromise { 43 | return new CancelablePromise( 44 | (resolve, reject) => Promise.all(values).then(resolve, reject), 45 | () => values.forEach(v => v && typeof v.cancel === 'function' && v.cancel()) 46 | ); 47 | } 48 | 49 | /** 50 | * Creates a Promise that is resolved with an array of results when all of the provided Promises 51 | * resolve, or rejected when any Promise is rejected. 52 | * @param values An array of Promises. 53 | * @returns A new CancelablePromise. 54 | */ 55 | static race(values: Array>): CancelablePromise { 56 | return new CancelablePromise( 57 | (resolve, reject) => Promise.race(values).then(resolve, reject), 58 | () => () => values.forEach(v => v && typeof v.cancel === 'function' && v.cancel()) 59 | ); 60 | } 61 | 62 | public static reject(reason: any): CancelablePromise; 63 | /** 64 | * Creates a new rejected CancelablePromise for the provided reason. 65 | * @param reason The reason the promise was rejected. 66 | * @returns A new rejected CancelablePromise. 67 | */ 68 | public static reject(reason?: any): CancelablePromise { 69 | return new CancelablePromise( 70 | (resolve, reject) => reject(reason) 71 | ); 72 | } 73 | 74 | static resolve(value?:void): CancelablePromise; 75 | /** 76 | * Creates a new resolved CancelablePromise for the provided value. 77 | * @param value A promise. 78 | * @returns A CancelablePromise whose internal state matches the provided promise. 79 | */ 80 | static resolve(value: T | PromiseLike): CancelablePromise { 81 | return new CancelablePromise( 82 | (resolve, reject) => resolve(value) 83 | ); 84 | } 85 | 86 | /** 87 | * Cancel the promise 88 | */ 89 | public cancel(): void { 90 | if (!this.isCancelled) { 91 | this.isCancelled = true; 92 | this.cancelFunction && this.cancelFunction(); 93 | } 94 | } 95 | 96 | /** 97 | * Attaches callbacks for the resolution and/or rejection of the Promise. 98 | * @param onfulfilled The callback to execute when the Promise is resolved. 99 | * @param onrejected The callback to execute when the Promise is rejected. 100 | * @returns A CancelablePromise for the completion of which ever callback is executed. 101 | */ 102 | public then( 103 | onfulfilled?: ((value: T) => TResult1 | PromiseLike) | undefined | null, 104 | onrejected?: ((reason: any) => TResult2 | PromiseLike) | undefined | null 105 | ): CancelablePromise { 106 | return new CancelablePromise( 107 | (resolve, reject) => this.promise.then(onfulfilled, onrejected).then(resolve, reject), 108 | this.cancel 109 | ); 110 | } 111 | 112 | 113 | /** 114 | * Attaches a callback for only the rejection of the Promise. 115 | * @param onrejected The callback to execute when the Promise is rejected. 116 | * @returns A CancelablePromise for the completion of the callback. 117 | */ 118 | catch(onrejected?: ((reason: any) => TResult | PromiseLike) | undefined | null): CancelablePromise { 119 | return this.then(undefined, onrejected); 120 | } 121 | } 122 | 123 | CancelablePromise.all([]).cancel() -------------------------------------------------------------------------------- /promise/index.ts: -------------------------------------------------------------------------------- 1 | import "./promise.finally"; 2 | import { promisify, cancelablePromisify, CancelablePromise } from "./promisify"; 3 | 4 | declare var wx: { 5 | request: Function; 6 | downloadFile: Function; 7 | uploadFile: Function; 8 | } 9 | 10 | const CANCELABLE_API = ['request', 'downloadFile', 'uploadFile']; 11 | const NOPROMIS_API = ['canIUse', 'drawCanvas']; 12 | 13 | function apply() { 14 | 15 | for (let key in wx) { 16 | let func: Function; 17 | 18 | if (!wx.hasOwnProperty(key)) { 19 | continue; 20 | } else if (/^on|^create|^pause|Sync$|Manager$/.test(key) || NOPROMIS_API.indexOf(key) >= 0 && key !== 'createBLEConnection') { 21 | continue; 22 | } else if (CANCELABLE_API.indexOf(key) >= 0) { 23 | func = cancelablePromisify(wx[key]); 24 | } else { 25 | func = promisify(wx[key]); 26 | } 27 | 28 | Object.defineProperty(wx, key, { 29 | get() { 30 | return func; 31 | } 32 | }) 33 | } 34 | } 35 | 36 | export { 37 | apply, 38 | promisify, 39 | cancelablePromisify, 40 | CancelablePromise 41 | } -------------------------------------------------------------------------------- /promise/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-promise", 3 | "version": "0.0.19", 4 | "description": "promise for Wechat MiniProgram", 5 | "keywords": [ 6 | "download", 7 | "miniprogram", 8 | "request", 9 | "upload" 10 | ], 11 | "homepage": "https://github.com/NewFuture/miniprogram-network/", 12 | "bugs": { 13 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/NewFuture/miniprogram.git" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "NewFuture", 21 | "files": [ 22 | "dist/**" 23 | ], 24 | "main": "dist/index.js", 25 | "typings": "dist/index.d.ts", 26 | "scripts": { 27 | "build": "tsc index.ts --target es6 --outDir dist -d", 28 | "test": "echo \"Error: no test specified\" && exit 1" 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /promise/promise.finally.ts: -------------------------------------------------------------------------------- 1 | 2 | // add Finally interface 3 | interface IFinallyPromise { 4 | finally(callback: Function): Promise 5 | } 6 | interface Promise extends IFinallyPromise { } 7 | 8 | if (!Promise.prototype.finally) { 9 | Promise.prototype.finally = function (callback: Function): Promise { 10 | const P = this.constructor as PromiseConstructor 11 | return this.then( 12 | value => P.resolve(callback()).then(() => value), 13 | reason => P.resolve(callback()).then(() => { throw reason; }) 14 | ); 15 | } 16 | } -------------------------------------------------------------------------------- /promise/promisify.ts: -------------------------------------------------------------------------------- 1 | import { CancelablePromise } from "./cancelable-promise"; 2 | 3 | interface WxAsyncOptions { 4 | success?: Function, 5 | fail?: Function, 6 | complete?: Function, 7 | [propName: string]: any 8 | } 9 | 10 | /** 11 | * Promisify wx function 12 | * @param func wx.function 13 | */ 14 | export function promisify(func: (o: WxAsyncOptions) => any): ((o: WxAsyncOptions) => Promise) { 15 | return (options: WxAsyncOptions) => new Promise((resolve, reject) => { 16 | options.success = (res: any) => { 17 | options.success && options.success(res); 18 | resolve(res); 19 | } 20 | options.fail = (err: any) => { 21 | options.fail && options.fail(err); 22 | reject(err); 23 | } 24 | func(options); 25 | }) 26 | } 27 | 28 | /** 29 | * Promisify wx function as CancelablePromisify 30 | * @param func wx.function 31 | */ 32 | export function cancelablePromisify(func: (o: WxAsyncOptions) => any): ((o: WxAsyncOptions) => CancelablePromise) { 33 | return (options: WxAsyncOptions) => new CancelablePromise((resolve, reject) => { 34 | options.success = (res: any) => { 35 | options.success && options.success(res); 36 | resolve(res); 37 | } 38 | options.fail = (err: any) => { 39 | options.fail && options.fail(err); 40 | reject(err); 41 | } 42 | return func(options) 43 | }, (task) => task.abort()); 44 | } 45 | 46 | export {CancelablePromise} -------------------------------------------------------------------------------- /queue/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-queue [![npm version](https://badge.fury.io/js/miniprogram-queue.svg)](https://npmjs.com/package/miniprogram-queue) 2 | 3 | > Queue Package for MiniProgram API 4 | > 5 | > 小程序底层操作队列化(`wx.request`, `wx.downloadFile`,`wx.uploadFile`) 6 | > 7 | > [miniprogram-network](https://npmjs.com/package/miniprogram-network)默认队列实现 8 | 9 | Features: 10 | 11 | * [x] 可自动注入/手动管理 12 | * [x] [取消操作(`abort`)](#abort-取消操作) 13 | * [x] [进度/header回调](#progress-进度支持) 14 | * [x] [插队](#jump-插队) 15 | * [x] 时间戳 16 | * [x] timeout 自定义超时时间 17 | * [ ] 动态收缩扩展 18 | 19 | ## Install(安装) 20 | 21 | ``` 22 | npm i miniprogram-queue 23 | ``` 24 | 25 | ## Usage(使用) 26 | 27 | ```js 28 | import {WxQueue} from 'miniprogram-queue'; 29 | //创建请求队列 30 | const requestQueue = new WxQueue(wx.request,10); 31 | // const uploadQueue = new WxQueue(wx.uploadFile,10); 32 | // const downloadQueue = new WxQueue(wx.downloadFile,10); 33 | 34 | // 发送请求 35 | const task = requestQueue.push({ 36 | url:'https://github.com/NewFuture/miniprogram-network/' 37 | }); 38 | 39 | // task.abort() 可取消操作 40 | ``` 41 | 42 | ## API 43 | 44 | ### 参数 45 | 46 | 与官网API参数兼容 支持 扩展参数: 47 | * `onProgressUpdate` 进度回调函数 48 | * `onHeadersReceived` 响应头回调函数 49 | * `jump` (默认`false`)是否插队 50 | * `timestamp` (默认`false`) 是否记录时间戳,是则complete回调中会包含 一个`time`字段 51 | ```ts 52 | { 53 | send: number, 54 | response: number 55 | } 56 | ``` 57 | 58 | 兼容API 59 | * [request](https://developers.weixin.qq.com/miniprogram/dev/api/network/request/wx.request.html) 60 | * [downloadFile](https://developers.weixin.qq.com/miniprogram/dev/api/network/download/wx.downloadFile.html) 61 | * [uploadFile](https://developers.weixin.qq.com/miniprogram/dev/api/network/upload/wx.uploadFile.html) 62 | 63 | 同时 `downloadFile` 和 `uploadFile` 支持通过[process 参数](#progress) 之间设置进度回调 64 | 65 | ### jump (插队) 66 | 67 | ```js 68 | //第二个参数为true时优先级最高 69 | requestQueue.push({ 70 | url:'https://github.com/', 71 | jump:true,//插队 72 | }); 73 | ``` 74 | 75 | ## Abort (取消操作) 76 | 77 | 所有操作返回一个`Task`对象,可取消操作 78 | 79 | 注意: 和官方API一致 **取消时依然会执行complete(如果配置了)**。 80 | 81 | * 自动注入方式 82 | ```js 83 | var task = wx.request(obj); 84 | task.abort(); 85 | ``` 86 | 87 | * 手动push 88 | ```js 89 | var task = queue.push(obj); 90 | task.abort(); 91 | ``` 92 | 93 | ## Progress (进度支持) 94 | 95 | * `DownloadTask.onProgressUpdate(function callback)` 96 | * `UploadTask.onProgressUpdate(function callback)` 97 | 98 | > 小程序`onProgressUpdate`API的接口,设计上不太合理, 99 | > 这个接口放在请求发生时更合适,而非在Task创建后。 100 | 101 | 此处保留了对onProgressUpdate的兼容适配,同时提供了可通过参数(`progress`)传入的方法 102 | 103 | ```js 104 | 105 | const task =uploadQueue.push({ 106 | // 其他参数 107 | onProgressUpdate:processCallback// callback function 108 | onHeadersReceived:console.log 109 | }); 110 | // function processCallback(progress,currentBytes,totalBytes){} 111 | ``` 112 | 113 | ```js 114 | // obj update object 115 | const task = wx.uploadFile(obj); 116 | // 保留原生调用方式支持 117 | task.onProgressUpdate(processCallback); // callback function 118 | // function processCallback(progress,currentBytes,totalBytes){} 119 | ``` 120 | -------------------------------------------------------------------------------- /queue/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-queue", 3 | "version": "4.5.1", 4 | "description": "Queue Management for Wechat MiniProgram", 5 | "keywords": [ 6 | "download", 7 | "miniprogram", 8 | "queue", 9 | "request", 10 | "upload", 11 | "小程序", 12 | "队列", 13 | "网络" 14 | ], 15 | "homepage": "https://miniprogram-network.newfuture.cc/queue/", 16 | "bugs": { 17 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 18 | }, 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 22 | }, 23 | "license": "Apache-2.0", 24 | "author": "NewFuture", 25 | "files": [ 26 | "dist/", 27 | "types/", 28 | "src/", 29 | "es/" 30 | ], 31 | "main": "dist/index.js", 32 | "module": "es/index.js", 33 | "types": "types/index.d.ts", 34 | "scripts": { 35 | "build": "npm run build:es5 && npm run build:es6", 36 | "build:es5": "tsc --declarationDir types", 37 | "build:es6": "tsc -m es6 --target es6 --outDir es", 38 | "clean": "rimraf -rf es dist types ", 39 | "lint": "tslint -p . -c ../tslint.json", 40 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 41 | "test": "echo \"Error: no test specified\" && exit 1" 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /queue/src/index.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | /** 4 | * 微信小程序操作队列封装管理 5 | * @example var rq = new WxQueue(wx.requst); 6 | * @template TParam 微信操作参数类型 7 | * @template TTask 微信操返回task类型 8 | */ 9 | export class WxQueue { 10 | /** 11 | * 队列最大长度 12 | */ 13 | public readonly MAX: number; 14 | 15 | /** 16 | * 任务ID计数器 17 | */ 18 | private taskid = 0; 19 | 20 | /** 21 | * 待完成队列 22 | */ 23 | private readonly todo: [number, TParam & ExtraOptions][] = []; 24 | 25 | /** 26 | * 保持正在运行的任务 27 | */ 28 | private readonly taskMap = new Map(); 29 | 30 | /** 31 | * 小程序的原始操作API 32 | */ 33 | private readonly operator: (params: TParam) => TTask; 34 | 35 | /** 36 | * 创建Wx操作队列 37 | * @param wxFunc Wx操作函数 38 | * @param maxLength 最大队列长度,默认10 39 | */ 40 | constructor(wxFunc: (params: TParam) => TTask, maxLength: number = 10) { 41 | this.operator = wxFunc; 42 | this.MAX = maxLength || 10; 43 | } 44 | 45 | /** 46 | * 向队列中添加操作 47 | * @param param 微信操作 48 | */ 49 | public push(param: QueueOption): TTask { 50 | const id = ++this.taskid; 51 | if (this.taskMap.size < this.MAX) { 52 | // task队列未满 53 | return this._process(id, param); 54 | } else if (param.jump) { 55 | // 插队 56 | this.todo.unshift([id, param]); 57 | } else { 58 | this.todo.push([id, param]); 59 | } 60 | 61 | return { 62 | abort: (): void => { this._abort(id); }, 63 | onProgressUpdate: (callback: ExtraOptions['onProgressUpdate']) => { this._onProgress(id, callback); }, 64 | onHeadersReceived: (callback: ExtraOptions['onHeadersReceived']) => { this._onHeaders(id, callback); } 65 | } as any; 66 | } 67 | 68 | /** 69 | * check and do next task 70 | */ 71 | private _next(): void { 72 | if (this.todo.length > 0 && this.taskMap.size < this.MAX) { 73 | const [taskid, taskOptions] = this.todo.shift()!; 74 | this._process(taskid, taskOptions); 75 | } 76 | } 77 | 78 | /** 79 | * process a task 80 | * @param id task ID 81 | * @param options task param 82 | */ 83 | private _process(id: number, options: QueueOption): TTask { 84 | const oldComplete = options.complete; 85 | let timeoutFailHandle: number | undefined; 86 | let taskTimeoutCancelled = false; 87 | let task: TTask; 88 | 89 | options.complete = (res: { errMsg: string; time?: TimeRecorder; timeout?: boolean }) => { 90 | if (timeoutFailHandle) { 91 | // 清理计时器 92 | clearTimeout(timeoutFailHandle); 93 | } 94 | if (options.timestamp && this.taskMap.has(id)) { 95 | res.time = this.taskMap.get(id)![1] || {}; 96 | res.time.response = Date.now(); 97 | } 98 | this.taskMap.delete(id); 99 | // 原始结束回调 100 | if (oldComplete) { 101 | if (taskTimeoutCancelled) { 102 | res.errMsg = `${(res.errMsg || '').split(':', 1)[0]}: timeout`; 103 | res.timeout = true; 104 | } 105 | oldComplete.call(options, res); 106 | } 107 | this._next(); 108 | }; 109 | 110 | if (options.timeout! > 0) { 111 | // 自定义timeout 拦截fail 注入timeout 112 | const oldFail = options.fail; 113 | if (oldFail) { 114 | options.fail = (res: { errMsg: string; timeout?: boolean }) => { 115 | if (taskTimeoutCancelled) { 116 | res.errMsg = `${(res.errMsg || '').split(':', 1)[0]}: timeout`; 117 | res.timeout = true; 118 | } 119 | if (oldFail) { 120 | oldFail.call(options, res); 121 | } 122 | }; 123 | } 124 | // 计时器 自定义超时 125 | timeoutFailHandle = setTimeout( 126 | () => { 127 | timeoutFailHandle = undefined; 128 | taskTimeoutCancelled = true; 129 | task.abort(); 130 | }, 131 | options.timeout!); 132 | } 133 | task = this.operator(options); 134 | 135 | // task progress polyfill 136 | if (options.onProgressUpdate && task.onProgressUpdate) { 137 | task.onProgressUpdate(options.onProgressUpdate); 138 | } 139 | // task onHeadersReceived 140 | if (options.onHeadersReceived) { 141 | task.onHeadersReceived(options.onHeadersReceived); 142 | } 143 | this.taskMap.set(id, [ 144 | task, 145 | options.timestamp ? { send: Date.now() } : undefined 146 | ]); 147 | 148 | return task; 149 | } 150 | 151 | /** 152 | * stop and remove a task 153 | * @param taskid - the id of task to abort 154 | */ 155 | private _abort(taskid: number) { 156 | const index = this.todo.findIndex(v => v[0] === taskid); 157 | if (index >= 0) { 158 | const completeCallback = this.todo[index][1].complete; 159 | this.todo.splice(index, 1); 160 | // call back complete. 161 | if (completeCallback) { 162 | completeCallback({ errMsg: 'request:fail abort', cancel: true, source: WxQueue.name }); 163 | } 164 | } else if (this.taskMap.has(taskid)) { 165 | this.taskMap.get(taskid)![0].abort(); 166 | this.taskMap.delete(taskid); 167 | } 168 | } 169 | 170 | /** 171 | * progress update callback 172 | * https://developers.weixin.qq.com/miniprogram/dev/api/network/download/DownloadTask.onProgressUpdate.html 173 | * @param taskid - task id 174 | * @param callback 回调操作 175 | */ 176 | private _onProgress( 177 | taskid: number, 178 | callback: ExtraOptions['onProgressUpdate'] 179 | ): void { 180 | const result = this.todo.find(v => v[0] === taskid); 181 | if (result) { 182 | result[1].onProgressUpdate = callback; 183 | } else if (this.taskMap.has(taskid)) { 184 | this.taskMap.get(taskid)![0].onProgressUpdate!(callback as any); 185 | } 186 | } 187 | 188 | private _onHeaders( 189 | taskid: number, 190 | callback: ExtraOptions['onHeadersReceived'] 191 | ) { 192 | const result = this.todo.find(v => v[0] === taskid); 193 | if (result) { 194 | result[1].onHeadersReceived = callback; 195 | } else if (this.taskMap.has(taskid)) { 196 | this.taskMap.get(taskid)![0].onHeadersReceived(callback); 197 | } 198 | } 199 | } 200 | 201 | export type QueueOption = T & ExtraOptions; 202 | 203 | /** 204 | * 微信操作参数声明 205 | */ 206 | interface ExtraOptions { 207 | /** 208 | * progress 回调 209 | */ 210 | onProgressUpdate?: 211 | | wx.UploadTaskOnProgressUpdateCallback 212 | | wx.DownloadTaskOnProgressUpdateCallback; 213 | /** 214 | * 是否插队 215 | */ 216 | jump?: boolean; 217 | 218 | /** 219 | * 自定义超时时间 220 | */ 221 | timeout?: number; 222 | 223 | /** 224 | * 记录时间戳 225 | */ 226 | timestamp?: boolean | object; 227 | 228 | /** 229 | * 开发者服务器返回的 HTTP Response Header 回调 230 | */ 231 | onHeadersReceived?(result: { header: object }): void; 232 | } 233 | 234 | interface BaseOption { 235 | /** 开发者服务器接口地址 */ 236 | url: string; 237 | /** 接口调用结束的回调函数(调用成功、失败都会执行) */ 238 | complete?: Function; 239 | /** 接口调用失败的回调函数 */ 240 | fail?: Function; 241 | /** 接口调用成功的回调函数 */ 242 | success?: Function; 243 | } 244 | 245 | interface BaseTask { 246 | abort(): void; 247 | /** HTTP Response Header 事件的回调函数 */ 248 | onHeadersReceived(callback: ExtraOptions['onHeadersReceived']): void; 249 | /** 下载进度变化事件的回调函数 */ 250 | onProgressUpdate?(callback: ExtraOptions['onProgressUpdate']): void; 251 | } 252 | 253 | declare namespace wx { 254 | type UploadTaskOnProgressUpdateCallback = ( 255 | res: { 256 | /** 257 | * 上传进度百分比 258 | */ 259 | progress: number; 260 | /** 261 | * 已经上传的数据长度,单位 Bytes 262 | */ 263 | totalBytesSent: number; 264 | /** 265 | * 预期需要上传的数据总长度,单位 Bytes 266 | */ 267 | totalBytesExpectedToSend: number; 268 | } 269 | ) => void; 270 | 271 | type DownloadTaskOnProgressUpdateCallback = ( 272 | res: { 273 | /** 274 | * 下载进度百分比 275 | */ 276 | progress: number; 277 | /** 278 | * 已经下载的数据长度,单位 Bytes 279 | */ 280 | totalBytesWritten: number; 281 | /** 282 | * 预期需要下载的数据总长度,单位 Bytes 283 | */ 284 | totalBytesExpectedToWrite: number; 285 | } 286 | ) => void; 287 | } 288 | 289 | /** 290 | * 设定一个定时器。在定时到期以后执行注册的回调函数 291 | * @param callback - 回调操作 292 | * @param delay - 延迟的时间,函数的调用会在该延迟之后发生,单位 ms。 293 | * @param rest - param1, param2, ..., paramN 等附加参数,它们会作为参数传递给回调函数。 294 | */ 295 | declare function setTimeout( 296 | callback: Function, 297 | delay?: number, 298 | rest?: any 299 | ): number; 300 | 301 | export interface TimeRecorder { 302 | send?: number; 303 | response?: number; 304 | } 305 | 306 | /** 307 | * 取消由 setTimeout 设置的定时器。 308 | * @param timeoutID - 要取消的定时器的 309 | */ 310 | declare function clearTimeout( 311 | timeoutID: number 312 | ): void; 313 | -------------------------------------------------------------------------------- /queue/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015", 7 | "es2015.promise" 8 | ] 9 | }, 10 | "files": [ 11 | "src/index.ts" 12 | ] 13 | } -------------------------------------------------------------------------------- /request/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-request [![npm version](https://badge.fury.io/js/miniprogram-request.svg)](https://npmjs.com/package/miniprogram-request) 2 | 3 | > An axios API like `Request` package for MiniProgram 4 | > 5 | > 更好用的小程序请求库封装 6 | > 小程序网络库[miniprogram-network](https://github.com/NewFuture/miniprogram-network) 核心库之一 7 | 8 | ## API 9 | 10 | ### methods: 11 | 12 | * `request(options): Promise`; 13 | * `request(method, action, data?, config?): Promise`; 14 | * `get(action, data?, config?): Promise`; 15 | * `post(action, data?, config?): Promise`; 16 | * `put(action, data?, config?): Promise`; 17 | * `delete(action, data?, config?): Promise`; 18 | * `patch(action, data?, config?): Promise`; 19 | * `head(action, data?, config?): Promise`; 20 | 21 | ### options 22 | 23 | * [x] `url` 地址 **required** (_只能请求时设置for single request_) 24 | * [x] `method` 方法 (_只能请求时设置for single request_) 25 | * [x] `data` 数据 (_只能请求时设置for single request_) 26 | * [x] `cancelToken` 取消 (_只能请求时设置for single request_) 27 | * [x] `onHeadersReceived` 接收头响应 (_只能请求时设置for single request_) 28 | * [x] `timeout` 自定义超时时间ms (_只能请求时设置for single request_) 29 | * [x] `responseType` 返回数据类型 30 | * [x] `headers` 请求头 31 | * [x] `params` URL参数 32 | * [x] `baseURL` 根URL 33 | * [x] `retry` 重试次数 34 | * [x] `timestamp` 是否记录发送和响应时间戳 35 | * [x] `transformSend` 输入转换函数 36 | * [x] `transformResponse` 输出转换函数 37 | 38 | ### Global Listeners 39 | 40 | * [x] `onSend` (before request data send & after request data transformed) 41 | * [x] `onResponse` (after request response data transformed) 42 | * [x] `onRejected` (before `catch` of Promise) 43 | * [x] `onAbort` 44 | * [x] `onComplete` 45 | 46 | ## Usage 47 | 48 | ## install 49 | ``` 50 | npm i miniprogram-request 51 | ``` 52 | 53 | ### quick start 54 | 55 | ```js 56 | import {REQUEST} from 'miniprogram-request'; 57 | 58 | // 设置全局配置,设置一次全部生效 59 | // 设置请求根地址,可选 60 | REQUEST.Defaults.baseURL = "https://minipgrogram.newfuture.cc/" 61 | // 添加监听时间 可选 62 | REQUEST.Listeners.onResponse.push(console.log); // 记录每次响应 63 | 64 | REQUEST.get('items') 65 | .then(applyFunction) // 返回数据 66 | .catch(err=>wx.showToast({title:'数据拉取失败'})); 67 | ``` 68 | 69 | ### URL build 70 | 71 | ```js 72 | REQUEST.post('/items',{name:'future'}) 73 | // POST /items 74 | // {name:"future"} 75 | 76 | REQUEST.get('/items/{id}',{show_detail:false},{params:{id:12345}}) 77 | // GET /items/12345?show_detail=false 78 | 79 | REQUEST.put('/items/{id}',{name:'new'},{params:{id:12345}}) 80 | // PUT /items/12345 81 | // {name:"new"} 82 | // --- json 序列化body 83 | 84 | 85 | //由于小程序不支持Patch,此处使用X-HTTP-Method-Override实现Patch 86 | //此功能需要服务器端支持 87 | REQUEST.patch('/items/{id}',{name:'new'},{params:{id:12345}}) 88 | // POST /items/12345 89 | // X-HTTP-Method-Override: PATCH 90 | // {name:"new"} 91 | 92 | 93 | ``` 94 | 95 | ### TypeScript 96 | 泛型支持 97 | ```js 98 | // TS 类型推断 99 | import { REQUEST, transformRequestResponseOkData } from 'miniprogram-request'; 100 | 101 | // 自动提取返回值为 2xx 时的 `response.data` 102 | REQUEST.Defaults.transformResponse = transformRequestResponseOkData 103 | 104 | interface Item { 105 | id: number, 106 | name: string 107 | } 108 | 109 | // 泛型 then的参数值类型为 Item[] 110 | REQUEST.get('/items') 111 | .then(list => list.forEach(i => console.log(i.id + i.name))) 112 | ``` 113 | 114 | ### CancelToken (abort) 115 | 可通过cancel token 方式取消请求 116 | ```js 117 | import { REQUEST, CancelToken } from 'miniprogram-request'; 118 | 119 | // 创建一个 tokensource 120 | const source = CancelToken.source(); 121 | 122 | REQUEST.get('items', { skip: 100 }, { 123 | // 配置 cancelToken 124 | cancelToken: source.token 125 | }); 126 | 127 | // 需要取消操作时 128 | source.cancel('cancel the reqeust'); 129 | ``` 130 | 131 | ## configuration 132 | 133 | ### 修改全局配置 134 | ```js 135 | REQUEST.Defaults.retry = 2;//设置网络错误时重试次数 136 | ``` 137 | 138 | ### 全部可配置内容 139 | ```js 140 | { 141 | 142 | /** 143 | * 请求的相对地址 144 | */ 145 | url: string; 146 | 147 | /** 148 | * 请求方法 149 | * HTTP request mthod: GET POST ... 150 | */ 151 | method:'OPTIONS'|'GET'|'HEAD'|'POST'|'PUT'|'DELETE'|'TRACE'|'CONNECT'; 152 | 153 | /** 154 | * 请求数据 155 | * reqeust data 156 | * * **data 数据说明:** 157 | * 158 | * 最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String 。转换规则如下: 159 | * 160 | * * 对于 `GET` 方法的数据,会将数据转换成 query string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...) 161 | * * 对于 `POST` 方法且 `header['content-type']` 为 `application/json` 的数据,会对数据进行 JSON 序列化 162 | * * 对于 `POST` 方法且 `header['content-type']` 为 `application/x-www-form-urlencoded` 的数据,会将数据转换成 query string (encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...) 163 | */ 164 | data?: any; 165 | 166 | /** 167 | * 取消操作的 CancelToken 168 | */ 169 | cancelToken?: CancelToken; 170 | 171 | /** 172 | * 接收到响应头回调 173 | */ 174 | onHeadersReceived?: TwxTask['onHeadersReceived']; 175 | 176 | /** 177 | * 请求的根目录 178 | * Base URL for request 179 | */ 180 | baseURL?: string; 181 | 182 | /** 183 | * 自定义头 184 | * user defined headers 185 | */ 186 | headers?: KeyBasicValuePair; 187 | 188 | /** 189 | * URL Path 190 | * the path parameters to be replace in path 191 | * Must be a plain object 192 | * @example 193 | * url = "/{ID}/status" 194 | * param = {ID: 12345} 195 | * request url will be /1234/status 196 | */ 197 | params?: KeyBasicValuePair, 198 | 199 | /** 200 | * 重试次数 默认重试1次 201 | * retry times when fail 202 | */ 203 | retry?: number; 204 | 205 | /** 206 | * response data type 207 | */ 208 | responseType?: "json" | "text" | "arraybuffer"; 209 | 210 | /** 211 | * 修改数据或者头;返回 wx.request参数 212 | * 异步返回promise 213 | * You may modify the data or headers object before it is sent. 214 | */ 215 | transformSend?: (options) => PromiseOrValue>; 216 | 217 | /** 218 | * 返回数据修改,返回值作为then的输入, throw exception 抛给catch 219 | * 异步返回Promise 220 | * allows changes to the response data to be made before it is passed to then/catch 221 | * @example `res=>res.data` 222 | */ 223 | transformResponse?: (res, options) => any|Promise; 224 | 225 | } 226 | ``` 227 | 228 | ### 单次请求特殊配置 229 | 230 | ```js 231 | /每次请求全部可配置参数 232 | //已设置的参数会覆盖默认参数 233 | //仅对此次请求生效 234 | REQUEST.request({ 235 | url: 'items', 236 | method: 'POST', 237 | data: {}, 238 | cancelToken: null, 239 | baseURL: 'https://qq.com/', 240 | headers: {}, 241 | params: null, 242 | retry: 3, 243 | responseType: "json", 244 | transformSend: transformRequestSendDefault, 245 | transformResponse: transformRequestResponseOkData 246 | }) 247 | 248 | // 快速请求配置参数 249 | REQUEST.post('items', {}, { 250 | //除了method,url和 data 不能设置其他均可设置 251 | cancelToken: null, 252 | baseURL: 'https://qq.com/', 253 | headers: {}, 254 | params: null, 255 | retry: 3, 256 | responseType: "json", 257 | transformSend: transformRequestSendDefault, 258 | transformResponse: transformRequestResponseOkData 259 | }) 260 | ``` 261 | 262 | ### 创建一个新的Http管理对象 263 | ```js 264 | // 重新创建一个Http 265 | const http = new Http({ 266 | //除了method,url,data 和cancelToken不能设置其他均可设置 267 | baseURL: 'https://qq.com/', 268 | headers: {}, 269 | params: null, 270 | retry: 3, 271 | responseType: "json", 272 | transformSend: transformRequestSendDefault, 273 | transformResponse: transformRequestResponseOkData 274 | }) 275 | ``` 276 | 277 | ## LifeCircle 278 | 279 | ![Request Life Circle](https://user-images.githubusercontent.com/6290356/47618036-485c5780-db09-11e8-8db8-57d106883607.png) 280 | 281 | 282 | ## references 283 | 284 | * API inspired by 285 | -------------------------------------------------------------------------------- /request/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-request", 3 | "version": "5.3.0", 4 | "description": "A better Request package for MiniProgram API", 5 | "keywords": [ 6 | "miniprogram", 7 | "wx.request", 8 | "小程序", 9 | "请求" 10 | ], 11 | "homepage": "https://miniprogram-network.newfuture.cc/request/", 12 | "bugs": { 13 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "NewFuture", 21 | "files": [ 22 | "dist/", 23 | "src/", 24 | "es/" 25 | ], 26 | "main": "dist/index.js", 27 | "module": "es/index.js", 28 | "scripts": { 29 | "build": "npm run build:es5 && npm run build:es6", 30 | "build:es5": "tsc", 31 | "build:es6": "tsc -m es6 --target es6 --outDir es", 32 | "clean": "rimraf -rf es dist types ", 33 | "lint": "tslint -p . -c ../tslint.json", 34 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 35 | "test": "echo \"Error: no test specified\" && exit 1" 36 | }, 37 | "dependencies": { 38 | "miniprogram-network-life-cycle": "^5.3.0-alpha.0", 39 | "miniprogram-network-utils": "^5.3.0-alpha.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /request/src/http.ts: -------------------------------------------------------------------------------- 1 | import { BaseConfiguration, ExtraConfiguration, LifeCycle, SuccessParam } from 'miniprogram-network-life-cycle'; 2 | import { GeneralCallbackResult, ParamsType } from 'miniprogram-network-utils'; 3 | import { transformRequestSendDefault } from './transform'; 4 | 5 | /** 6 | * 小程序HTTP 请求生命周期封装 7 | * @example 8 | * `const http = new Http({ baseURL: 'https://api.newfuture.cc/', retry: 3 });` 9 | * @template TExt 扩展参数属性类型 10 | */ 11 | export class Http< 12 | TExt extends {} = {} 13 | > 14 | extends LifeCycle< 15 | TExt & wx.RequestOption, 16 | wx.RequestTask, 17 | RequestInit, 18 | FullRequestOption 19 | > { 20 | /** 21 | * 新建 Http实列 22 | * @param config 全局默认配置 23 | * @param request 请求处理方法,默认使用请求队列处理 24 | * @param listeners 请求事件监听 25 | */ 26 | public constructor( 27 | config?: RequestInit, 28 | request?: (o: TExt & wx.RequestOption) => wx.RequestTask, 29 | listeners?: Http['Listeners'] 30 | ) { 31 | super( 32 | // tslint:disable-next-line: no-use-before-declare 33 | request || wx.request, 34 | // tslint:disable-next-line: no-object-literal-type-assertion 35 | config || { transformSend: transformRequestSendDefault } as RequestInit, 36 | listeners 37 | ); 38 | } 39 | 40 | /** 41 | * Object 参数发起请求 42 | * @param options 每个请求的全部配置信息,未设置内容使用默认全局配置 43 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 44 | * @template TData request请求参数的格式类型,默认 any 45 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 46 | */ 47 | public request< 48 | TReturn = SuccessParam, 49 | TData extends BaseData = BaseData, 50 | TParams = ParamsType, 51 | >(options: RequestOption): Promise; 52 | /** 53 | * 发送一个 request请求 54 | * @param method 操作方法,和小程序一致 55 | * @param action 请求操作URL,支持{name}格式参数 56 | * @param data 可转未query string 57 | * @param config 可覆盖默认配置 58 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 59 | * @template TData request请求参数的格式类型,默认 any 60 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 61 | */ 62 | public request< 63 | TReturn = SuccessParam, 64 | TData extends BaseData = BaseData, 65 | TParams = ParamsType, 66 | >( 67 | method: NonNullable, 68 | action: string, 69 | data?: TData, 70 | config?: RequestConfig 71 | ): Promise; 72 | public request>(): Promise { 73 | const argNum = arguments.length; 74 | // tslint:disable-next-line: no-unsafe-any 75 | const options: FullRequestOption = argNum === 1 ? arguments[0] : (arguments[3] || {}); 76 | if (argNum > 1) { 77 | options.method = arguments[0] as FullRequestOption['method']; 78 | options.url = arguments[1] as string; 79 | if (argNum > 2) { 80 | // tslint:disable-next-line: no-unsafe-any 81 | options.data = arguments[2]; 82 | } 83 | } 84 | return this.process(options); 85 | } 86 | 87 | /** 88 | * GET 操作 89 | * @param action 请求操作URL,支持{name}格式参数 90 | * @param data 可转为query string 91 | * @param config 可覆盖默认配置 92 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 93 | * @template TData get query data请求参数的格式类型,默认 any 94 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 95 | */ 96 | // tslint:disable-next-line: no-reserved-keywords 97 | public get< 98 | TReturn = SuccessParam, 99 | TData extends BaseData = BaseData, 100 | TParams = ParamsType, 101 | >( 102 | action: string, 103 | data?: TData, 104 | config?: RequestConfig 105 | ): Promise { 106 | return this.request('GET', action, data, config); 107 | } 108 | 109 | /** 110 | * POST 操作 111 | * @param action 请求操作URL,支持{name}格式参数 112 | * @param data 操作数据,默认会以json方式上传 113 | * @param config 可覆盖默认配置 114 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 115 | * @template TData post data参数格式类型,默认 any 116 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 117 | */ 118 | public post< 119 | TReturn = SuccessParam, 120 | TData extends BaseData = BaseData, 121 | TParams = ParamsType, 122 | >( 123 | action: string, 124 | data?: TData, 125 | config?: RequestConfig 126 | ): Promise { 127 | return this.request('POST', action, data, config); 128 | } 129 | 130 | /** 131 | * PUT 操作 132 | * @param action 请求操作URL,支持{name}格式参数 133 | * @param data 操作数据,默认会以json方式上传 134 | * @param config 可覆盖默认配置 135 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 136 | * @template TData post data数据格式类型,默认 any 137 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 138 | */ 139 | public put< 140 | TReturn = SuccessParam, 141 | TData extends BaseData = BaseData, 142 | TParams = ParamsType, 143 | >( 144 | action: string, 145 | data?: TData, 146 | config?: RequestConfig 147 | ): Promise { 148 | return this.request('PUT', action, data, config); 149 | } 150 | 151 | /** 152 | * DELETE 操作 153 | * @param action 请求操作URL,支持{name}格式参数 154 | * @param data 可转为query string 155 | * @param config 可覆盖默认配置 156 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 157 | * @template TData put query data参数格式类型,默认 any 158 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 159 | */ 160 | // tslint:disable-next-line: no-reserved-keywords 161 | public delete< 162 | TReturn = SuccessParam, 163 | TData extends BaseData = BaseData, 164 | TParams = ParamsType, 165 | >( 166 | action: string, 167 | data?: TData, 168 | config?: RequestConfig 169 | ): Promise { 170 | return this.request('DELETE', action, data, config); 171 | } 172 | 173 | /** 174 | * HEAD 操作 175 | * @param action 请求操作URL,支持{name}格式参数 176 | * @param data 可转为query string 177 | * @param config 可覆盖默认配置 178 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 179 | * @template TData head query data参数格式类型,默认 any 180 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 181 | */ 182 | public head< 183 | TReturn = SuccessParam, 184 | TData extends BaseData = BaseData, 185 | TParams = ParamsType, 186 | >( 187 | action: string, 188 | data?: TData, 189 | config?: RequestConfig 190 | ): Promise { 191 | return this.request('HEAD', action, data, config); 192 | } 193 | 194 | /** 195 | * Patch 操作 196 | * 由于小程序不支持PATCH 方法 197 | * 采用X-HTTP-Method-Override兼容处理,需要服务器端支持 198 | * @param action 请求操作URL,支持{name}格式参数 199 | * @param data 操作数据,默认会以json方式上传 200 | * @param config 可覆盖默认配置 201 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 202 | * @template TData patch data参数格式类型,默认 any 203 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 204 | */ 205 | public patch< 206 | TReturn = SuccessParam, 207 | TData extends BaseData = BaseData, 208 | TParams = ParamsType, 209 | >( 210 | action: string, 211 | data?: TData, 212 | config?: RequestConfig 213 | ): Promise { 214 | if (!config) { 215 | // tslint:disable-next-line: no-parameter-reassignment 216 | config = { 217 | headers: { 'X-HTTP-Method-Override': 'PATCH' } 218 | } as unknown as RequestConfig; 219 | } else if (!config.headers) { 220 | config.headers = { 'X-HTTP-Method-Override': 'PATCH' }; 221 | } else { 222 | config.headers['X-HTTP-Method-Override'] = 'PATCH'; 223 | } 224 | return this.request('POST', action, data, config); 225 | } 226 | } 227 | 228 | /** 229 | * Request Data支持的全部数据格式 230 | */ 231 | type BaseData = string | object | ArrayBuffer | undefined; 232 | /** 233 | * 构造函数 默认配置信息 234 | * (创建Request的配置信息) 235 | */ 236 | export interface RequestInit 237 | extends BaseConfiguration< 238 | FullRequestOption, 239 | T & wx.RequestOption, 240 | TReturn> { 241 | /** 242 | * response data type 243 | */ 244 | responseType?: 'json' | 'text' | 'arraybuffer'; 245 | } 246 | 247 | /** 248 | * 单个请求的额外配置信息 249 | * @template TParams 参数类型 250 | * @template TExt 扩展配置 251 | */ 252 | export type RequestConfig< 253 | TParams = ParamsType, 254 | TExt extends {} = {}, 255 | TReturn = HttpResponse, 256 | > = Partial & Partial & ExtraConfiguration> & { 257 | /** 258 | * 路径参数 259 | * URL Path Params 260 | * the path parameters to be replace in path 261 | * Must be a plain `object` or `array` 262 | * @example 263 | * url = "/{ID}/status" 264 | * param = {ID: 12345} 265 | * request url will be /1234/status 266 | */ 267 | params?: TParams; 268 | }; 269 | 270 | interface UniqueRequestOption { 271 | 272 | /** 273 | * 请求的地址 274 | */ 275 | url: string; 276 | 277 | /** 278 | * 请求方法 279 | * HTTP request mthod: GET POST ... 280 | */ 281 | method?: wx.RequestOption['method']; 282 | 283 | /** 284 | * 请求数据 285 | * reqeust data 286 | * * **data 数据说明:** 287 | * 288 | * 最终发送给服务器的数据是 String 类型,如果传入的 data 不是 String 类型,会被转换成 String 。转换规则如下: 289 | * 290 | * * 对于 `GET` 方法的数据,会将数据转换成 query string(encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...) 291 | * * 对于 `POST` 方法且 `header['content-type']` 为 `application/json` 的数据,会对数据进行 JSON 序列化 292 | * * 对于 `POST` 方法且 `header['content-type']` 为 `application/x-www-form-urlencoded` 的数据,会将数据转换成 query string 293 | * (encodeURIComponent(k)=encodeURIComponent(v)&encodeURIComponent(k)=encodeURIComponent(v)...) 294 | */ 295 | data?: TData; 296 | 297 | // onProgress?: wx.DownloadTask['offProgressUpdate'] 298 | } 299 | 300 | /** 301 | * 每个HTTP Request请求的全部可配置信息 302 | * @template TData 数据data的类型限制 303 | * @template TParams 参数类型 304 | * @template TExt 扩展信息 305 | */ 306 | export type RequestOption< 307 | TData extends BaseData = BaseData, 308 | TParams = ParamsType, 309 | TExt extends {} = {}, 310 | TReturn = HttpResponse 311 | > = RequestConfig & UniqueRequestOption; 312 | /** 313 | * 发送一个请求的完整可配置信息 314 | * @template TExtend 扩展信息 315 | * @template TData 数据data的类型限制 316 | */ 317 | export interface FullRequestOption< 318 | TExtend extends {} = {}, 319 | TData extends BaseData = BaseData, 320 | TReturn extends any = any, 321 | > extends 322 | RequestInit, 323 | ExtraConfiguration, 324 | UniqueRequestOption { } 325 | 326 | export declare namespace wx { 327 | function request(option: RequestOption): RequestTask; 328 | interface RequestOption { 329 | /** 开发者服务器接口地址 */ 330 | url: string; 331 | /** 响应的数据类型 332 | * 333 | * 可选值: 334 | * - 'text': 响应的数据为文本; 335 | * - 'arraybuffer': 响应的数据为 ArrayBuffer; 336 | * 337 | * 最低基础库: `1.7.0` 338 | */ 339 | responseType?: 'text' | 'arraybuffer'; 340 | /** 返回的数据格式 341 | * 342 | * 可选值: 343 | * - 'json': 返回的数据为 JSON,返回后会对返回的数据进行一次 JSON.parse; 344 | * - '其他': 不对返回的内容进行 JSON.parse; 345 | */ 346 | dataType?: 'json' | '其他'; 347 | /** HTTP 请求方法 348 | * 349 | * 可选值: 350 | * - 'OPTIONS': HTTP 请求 OPTIONS; 351 | * - 'GET': HTTP 请求 GET; 352 | * - 'HEAD': HTTP 请求 HEAD; 353 | * - 'POST': HTTP 请求 POST; 354 | * - 'PUT': HTTP 请求 PUT; 355 | * - 'DELETE': HTTP 请求 DELETE; 356 | * - 'TRACE': HTTP 请求 TRACE; 357 | * - 'CONNECT': HTTP 请求 CONNECT; 358 | */ 359 | method?: 360 | | 'OPTIONS' 361 | | 'GET' 362 | | 'HEAD' 363 | | 'POST' 364 | | 'PUT' 365 | | 'DELETE' 366 | | 'TRACE' 367 | | 'CONNECT'; 368 | /** 设置请求的 header,header 中不能设置 Referer。 369 | * 370 | * `content-type` 默认为 `application/json 371 | */ 372 | header?: object; 373 | /** 374 | * 请求的参数 375 | */ 376 | data?: string | object | ArrayBuffer; 377 | /** 378 | * 开启 http2 379 | */ 380 | enableHttp2?: boolean; 381 | /** 382 | * 开启 quic 383 | */ 384 | enableQuic?: boolean; 385 | /** 386 | * 开启 缓存 387 | */ 388 | enableCache?: boolean; 389 | /** 390 | * 开启 HttpDNS 391 | */ 392 | enableHttpDNS?: boolean; 393 | /** 394 | * HttpDNS 服务商 Id。 HttpDNS 用法详见 移动解析HttpDNS 395 | */ 396 | httpDNSServiceId?: string | boolean; 397 | /** 398 | * 开启 transfer-encoding chunked。 399 | */ 400 | enableChunked?: boolean; 401 | /** 402 | * 接口调用结束的回调函数(调用成功、失败都会执行) 403 | */ 404 | complete?(res: { errMsg: string }): void; 405 | /** 406 | * 接口调用失败的回调函数 407 | */ 408 | fail?(res: { errMsg: string }): void; 409 | /** 410 | * 接口调用成功的回调函数 411 | */ 412 | success?(result: HttpResponse): void; 413 | } 414 | interface RequestTask { 415 | /** [RequestTask.abort()](RequestTask.abort.md) 416 | * 417 | * 中断请求任务 418 | * 419 | * 最低基础库: `1.4.0` 420 | */ 421 | abort(): void; 422 | 423 | /** [RequestTask.onHeadersReceived(function callback)](RequestTask.onHeadersReceived.md) 424 | * 425 | * 监听HTTP Response Header 事件,会比请求完成事件更早 426 | * 427 | * 最低基础库: `2.1.0` 428 | */ 429 | onHeadersReceived( 430 | /** HTTP Response Header 事件的回调函数 */ 431 | callback: (result?: { header: object }) => void 432 | ): void; 433 | } 434 | } 435 | 436 | export interface HttpResponse extends GeneralCallbackResult { 437 | /** 开发者服务器返回的 HTTP Response Header 438 | * 439 | * 最低基础库: `1.2.0` 440 | */ 441 | header: object; 442 | /** 开发者服务器返回的 HTTP 状态码 */ 443 | statusCode: number; 444 | /** 开发者服务器返回的数据 */ 445 | data: string | object | ArrayBuffer; 446 | /** 447 | * cookie信息2.4.2以上版本有 448 | * 非正式支持 449 | */ 450 | cookies?: ({ 451 | domain: string; 452 | httpOnly: boolean; 453 | name: string; 454 | path: string; 455 | value: string; 456 | } | string)[]; 457 | } 458 | -------------------------------------------------------------------------------- /request/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Http } from './http'; 2 | 3 | export { CancelToken, ICancelTokenSource } from 'miniprogram-network-life-cycle'; 4 | export { 5 | Http, 6 | RequestConfig, 7 | RequestInit, 8 | RequestOption, 9 | HttpResponse 10 | } from './http'; 11 | export { 12 | RequestParams, 13 | transformRequestResponseOkData, 14 | transformRequestSendDefault 15 | } from './transform'; 16 | 17 | /** 18 | * 预定义全局 REQUEST 对象 19 | */ 20 | // tslint:disable-next-line: export-name 21 | export const REQUEST = /*#__PURE__*/ new Http(); 22 | -------------------------------------------------------------------------------- /request/src/transform.ts: -------------------------------------------------------------------------------- 1 | // import { RequestData, RequestOptions } from './configuration'; 2 | import { buildParams, getCommonOptions, Omit } from 'miniprogram-network-utils'; 3 | import { FullRequestOption, HttpResponse, wx } from './http'; 4 | 5 | /** 6 | * 微信请求参数 (不包含回调函数) 7 | */ 8 | export type RequestParams = Omit; 9 | 10 | /** 11 | * 构建请求参数 12 | * @param data - 完整配置参数 13 | */ 14 | export function transformRequestSendDefault< 15 | T extends {} = {}, 16 | // TwxParams extends RequestParams = RequestParams 17 | >(data: FullRequestOption): RequestParams { 18 | const wxParam: RequestParams = { 19 | url: buildParams(data.url, data.params, data.baseURL), 20 | header: data.headers 21 | }; 22 | if (data.responseType === 'arraybuffer') { 23 | wxParam.responseType = 'arraybuffer'; 24 | } else if (data.responseType === 'json') { 25 | wxParam.dataType = 'json'; 26 | } 27 | return getCommonOptions(wxParam, data, ['data', 'method', 'enableCache', 'enableChunked', 'enableHttp2', 'enableHttpDNS', 'enableQuic', 'httpDNSServiceId']); 28 | } 29 | 30 | /** 31 | * 返回请求成功的响应数据data部分 32 | * statusCode 2xx 操作成功仅返回data数据 33 | * 否则抛出错误(rejected) 34 | * @param res - 返回结果 35 | * @param config - 完整配置参数 36 | */ 37 | export function transformRequestResponseOkData(res: HttpResponse, config: FullRequestOption): T { 38 | if (res.statusCode >= 200 && res.statusCode < 300) { 39 | return res.data as any as T; 40 | } 41 | throw res; 42 | } 43 | -------------------------------------------------------------------------------- /request/test/cancel.ts: -------------------------------------------------------------------------------- 1 | import { Request, CancelToken } from '../index'; 2 | // 创建一个 tokensource 3 | const source = CancelToken.source(); 4 | 5 | Request.get('items', { skip: 100 }, { 6 | // 配置 cancelToken 7 | cancelToken: source.token 8 | }); 9 | 10 | // 需要取消操作时 11 | source.cancel('cancel the reqeust'); -------------------------------------------------------------------------------- /request/test/config.ts: -------------------------------------------------------------------------------- 1 | import { REQUEST, Http, transformRequestSendDefault, transformRequestResponseOkData } from '../src/index'; 2 | 3 | //每次请求全部可配置参数 4 | //已设置的参数会覆盖默认参数 5 | //仅对此次请求生效 6 | REQUEST.request({ 7 | url: 'items', 8 | method: 'POST', 9 | data: {}, 10 | /** 11 | * 取消操作的 CancelToken 12 | */ 13 | cancelToken: undefined, 14 | /** 15 | * 请求的根目录 16 | * Base URL for request 17 | */ 18 | baseURL: 'https://qq.com/', 19 | 20 | /** 21 | * 自定义头 22 | * user defined headers 23 | */ 24 | headers: {}, 25 | 26 | /** 27 | * URL Path 28 | * the path parameters to be replace in path 29 | * Must be a plain object 30 | */ 31 | params: {}, 32 | 33 | /** 34 | * 重试次数 35 | * retry times when fail 36 | */ 37 | retry: 3, 38 | 39 | /** 40 | * response data type 41 | */ 42 | responseType: "json", 43 | 44 | /** 45 | * 修改数据或者头;返回 wx.request参数 46 | * 异步返回promise 47 | * You may modify the data or headers object before it is sent. 48 | */ 49 | transformSend: transformRequestSendDefault, 50 | /** 51 | * 返回数据修改,返回值作为then的输入, throw exception 抛给catch 52 | * 异步返回Promise 53 | * allows changes to the response data to be made before it is passed to then/catch 54 | * @example `res=>res.data` 55 | */ 56 | transformResponse: transformRequestResponseOkData 57 | }) 58 | 59 | // 快速请求配置参数 60 | REQUEST.post('items', {}, { 61 | //除了method,url和 data 不能设置其他均可设置 62 | cancelToken: undefined, 63 | baseURL: 'https://qq.com/', 64 | headers: {}, 65 | params: undefined, 66 | retry: 3, 67 | responseType: "json", 68 | transformSend: transformRequestSendDefault, 69 | transformResponse: transformRequestResponseOkData, 70 | }) 71 | 72 | 73 | // 重新创建一个Http 74 | const http = new Http({ 75 | //除了method,url,data 和cancelToken不能设置其他均可设置 76 | baseURL: 'https://qq.com/', 77 | headers: {}, 78 | params: undefined, 79 | retry: 3, 80 | responseType: "json", 81 | transformSend(options) { 82 | return transformRequestSendDefault(options); 83 | }, 84 | transformResponse: transformRequestResponseOkData 85 | }) 86 | 87 | http.get('test');//将采用默认配置 -------------------------------------------------------------------------------- /request/test/type.ts: -------------------------------------------------------------------------------- 1 | //TS 类型推断 2 | import { Request } from '../index'; 3 | 4 | interface Item { 5 | id: number, 6 | name: string 7 | } 8 | 9 | // 泛型 then的参数值类型为 Item[] 10 | Request.get('/items') 11 | .then(list => list.forEach(i => console.log(i.id + i.name))) 12 | 13 | -------------------------------------------------------------------------------- /request/test/url.ts: -------------------------------------------------------------------------------- 1 | import {Request} from '../index'; 2 | 3 | // 设置全局配置,设置一次全部生效 4 | // 设置请求根地址,可选 5 | Request.Defaults.baseURL = "https://minipgrogram.newfuture.cc/" 6 | // 添加监听时间 可选 7 | Request.Listeners.onResponse.push(console.log); // 记录每次响应 8 | 9 | // @ts-ignore 10 | Request.get('items').then(applyFunction).catch(err=>wx.showToast({title:'数据拉取失败'})); 11 | 12 | // URL 参数 13 | Request.get('items/{id}',{show_detail:false},{params:{id:12345}}) 14 | // GET /items/12345?show_detail=false 15 | -------------------------------------------------------------------------------- /request/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015", 7 | "es2015.promise" 8 | ] 9 | }, 10 | "files": [ 11 | "src/index.ts", 12 | ], 13 | "exclude": [ 14 | "node_modules", 15 | "types", 16 | "dist" 17 | ] 18 | } -------------------------------------------------------------------------------- /socket/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-socket 2 | 3 | designing -------------------------------------------------------------------------------- /socket/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-socket", 3 | "version": "0.0.1-alpha", 4 | "description": "A better Request package for MiniProgram API", 5 | "main": "dist/index.js", 6 | "typings": "dist/index.d.ts", 7 | "files": [ 8 | "dist/**" 9 | ], 10 | "scripts": { 11 | "build": "tsc", 12 | "test": "echo \"Error: no test specified\" && exit 1" 13 | }, 14 | "keywords": [ 15 | "miniprogram" 16 | ], 17 | "author": "NewFuture", 18 | "license": "Apache-2.0", 19 | "repository": { 20 | "type": "git", 21 | "url": "git+https://github.com/NewFuture/miniprogram.git" 22 | }, 23 | "dependencies": { 24 | "miniprogram-network-utils": "^3.2.1" 25 | }, 26 | "bugs": { 27 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 28 | }, 29 | "homepage": "https://github.com/NewFuture/miniprogram-network/" 30 | } 31 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "sourceMap": true, 5 | "removeComments": false, 6 | "strict": true, 7 | "noImplicitAny": true, 8 | "noImplicitReturns": true, 9 | "moduleResolution": "node", 10 | "alwaysStrict": true, 11 | "forceConsistentCasingInFileNames": true, 12 | "declarationMap": true, 13 | "listEmittedFiles": true, 14 | "declaration": true, 15 | "esModuleInterop": false, 16 | "noEmitHelpers": false, // 注入 helper function 17 | // "importHelpers": true, 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint-microsoft-contrib", 3 | "rulesDirectory": [ 4 | "node_modules/tslint-microsoft-contrib" 5 | ], 6 | "defaultSeverity": "warn", 7 | "jsRules": {}, 8 | "rules": { 9 | "linebreak-style": [ 10 | false, 11 | "LF" 12 | ], 13 | "file-name-casing": [ 14 | true, 15 | "kebab-case" 16 | ], 17 | "function-name": [ 18 | true, 19 | { 20 | "method-regex": "^[a-z][\\w\\d]+$", 21 | "private-method-regex": "^_[a-z][\\w\\d]+$", 22 | "protected-method-regex": "^[a-z][\\w\\d]+$", 23 | "static-method-regex": "^[a-z][\\w\\d]+$", 24 | "function-regex": "^[a-z][\\w\\d]+$" 25 | } 26 | ], 27 | "export-name": [ 28 | false, 29 | { 30 | "ignore-case": true, 31 | "allow": [ 32 | "-" 33 | ] 34 | } 35 | ], 36 | "max-func-body-length":[ true,{ "func-body-length": 50 }], 37 | "no-any": false, 38 | "interface-name": false, 39 | "typedef": false, 40 | "no-single-line-block-comment": false, 41 | "no-relative-imports": false, 42 | "no-increment-decrement": false, 43 | "prefer-type-cast": false, 44 | "no-non-null-assertion": false, 45 | "newline-before-return": false, 46 | "strict-boolean-expressions": false, 47 | "promise-function-async": false, 48 | "use-named-parameter": false 49 | }, 50 | "linterOptions": { 51 | "exclude": [ 52 | "types/*.d.ts" 53 | ] 54 | } 55 | } -------------------------------------------------------------------------------- /uploader/README.md: -------------------------------------------------------------------------------- 1 | # miniprogram-uploader [![npm version](https://badge.fury.io/js/miniprogram-uploader.svg)](https://npmjs.com/package/miniprogram-uploader) 2 | 3 | > An axios API like `Upload` package for MiniProgram 4 | > 5 | > 小程序上传封装 6 | > 小程序网络库[miniprogram-network](https://github.com/NewFuture/miniprogram-network) 核心库之一 7 | 8 | 9 | ## API 10 | 11 | ### methods: 12 | 13 | * `upload(options: UploadOption): Promise`; 14 | * `upload(filePath: string, name: string, url?: string, options?): Promise`; 15 | 16 | ### options 17 | 18 | * [x] `filePath` 文件路径 **required** (_只能请求时设置for single request_) 19 | * [x] `name` 上传文件名 **required** (_只能请求时设置for single request_) 20 | * [x] `data` 额外数据 (_只能请求时设置for single request_) 21 | * [x] `cancelToken` 取消 (_只能请求时设置for single request_) 22 | * [x] `onProgressUpdate` 下载进度响应 (_只能请求时设置for single request_) 23 | * [x] `onHeadersReceived` 接收头响应 (_只能请求时设置for single request_) 24 | * [x] `timeout` 自定义超时时间ms (_只能请求时设置for single request_) 25 | * [x] `url` 上传地址 26 | * [x] `headers` 请求头 27 | * [x] `params` URL参数 28 | * [x] `baseURL` 根URL 29 | * [x] `retry` 重试次数 30 | * [x] `timestamp` 是否记录发送和响应时间戳 31 | * [x] `transformSend` 输入转换函数 32 | * [x] `transformResponse` 输出转换函数 33 | 34 | ### Global Listeners 35 | 36 | * [x] `onSend` (before request data send & after request data transformed) 37 | * [x] `onResponse` (after request response data transformed) 38 | * [x] `onRejected` (before `catch` of Promise) 39 | * [x] `onAbort` 40 | * [x] `onComplete` 41 | 42 | ## Usage 43 | 44 | ### install 45 | ``` 46 | npm i miniprogram-uploader 47 | ``` 48 | 49 | ### quick start 50 | 51 | ```js 52 | import {UPLOAD} from 'miniprogram-uploader'; 53 | UPLOAD.upload(localPath,'file','https://upload.site/file') 54 | .then(console.log) // 返回数据 55 | .catch(err=>wx.showToast({title:'下载失败'})); 56 | ``` 57 | 58 | 59 | ### 直接返回保存位置 60 | 61 | ```js 62 | import {UPLOAD,transformUploadResponseOkData} from 'miniprogram-uploader'; 63 | // 根据状态码,直接返回保存地址 64 | //默认配置全局有效 65 | UPLOAD.Defaults.transformResponse=transformUploadResponseOkData; 66 | 67 | //js 68 | UPLOAD.upload(localPath,'file','https://upload.site/file').then(console.log);//打印data string 69 | //TS 70 | UPLOAD.upload<{url:string}>(localPath,'file','https://upload.site/file') 71 | .then(data=>{ 72 | console.log(data)//打印数据object {url:'xxx'} 73 | }) 74 | 75 | //返回完整数据 对当前下载有效 76 | UPLOAD.upload(url:'item/1.jpg',null,{transformResponse:(res,o)=>res}) 77 | .then(console.log) //打印 返回的Object {errMsg:'xx',data:"{url:'xxx'}"} 78 | ``` 79 | 80 | 81 | 82 | ### CancelToken (abort) 83 | 84 | 可通过cancel token 方式取消请求 85 | ```js 86 | import { UPLOAD, CancelToken } from 'miniprogram-uploader'; 87 | 88 | // 创建一个 tokensource 89 | const source = CancelToken.source(); 90 | 91 | UPLOAD.upload({ 92 | filePath:localPath, 93 | file:'tempfile', 94 | // 配置 cancelToken 95 | cancelToken: source.token 96 | }); 97 | 98 | // 需要取消操作时 99 | source.cancel('canceled'); 100 | ``` 101 | -------------------------------------------------------------------------------- /uploader/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "miniprogram-uploader", 3 | "version": "5.3.0-alpha.0", 4 | "description": "miniprogram upload package", 5 | "keywords": [ 6 | "miniprogram", 7 | "wx.uploadFile", 8 | "小程序", 9 | "上传" 10 | ], 11 | "homepage": "https://miniprogram-network.newfuture.cc/uploader/", 12 | "bugs": { 13 | "url": "https://github.com/NewFuture/miniprogram-network/issues" 14 | }, 15 | "repository": { 16 | "type": "git", 17 | "url": "git+https://github.com/NewFuture/miniprogram-network.git" 18 | }, 19 | "license": "Apache-2.0", 20 | "author": "NewFuture", 21 | "files": [ 22 | "dist/", 23 | "src/", 24 | "es/" 25 | ], 26 | "main": "dist/index.js", 27 | "module": "es/index.js", 28 | "scripts": { 29 | "build": "npm run build:es5 && npm run build:es6", 30 | "build:es5": "tsc", 31 | "build:es6": "tsc -m es6 --target es6 --outDir es", 32 | "clean": "rimraf -rf es dist types ", 33 | "lint": "tslint -p . -c ../tslint.json", 34 | "lint:fix": "tslint --fix -p . -c ../tslint.json", 35 | "test": "echo \"Error: no test specified\" && exit 1" 36 | }, 37 | "dependencies": { 38 | "miniprogram-network-life-cycle": "^5.3.0-alpha.0", 39 | "miniprogram-network-utils": "^5.3.0-alpha.0" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /uploader/src/index.ts: -------------------------------------------------------------------------------- 1 | import { Uploader } from './uploader'; 2 | 3 | export { 4 | CancelToken, 5 | ICancelTokenSource 6 | } from 'miniprogram-network-life-cycle'; 7 | export { 8 | transformUploadResponseOkData, 9 | transformUploadSendDefault, 10 | UploadParams 11 | } from './transform'; 12 | export { Uploader, UploadInit, UploadOption, UploaderResponse } from './uploader'; 13 | /** 14 | * 预定义全局 Upload 对象 15 | */ 16 | // tslint:disable-next-line: export-name 17 | export const UPLOAD = /*#__PURE__*/ new Uploader(); 18 | -------------------------------------------------------------------------------- /uploader/src/transform.ts: -------------------------------------------------------------------------------- 1 | import { buildParams, getCommonOptions, Omit } from 'miniprogram-network-utils'; 2 | import { UploaderResponse, UploadOption, wx } from './uploader'; 3 | 4 | /** 5 | * 微信请求参数 (不包含回调函数) 6 | */ 7 | export type UploadParams = Omit; 8 | 9 | /** 10 | * 构建请求参数 11 | * baseUrl和dataUrl不同时为空 12 | * @param data - 完整参数 13 | */ 14 | export function transformUploadSendDefault(data: UploadOption): UploadParams { 15 | return getCommonOptions>( 16 | { 17 | url: buildParams(data.url || '', data.params, data.baseURL), 18 | formData: data.data, 19 | header: data.headers 20 | }, 21 | data, 22 | ['filePath', 'name']) as UploadParams; 23 | } 24 | 25 | /** 26 | * 根据错误码处理数据(会尝试JSON.parse) 27 | * statusCode 2xx 操作成功仅返回data数据 28 | * 否则抛出错误(rejected) 29 | * @param res - 返回结果 30 | * @param options - 全部配置 31 | * @returns 反序列化对象 32 | */ 33 | export function transformUploadResponseOkData(res: UploaderResponse, options: UploadOption): T { 34 | if (res.statusCode >= 200 && res.statusCode < 300) { 35 | if (typeof res.data === 'string') { 36 | try { 37 | return JSON.parse(res.data) as T; 38 | } catch { 39 | // empty 40 | } 41 | } 42 | return res.data as any as T; 43 | } 44 | 45 | throw res; 46 | } 47 | -------------------------------------------------------------------------------- /uploader/src/uploader.ts: -------------------------------------------------------------------------------- 1 | 2 | import { BaseConfiguration, ExtraConfiguration, LifeCycle, SuccessParam } from 'miniprogram-network-life-cycle'; 3 | import { GeneralCallbackResult, ParamsType } from 'miniprogram-network-utils'; 4 | import { transformUploadSendDefault } from './transform'; 5 | 6 | /** 7 | * 默认配置信息 8 | */ 9 | export interface UploadInit extends BaseConfiguration { 10 | /** 11 | * 上传API 12 | */ 13 | url?: string; 14 | /** 15 | * 上传文件名字段 16 | */ 17 | name?: string; 18 | } 19 | /** 20 | * 下载的全部配置项 21 | * @template TData patch data参数格式类型,默认 any 22 | */ 23 | interface BaseUploadOption< 24 | TData extends object = NonNullable, 25 | > { 26 | 27 | /** 28 | * 本地文件路径 29 | */ 30 | filePath: string; 31 | 32 | /** 33 | * 上传服务器API地址 34 | * 如果URL以`http://`或者`https://`开头将忽略 baseURL 35 | */ 36 | url?: NonNullable; 37 | 38 | /** 39 | * 上传文件名字段 40 | */ 41 | name?: string; 42 | 43 | /** 44 | * 请求附加的 form Data 45 | */ 46 | data?: TData; 47 | } 48 | 49 | /** 50 | * 上传的全部配置信息 51 | */ 52 | export interface FullUploadOption extends UploadInit, ExtraConfiguration, BaseUploadOption { 53 | /** 54 | * 下载进度回调函数 55 | */ 56 | onProgressUpdate?: wx.UploadTaskOnProgressUpdateCallback; 57 | } 58 | 59 | /** 60 | * 每个传的额外配置 61 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 62 | */ 63 | type UploadConfig< 64 | TParams = ParamsType, 65 | TReturn = any, 66 | > = Partial & ExtraConfiguration> & { 67 | /** 68 | * 路径参数 69 | * URL Path Params 70 | * the path parameters to be replace in path 71 | * Must be a plain `object` or `array` 72 | * @example 73 | * url = "/{ID}/status" 74 | * param = {ID: 12345} 75 | * request url will be /1234/status 76 | */ 77 | params?: TParams; 78 | 79 | /** 80 | * 下载进度回调函数 81 | */ 82 | onProgressUpdate?: wx.UploadTaskOnProgressUpdateCallback; 83 | }; 84 | 85 | /** 86 | * 单个上传的全部参数 87 | * @template TData patch data参数格式类型,默认 any 88 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 89 | */ 90 | export type UploadOption< 91 | TData extends object = object, 92 | TParams = ParamsType, 93 | TReturn = any, 94 | > = UploadConfig & BaseUploadOption; 95 | 96 | /** 97 | * 上传管理 98 | */ 99 | export class Uploader extends LifeCycle< 100 | wx.UploadFileOption, 101 | wx.UploadTask, 102 | UploadInit, 103 | FullUploadOption 104 | > { 105 | /** 106 | * 默认上传请求参数转换函数 107 | */ 108 | // protected readonly TransformSendDefault = transformUploadSendDefault; 109 | 110 | /** 111 | * 创建Upload管理 112 | * @param config 全局配置 113 | * @param uploader 操作函数,默认使用上传队列 114 | * @param listeners 上传事件监听通知 115 | */ 116 | constructor( 117 | config?: UploadInit, 118 | uploader?: (op: wx.UploadFileOption) => wx.UploadTask, 119 | listeners?: Uploader['Listeners'] 120 | ) { 121 | super( 122 | // tslint:disable-next-line: no-use-before-declare 123 | uploader || wx.uploadFile, 124 | config || { transformSend: transformUploadSendDefault }, 125 | listeners 126 | ); 127 | } 128 | 129 | /** 130 | * 自定义上传 131 | * @param options 全部配置信息:filePath,name,为必填字段 132 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 133 | * @template TData 上传 query data参数格式类型,默认 any 134 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 135 | */ 136 | public upload< 137 | TReturn = SuccessParam, 138 | TData extends object = object, 139 | TParams = ParamsType, 140 | >(options: UploadOption): Promise; 141 | /** 142 | * 快速上传文件 143 | * @param filePath 本地文件路径 144 | * @param name 文件名 145 | * @param url 上传地址可选 146 | * @param data 附加formData数据,可选 147 | * @param options 其他参数 148 | * @template TReturn Promise 返回的格式类型,默认返回微信原始返回数据格式 149 | * @template TData 上传 query data参数格式类型,默认 any 150 | * @template TParams 路径参数(如`/items/{id}`或者`/{0}/{1}`)的格式类型,默认 任意object或数组 151 | */ 152 | public upload< 153 | TReturn = SuccessParam, 154 | TData = object, 155 | TParams = ParamsType, 156 | >( 157 | filePath: string, 158 | name: string, 159 | url?: string, 160 | data?: TData, 161 | config?: UploadConfig 162 | ): Promise; 163 | 164 | public upload(): Promise { 165 | const argNum: number = arguments.length; 166 | const options: FullUploadOption = argNum === 1 ? arguments[0] as FullUploadOption : (arguments[4] as FullUploadOption || {}); 167 | if (argNum > 1) { 168 | options.filePath = arguments[0] as string; 169 | options.name = arguments[1] as string; 170 | options.url = arguments[2] as string; 171 | options.data = arguments[3] as object; 172 | } 173 | return this.process(options); 174 | } 175 | } 176 | 177 | export declare namespace wx { 178 | function uploadFile(option: UploadFileOption): UploadTask; 179 | interface GeneralCallbackResult { 180 | errMsg: string; 181 | } 182 | interface UploadFileOption { 183 | /** 文件对应的 key,开发者在服务端可以通过这个 key 获取文件的二进制内容 */ 184 | name: string; 185 | /** 要上传文件资源的路径 */ 186 | filePath: string; 187 | /** 开发者服务器地址 */ 188 | url: string; 189 | /** HTTP 请求中其他额外的 form data */ 190 | formData?: object; 191 | /** HTTP 请求 Header,Header 中不能设置 Referer */ 192 | header?: object; 193 | /** 接口调用结束的回调函数(调用成功、失败都会执行) */ 194 | complete?(res: GeneralCallbackResult): void; 195 | /** 接口调用失败的回调函数 */ 196 | fail?(res: GeneralCallbackResult): void; 197 | /** 接口调用成功的回调函数 */ 198 | success?(result: UploaderResponse): void; 199 | } 200 | interface UploadTask { 201 | /** [UploadTask.abort()](UploadTask.abort.md) 202 | * 203 | * 中断上传任务 204 | * 205 | * 最低基础库: `1.4.0` 206 | */ 207 | abort(): void; 208 | /** [UploadTask.offHeadersReceived(function callback)](UploadTask.offHeadersReceived.md) 209 | * 210 | * 取消监听HTTP Response Header 事件,会比请求完成事件更早 211 | * 212 | * 最低基础库: `2.1.0` 213 | */ 214 | // offHeadersReceived( 215 | // /** HTTP Response Header 事件的回调函数 */ 216 | // callback: ( 217 | // res: GeneralCallbackResult, 218 | // ) => void, 219 | // ): void; 220 | /** [UploadTask.offProgressUpdate(function callback)](UploadTask.offProgressUpdate.md) 221 | * 222 | * 取消监听上传进度变化事件 223 | * 224 | * 最低基础库: `2.1.0` 225 | */ 226 | // offProgressUpdate( 227 | // /** 上传进度变化事件的回调函数 */ 228 | // callback: (res: GeneralCallbackResult) => void, 229 | // ): void; 230 | /** [UploadTask.onHeadersReceived(function callback)](UploadTask.onHeadersReceived.md) 231 | * 232 | * 监听HTTP Response Header 事件,会比请求完成事件更早 233 | * 234 | * 最低基础库: `2.1.0` 235 | */ 236 | onHeadersReceived( 237 | /** HTTP Response Header 事件的回调函数 */ 238 | callback: ( 239 | result: { 240 | /** 开发者服务器返回的 HTTP Response Header */ 241 | header: object; 242 | } 243 | ) => void 244 | ): void; 245 | /** [UploadTask.onProgressUpdate(function callback)](UploadTask.onProgressUpdate.md) 246 | * 247 | * 监听上传进度变化事件 248 | * 249 | * 最低基础库: `1.4.0` 250 | */ 251 | onProgressUpdate( 252 | callback: UploadTaskOnProgressUpdateCallback): void; 253 | } 254 | 255 | /** 上传进度变化事件的回调函数 */ 256 | type UploadTaskOnProgressUpdateCallback = ( 257 | result: { 258 | /** 上传进度百分比 */ 259 | progress: number; 260 | /** 预期需要上传的数据总长度,单位 Bytes */ 261 | totalBytesExpectedToSend: number; 262 | /** 已经上传的数据长度,单位 Bytes */ 263 | totalBytesSent: number; 264 | }) => void; 265 | } 266 | export interface UploaderResponse extends GeneralCallbackResult { 267 | /** 开发者服务器返回的 HTTP 状态码 */ 268 | statusCode: number; 269 | /** 开发者服务器返回的数据 */ 270 | data: string; 271 | } 272 | -------------------------------------------------------------------------------- /uploader/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "dist", 5 | "lib": [ 6 | "es2015", 7 | "es2015.promise" 8 | ] 9 | }, 10 | "files": [ 11 | "src/index.ts" 12 | ] 13 | } --------------------------------------------------------------------------------