├── LICENSE ├── README.md ├── fetch-with-loading.js ├── index.html └── package.json /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 阅文前端 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fetch-with-loading 2 | 3 | fetch-with-loading 是一个带有 loading 的 promise 扩展 4 | 5 | ## 特点 6 | 7 | 1. 使用方便,没有 API,仅仅是对默认请求的扩展 8 | 1. 提示可以自定义内容和样式,灵活快捷 9 | 1. 支持其他 promise 请求库,如 axios 10 | 1. 无任何依赖,非常轻量,无框架限制 11 | 12 | ## 实现效果 13 | 14 | 使用该扩展,可以带来丰富细腻的用户体验 15 | 16 | 1. 如果请求可以在200ms内完成,表示网速很快,则不显示loading 17 | 1. 如果请求超过200ms,则至少显示200ms的loading,避免一闪而过的情况 18 | 1. 如果请求时间更长,比如超过10s,需要每隔3秒更换提示,缓解用户焦虑,如:"加载中=>正在努力加载中=>快好了" 19 | 20 | [在线预览](https://yued-fe.github.io/fetch-with-loading/) 21 | 22 | ## 快速开始 23 | 24 | ### 安装 25 | 26 | 1. 直接在 [github](https://github.com/yued-fe/fetch-with-loading) 获取 fetch-with-loading.js 27 | 28 | ```html 29 | 30 | ``` 31 | 32 | 2. 直接使用 unpkg [在线链接](https://unpkg.com/fetch-with-loading) 33 | 34 | ```html 35 | 36 | ``` 37 | 38 | 3. 通过 [npm](https://www.npmjs.com/package/fetch-with-loading) 安装 39 | 40 | ```cmd 41 | npm i fetch-with-loading 42 | ``` 43 | 44 | ### 使用 45 | 46 | 通过 script 引用,会得到一个全局变量 `fetchWithLoading` 47 | 48 | 通过 npm 安装,需要 import 导入 49 | 50 | ```js 51 | import fetchWithLoading from 'fetch-with-loading'; 52 | ``` 53 | 54 | 在页面中使用 55 | 56 | ```js 57 | // 重新定义一个请求方法 58 | const fetch_with_loading = new fetchWithLoading(); 59 | fetch_with_loading('/list').then(res => console.log(res)) 60 | 61 | // 也可以直接重置默认fetch 62 | window.fetch = new fetchWithLoading(); 63 | fetch('/list').then(res => console.log(res)) 64 | 65 | // 自定义提示 66 | window.fetch = new fetchWithLoading(['加载中...','正在努力加载中...','快好了...']); 67 | ``` 68 | 69 | ## 可选配置项 70 | 71 | ```js 72 | new fetchWithLoading(tips|options, NativeFetch) 73 | ``` 74 | 75 | 参数 76 | 77 | 第一个参数支持数组或者对象两种格式,分别是 78 | 79 | * `tips`是一个数组,表示 Loading 提示队列,默认为 `['加载中...','正在努力加载中...','快好了...']`,可无限追加 80 | 81 | ```js 82 | // 示例 83 | new fetchWithLoading(['加载中...','还在努力加载中...','请稍等,快好了...']) 84 | ``` 85 | 86 | * `options`是一个对象,表示 Loading 的所有可定制选项,默认为 87 | 88 | ```js 89 | { 90 | tips: ['加载中...', '正在努力加载中...', '快好了...'], 91 | timestep: 3000, 92 | delay: 200, 93 | duration: 200, 94 | } 95 | ``` 96 | 97 | 其中: 98 | 99 | * `tips` 和前面一致 100 | * `timestep(ms)` 表示每隔多长时间更换提示信息,默认为 `3000` 101 | * `delay(ms)` 表示在多长时间内完成请求可无需显示提示信息,默认为 `200` 102 | * `duration(ms)` 表示出现提示信息后,至少显示多长时间,默认为 `200` 103 | 104 | ```js 105 | // 示例 106 | new fetchWithLoading({ 107 | tips: ['加载中...', '正在努力加载中...', '快好了...'], 108 | timestep: 2000, 109 | delay: 300, 110 | duration: 500, 111 | }) 112 | ``` 113 | 114 | 第二个参数是一个 promise 对象 115 | 116 | * `NativeFetch` 表示请求方法,默认是 `fetch` 117 | 118 | 如果习惯使用 [axios](http://www.axios-js.com/zh-cn/) ,可以传入 119 | 120 | ```js 121 | // 示例 122 | import axios from 'axios'; 123 | const axios_with_loading = new fetchWithLoading(['加载中...','还在努力加载中...','请稍等,快好了...'], axios) 124 | ``` 125 | 126 | 如果需要自定义拦截器 127 | 128 | ```js 129 | import axios from 'axios'; 130 | const service = axios.create({...}); 131 | // 请求拦截器 132 | service.interceptors.request.use(); 133 | // 响应拦截器 134 | service.interceptors.response.use(); 135 | // fetch with loading 136 | const axios_with_loading = new fetchWithLoading(['加载中...','还在努力加载中...','请稍等,快好了...'], service); 137 | ``` 138 | 也就是说,不影响原有逻辑,只需要在最后包裹一层就行了 139 | 140 | ## 其他耗时操作 141 | 142 | 不仅仅是请求,其他耗时操作也同样适用,比如 setTimeout 143 | 144 | ```js 145 | // 需要改写成 promise 146 | function timeout(delay) { 147 | return new Promise(resolve => { 148 | setTimeout(() => resolve(true), delay); 149 | }); 150 | } 151 | // 封装 152 | const timeout_with_loading = new fetchWithLoading(['加载中...','还在努力加载中...','请稍等,快好了...'], timeout); 153 | 154 | // 使用 155 | timeout_with_loading(10000).then(res => console.log(res)) 156 | ``` 157 | 158 | 通常可以用来模拟请求,[在线预览](https://yued-fe.github.io/fetch-with-loading/) 159 | 160 | ## 自定义样式 161 | 162 | loading 的样式可以通过以下自定义,默认是一个黑色半透明的圆角矩形 163 | 164 | ```css 165 | .toast{ 166 | /*toast*/ 167 | } 168 | .toast[loading]::before{ 169 | /*loading*/ 170 | } 171 | ``` 172 | 173 | ## 兼容性 174 | 175 | 现代浏览器,包括移动端,不支持 `IE` 176 | 177 | > 兼容性取决于 [Promise](https://caniuse.com/?search=Promise) 178 | 179 | 180 | ## 联系我 181 | 182 | 有相关问题或者意见可与我联系 yanwenbin@yuewen.com 或者直接提 [issue](https://github.com/yued-fe/fetch-with-loading/issues) -------------------------------------------------------------------------------- /fetch-with-loading.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @fetch-with-loading.js 3 | * @author xboxyan 4 | * @email yanwenbin1991@live.com 5 | * Created: 21-07-08 6 | */ 7 | class fetchWithLoading { 8 | constructor(options, NativeFetch){ 9 | this.initLoading(''); 10 | return this.initFetch(options, NativeFetch); 11 | } 12 | initFetch(options, NativeFetch=fetch){ 13 | let params = { 14 | tips: ['加载中...', '正在努力加载中...', '快好了...'], 15 | timestep: 3000, 16 | delay: 200, 17 | duration: 200, 18 | } 19 | if (options) { 20 | if (Array.isArray(options)) { 21 | params.tips = options; 22 | } else { 23 | params = {...params, ...options} 24 | } 25 | } 26 | const { tips, timestep, delay, duration } = params; 27 | return value => new Promise((resolve, reject) => { 28 | const promiseFetch = NativeFetch(value); 29 | Promise.race([promiseFetch, this.timeout(delay)]).then((result) => { 30 | if (!result) { 31 | this.showLoading(tips, timestep); 32 | Promise.all([promiseFetch, this.timeout(duration)]).then((data) => { 33 | resolve(data[0]); 34 | this.hideLoading(); 35 | }); 36 | } else { 37 | resolve(result) 38 | } 39 | }); 40 | }) 41 | } 42 | initLoading(msg) { 43 | if (!window._LOADING_EL) { 44 | window._LOADING_EL = document.createElement('div'); 45 | window._LOADING_EL.className = 'toast'; 46 | window._LOADING_EL.setAttribute('loading',''); 47 | const style = document.createElement('style'); 48 | style.textContent = `/* toast */ 49 | .toast { 50 | position: fixed; 51 | bottom: 50%; 52 | left: 50%; 53 | display: flex; 54 | align-items: center; 55 | transform: translate(-50%, -50%); 56 | background: rgba(33, 32, 44, 0.9); 57 | border-radius: 4px; 58 | padding: 12px 16px; 59 | font-size: 14px; 60 | line-height: 1.8; 61 | color: #fff; 62 | visibility: hidden; 63 | opacity: 0; 64 | z-index: 12; 65 | } 66 | .toast.show { 67 | visibility: visible; 68 | opacity: 1; 69 | } 70 | .toast[loading]::before { 71 | content: ''; 72 | width: 12px; 73 | height: 12px; 74 | border: 2px solid; 75 | border-top-color: transparent; 76 | border-radius: 100%; 77 | flex-shrink: 0; 78 | margin: .1em .5em .1em 0; 79 | animation: rotate 1s linear infinite; 80 | animation-play-state: paused; 81 | } 82 | .toast.show[loading]::before{ 83 | animation-play-state: running; 84 | } 85 | @keyframes rotate { 86 | to { 87 | transform: rotate(360deg); 88 | } 89 | } 90 | ` 91 | document.querySelector('head').appendChild(style); 92 | document.body.appendChild(window._LOADING_EL); 93 | } 94 | window._LOADING_EL.innerText = msg; 95 | } 96 | showLoading(tips, timestep) { 97 | let index = 0; 98 | this.initLoading(tips[index]) 99 | window._FETCH_LOADING_ = this; 100 | window._LOADING_EL.classList.add('show'); 101 | this.timerLoading && clearInterval(this.timerLoading); 102 | this.timerLoading = setInterval(() => { 103 | index++; 104 | this.initLoading(tips[index]); 105 | if (index >= tips.length -1 || window._FETCH_LOADING_ !== this) { 106 | this.timerLoading && clearInterval(this.timerLoading); 107 | } 108 | }, timestep) 109 | } 110 | hideLoading() { 111 | window._FETCH_LOADING_ === this && window._LOADING_EL.classList.remove('show'); 112 | this.timerLoading && clearInterval(this.timerLoading); 113 | } 114 | timeout(delay, result) { 115 | return new Promise(resolve => { 116 | setTimeout(() => resolve(result), delay); 117 | }); 118 | } 119 | } 120 | 121 | if (typeof module !== 'undefined' && typeof module.exports !== 'undefined') { 122 | module.exports = fetchWithLoading; 123 | } else { 124 | window.fetchWithLoading = fetchWithLoading; 125 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | fetch-with-loading 9 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 |

fetch-with-loading

66 |
这里采用定时器模拟
67 |

github

68 | https://github.com/yued-fe/fetch-with-loading 69 |

常规用法

70 |
window.fetch = new fetchWithLoading();
71 | 72 | 73 | 74 |

自定义提示

75 |
window.fetch = new fetchWithLoading(['请求中...','还在请求中...','可能会有点慢...','快好了,还等会...']);
 76 | //请求
 77 | fetch('/list').then(res => console.log(res))
78 | 79 |

全部配置项

80 |
window.fetch = new fetchWithLoading({
 81 |     tips: ['请求中...','还在请求中...','可能会有点慢...','别着急...','马上就好了...'],
 82 |     timestep: 2000, //表示每隔多长时间更换提示信息
 83 |     delay: 200, //表示在多长时间内完成请求可无需显示提示信息
 84 |     duration: 500, //表示出现提示信息后,至少显示多长时间
 85 | });
 86 | //请求
 87 | fetch('/list').then(res => console.log(res))
 88 | 
89 | 90 | 91 | 92 |

自定义样式

93 |
.toast{
 94 |     /*toast*/
 95 |     background: #fff;
 96 |     color: #333;
 97 |     box-shadow: 0 4px 12px rgba(0,0,0,0.15);
 98 | }
99 | 100 | 101 | 102 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fetch-with-loading", 3 | "version": "1.0.4", 4 | "description": "fetch-with-loading 是一个带有 loading 的 promise 扩展库", 5 | "main": "fetch-with-loading.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1" 8 | }, 9 | "repository": { 10 | "type": "git", 11 | "url": "git+https://github.com/yued-fe/fetch-with-loading.git" 12 | }, 13 | "keywords": [ 14 | "fetch", 15 | "axios", 16 | "loading", 17 | "yux" 18 | ], 19 | "author": "XboxYan", 20 | "license": "MIT", 21 | "bugs": { 22 | "url": "https://github.com/yued-fe/fetch-with-loading/issues" 23 | }, 24 | "files": [ 25 | "fetch-with-loading.js" 26 | ], 27 | "homepage": "https://github.com/yued-fe/fetch-with-loading#readme" 28 | } --------------------------------------------------------------------------------