本文最后更新于:2020年12月30日 凌晨
场景
项目中的 Ajax 加载时的 loading 框有时候会关闭了弹窗之后很久页面上的数据才加载出来,而且这个问题是随机出现的,有些页面存在,有些页面则正常。
最小复现代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/rx-util.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.js"></script> <script>
function load() { const id = layer.load(1) return () => { layer.close(id) } }
async function request(time) { const close = load() console.log('request start: ', time) await rx.wait(time) close() console.log('request end: ', time) }
;(() => { request(5000).then(() => console.log('第二个请求加载完成了')) request(1000).then(() => console.log('第一个请求加载完成了')) })() </script> </body> </html>
|
控制台打印
1 2 3 4 5 6
| request start: 5000 request start: 1000 request end: 1000 第一个请求加载完成了 request end: 5000 第二个请求加载完成了
|
思考
本来吾辈猜测是 vuejs 页面渲染的锅,认为 vuejs 的生命周期函数 mouted
执行时 DOM 还没加载完全的缘故。
所以把 load
异步化,等待 document 加载完毕才会真正执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/rx-util.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.js"></script> <script>
async function load() { await rx.wait(() => document.readyState === 'complete') const id = layer.load(1) return async () => { await rx.wait(() => document.readyState === 'complete') layer.close(id) } }
async function request(time) { const close = await load() console.log('request start: ', time) await rx.wait(time) await close() console.log('request end: ', time) }
;(() => { request(5000).then(() => console.log('第二个请求加载完成了')) request(1000).then(() => console.log('第一个请求加载完成了')) })() </script> </body> </html>
|
控制台打印
1 2 3 4 5 6
| request start: 5000 request start: 1000 request end: 1000 第一个请求加载完成了 request end: 5000 第二个请求加载完成了
|
然而实际上却并不是这个问题。。。
经过某位网友提醒,layer 源码中默认只允许一个活动的 load
弹窗。瞬间吾辈都不知道要怎么吐槽了,单例模式避免无谓的内存浪费是正常的,然而新的 load
函数却会关闭之前的 load
这种操作真的是很厉害了呢
例如下面这段代码,无论调用多少次 layer.close(id1)
,页面上的 loading
都不会关闭。。。
1 2 3 4 5 6 7
| const id1 = layer.load() const id2 = layer.load() layer.close(id1) layer.close(id1)
layer.close(id1) layer.close(id1)
|
这里吾辈可以想象到,layer 认为先加载的 load()
就应该先被 close()
,而没有考虑到复杂异步的情况。
解决
既然 layer 的 load
本身存在缺陷,那么却是只能自己对 load
和 close
功能做控制了
基本思路
layer.load
每次都会关闭掉之前的弹窗,那么就记录最后一次的弹窗 id,在真正需要关闭的时候 close 掉就好了
layer.load
关闭是直接关闭弹窗,如果是最后一个就会出现弹窗消失但数据没加载完全的问题,那么关闭这儿要判断当前是否还有活动的弹窗,只有在没有的情况下才真正关闭
修改后的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53
| <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta http-equiv="X-UA-Compatible" content="ie=edge" /> <title>Document</title> </head> <body> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/rx-util.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/jquery.min.js"></script> <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/layer.js"></script> <script>
const load = ((num, lastId) => () => { lastId = layer.load(1) num++ return () => { num-- if (num < 0) { num = 0 } if (num > 0) { console.log('弹窗没有真正关闭哦') return } layer.close(lastId) console.log('弹窗真的关闭啦') } })(0)
async function request(time) { const close = await load() console.log('request start: ', time) await rx.wait(time) await close() console.log('request end: ', time) }
;(() => { request(5000).then(() => console.log('第二个请求加载完成了')) request(1000).then(() => console.log('第一个请求加载完成了')) })() </script> </body> </html>
|
控制台打印
1 2 3 4 5 6 7 8
| request start: 5000 request start: 1000 弹窗没有真正关闭哦 request end: 1000 第一个请求加载完成了 弹窗真的关闭啦 request end: 5000 第二个请求加载完成了
|