H5页面异步图片延迟显示解决方案

问题提出

在开发h5页面的时候会经常涉及到图片的使用,对于图片的使用优化前端解决方案颇多,比如图片的懒加载等。但是有另外一种常见情况,网上却鲜有解决方案,那就是交互类图片异步显示问题。
举个例子,比如页面中有一个规则弹窗,弹窗背景使用的是图片的形式呈现,在用户网络不稳定情况下,就会发生弹窗已经出现但是弹窗背景并未加载完成,造成弹窗添加微动效无法正常展示,严重影响用户体验,如图示:
在这里插入图片描述

原因简述

造成图片出现延迟显示的原因很简单,因为页面中的图片不论是通过<img>标签还是通过background属性加载,只要dom元素没有挂载或是元素被设置display: none;,浏览器都不会去请求加载该图片资源,只有当元素被挂载到dom树上时,浏览器才会请求图片资源,这时候弹窗已经展示出来,弹窗的动效已经开始执行。

解决方案

  • 一般方案

对于这种情况,肯定很多小伙伴会说直接把图片打包时转化成base64文件保存在项目代码中不就可以了吗?当然,这不失为一种解决方案,但是这样解决无疑会增大项目文件体积,如果是单页面应用会严重影响页面首屏加载。

  • 进阶方案:图片预加载

对于项目中的图片资源预加载,我们其实可以成分利用浏览器的缓存,即当用户完成页面资源请求时,我们对需要异步展示的图片资源先进行预请求,用户真正展示图片资源时,直接使用浏览器中缓存资源,这样就可以实现图片的立即展示了。我们可以对异步图片资源进行提取,在页面资源加载完成时,手动加载异步图片资源:

const loadArr = [
      '//pic0.iqiyipic.com/lequ/20220408/7c16eaa957964cacb746b890d93d2860.gif',
      '//pic3.iqiyipic.com/lequ/20220406/db90143202474bddb901da283aa1cd7d.png',
      '//pic2.iqiyipic.com/lequ/20220407/3d48af83678c4416afc38e2bd52a4672.png',
    ]
 window.onload=()=>{
   loadArr.forEach((src) => {
      try {
       	const image = new Image()
       	image.src = src
      } catch (err) {
       	console.log('err: ', err)
      }
   })
 }
  • 终极解决:利用工程化自动完成异步图片预加载

上面的解决方案,完全依赖于手动提取,如果项目较大很容易出现遗漏或者忘记替换的情况发生,有没有自动化解决方案呢?答案是肯定的,也是我们项目中目前在用的解决方案,那就是通过自定义语法标注异步图片资源,再通过自定义loader进行异步图片资源提取,最后利用plugin在webpack打包完成前在打包结果js文件中植入自执行函数,在页面onload事件中加载图片资源,具体代码实现如下:

在自定义loader中通过正则匹配使用$[] 标注的图片路径,并将其收集到node全局对象的$$preImgLoadArr属性中。

// preImgLoader.js
module.exports = function (content) {

    content = content.replace(/\$\[([\w|\:|\\|\.|\_|\-|\/|\?|\=|\,|\']+)\]/gi, (str) => {
        str = str.slice(2, -1)
        if (!global.$$preImgLoadArr) global.$$preImgLoadArr = []
        !global.$$preImgLoadArr.includes(str) && global.$$preImgLoadArr.push(str)
        return str
    })

    return content;
}

通过webpack编译器compiler获取打包完成前生命周期,插入自执行函数。

// preImgPlugin.js
function preImgPlugin(options) {
    this.options = options || {}
}

preImgPlugin.prototype.apply = function (compiler) {
    compiler.hooks.emit.tapAsync('preImgPlugin', function (compilation, cb) {
        if (global.$$preImgLoadArr) {
            for (let key in compilation.assets) {
                if (key.includes('.min.js') && compilation.assets[key] && compilation.assets[key]._value) {
                    compilation.assets[key]._value += `;(function () { var arr = [${global.$$preImgLoadArr}]; window.onload = function () {console.log('Picture preload'); for (let src of arr) { try { var image = new Image(); image.src = src; } catch (err) {console.log(err);}}}})();`
                }
            }
            global.$$preImgLoadArr = null
        }
        cb()
    })
}

module.exports = preImgPlugin

webpack中配置如下:

module.exports = {
	module: {
	      rules: [
	        {
	          test: /\.vue$/,
	          use: [
	            'vue-loader',
	            // 图片预加载loader配置
	            path.resolve(__dirname,'./config/loaders/preImgLoader')
	          ],
	        },
	      ],
	},
	plugins:[
		// 图片预加载plugin配置
		new PreImgPlugin()
	],
}

页面中使用:

.rule-box {
    .size(645px,769px);
    .po-relative;
    background: url($['//pic2.iqiyipic.com/lequ/20220428/d40031608ce8479583dc065439ad51dd.png'])
      no-repeat 0 0 / contain;
}

通过上述配置,我们在打包的js文件中可以看到自执行函数的插入,在页面加载完成时页面会主动请求预加载图片资源并缓存,从而解决了异步图片延时显示的问题。

  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值