layui-layer load 弹窗自动关闭的问题


layui-layer load 弹窗自动关闭的问题

场景

项目中的 Ajax 加载时的 loading 框有时候会关闭了弹窗之后很久页面上的数据才加载出来,而且这个问题是随机出现的,有些页面存在,有些页面则正常。

最小复现代码

<!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>
      /**
       * 加载遮罩框
       *
       * @returns {Function} 一个关闭遮罩框的函数
       */
      function load() {
        const id = layer.load(1)
        return () => {
          layer.close(id)
        }
      }
      /**
       * 模拟 ajax 异步请求
       */
      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>

控制台打印

request start:  5000
request start:  1000
request end:  1000
第一个请求加载完成了
request end:  5000
第二个请求加载完成了

思考

本来吾辈猜测是 vuejs 页面渲染的锅,认为 vuejs 的生命周期函数 mouted 执行时 DOM 还没加载完全的缘故。
所以把 load 异步化,等待 document 加载完毕才会真正执行。

<!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>
      /**
       * 加载遮罩框
       *
       * @returns {Function} 一个关闭遮罩框的函数
       */
      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)
        }
      }
      /**
       * 模拟 ajax 异步请求
       */
      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>

控制台打印

request start:  5000
request start:  1000
request end:  1000
第一个请求加载完成了
request end:  5000
第二个请求加载完成了

然而实际上却并不是这个问题。。。

经过某位网友提醒,layer 源码中默认只允许一个活动的 load 弹窗。瞬间吾辈都不知道要怎么吐槽了,单例模式避免无谓的内存浪费是正常的,然而新的 load 函数却会关闭之前的 load 这种操作真的是很厉害了呢

例如下面这段代码,无论调用多少次 layer.close(id1),页面上的 loading 都不会关闭。。。

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 本身存在缺陷,那么却是只能自己对 loadclose 功能做控制了
基本思路

  1. layer.load 每次都会关闭掉之前的弹窗,那么就记录最后一次的弹窗 id,在真正需要关闭的时候 close 掉就好了
  2. layer.load 关闭是直接关闭弹窗,如果是最后一个就会出现弹窗消失但数据没加载完全的问题,那么关闭这儿要判断当前是否还有活动的弹窗,只有在没有的情况下才真正关闭

修改后的代码

<!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>
      /**
       * 加载遮罩框
       *
       * @returns {Function} 一个关闭遮罩框的函数
       */
      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)

      /**
       * 模拟 ajax 异步请求
       */
      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>

控制台打印

request start:  5000
request start:  1000
弹窗没有真正关闭哦
request end:  1000
第一个请求加载完成了
弹窗真的关闭啦
request end:  5000
第二个请求加载完成了

文章作者: rxliuli
版权声明: 本博客所有文章除特別声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 rxliuli !
 上一篇
JavaScript 防抖和节流 JavaScript 防抖和节流
JavaScript 防抖和节流场景网络上已经存在了大量的有关 防抖 和 节流 的文章,为何吾辈还要再写一篇呢?事实上,防抖和节流,吾辈在使用中发现了一些奇怪的问题,并经过了数次的修改,这里主要分享一下吾辈遇到的问题以及是如何解决的。 为什
2019-05-09 rxliuli
下一篇 
使用 GitHub 作为 Maven 仓库 使用 GitHub 作为 Maven 仓库
使用 GitHub 作为 Maven 仓库 GitHub 示例 场景吾辈在日常工具中也有一些公共的代码库,一直想分离成单独的类库却没有机会,看到使用 github 就能部署 maven 仓库就尝试了一下。 这里吐槽一下 maven 中央
2019-04-09 rxliuli
  目录