JavaScript前端静态资源预加载实现示例

为什么要静态资源预加载?

静态资源文件主要指的就是图片,我们在项目中用到最多的静态资源文件也是图片。但是图片作为一种可替换元素,在使用时有很多的问题。

在了解图片问题之前,先看看发生在我身上的真实小故事,帮助大家更好的理解。

在一个阳光明媚的清晨,你因为各种原因出门晚了。你一路飞奔找到最近的小蓝车,途中完成wifi切换流量、打开支付宝、点击哈罗等操作。看着进入小程序的loading,你知道今天又能挤上地铁,嘴角不禁微微上扬。

在loading消失的最后一秒你也刚好气喘吁吁的跑到了小蓝车面前,迫不及待的点击"扫一扫"三个大字...

就在你手指将要触碰到屏幕的那一瞬间,页面突然一闪,原来扫一扫的位置变成了去购买。下一秒页面不出意外的跳转到了优惠活动页,重新开始了loading...

待你点击返回扫开小蓝,红绿灯已经由黄转红,今天铁定是迟到了...

相信每天踩着最后一秒打卡的朋友一定很理解我的心路历程???。

封面的动图就是那个页面的变动过程,这个动图也很好的说明了图片在前端渲染时出现的问题。

图片在使用时的问题

文件大小要大于文字,在网络中传输慢。

故事中同一个接口的去购买文字已经展示出来,但是banner图还没渲染出来。

影响用户体验

故事中打开页面后原本是banner图的位置有一大块空白,这对于用户体验来说不够好。

试想如果一个页面中大大小小的图片有很多,在加载时都是这样的空白会是什么体验?

或者当你点击了跳转或者打开一个内容时,内容中的图片位置总是先显示空白会是什么体验?

影响Dom结构

故事中页面之所以会发生抖动和闪烁,是因为dom结构发生了变化。故事中dom结构变化的原因可能是因为优惠接口返回慢而导致的,但是图片的加载和渲染也能出现类似的场景。进而导致用户的行为因为页面dom的变动而发生错误。

图片会裂开

将图片作为背景图片时,加载失败顶多导致背景空白。但是直接使用image标签如果图片加载失败就会有一个裂开的图片展示到页面上,这种场景相信大家都见到过。

解决办法

  • 可以将图片压缩到尽可能小,这样可以大幅提高传输和加载的速度;
  • 使用背景图片代替image标签;
  • 可以尝试将小图标整合成精灵图雪碧图;
  • 针对dom结构变动,可以参考哈罗在图片外层设置一个差不多高度的div预先给图片留个位置;

无法解决的场景

以上的解决方法都适用于所用到的图片比较小,或者比较少的情况下。当图片本身比较大时即使经过压缩还是很大,也不方便做成精灵图。当一个项目拥有很多图片,用户在不同项目的子路由间跳转或者通过交互触发页面上大量图片更新时,上面的方法就不是很适用了。

什么是静态资源预加载?

静态资源预加载的原理是利用浏览器的缓存机制配合img的onload事件,将漫长的图片的加载过程放在真正进入页面之前,进入页面后所有用到的图片都会直接从内存读取。

也就是说要将页面的初始化接口在进入页面之前调用,从而获取要加载的图片链接。

静态资源预加载的优点和缺点

优点

  • 用户进入页面后一瞬间就可以看到整个页面而不会出现白屏;
  • 用户在切换tab或者是跳转到子路由页面时不会因为加载图片而出现页面的卡顿
  • 不会出现图片由空白从上至下一点点的向下渲染的过程;
  • 不会出现因为图片没及时加载出来而导致的dom结构崩乱,造成页面闪烁

缺点

  • 漫长的图片加载放到进入页面之前会让用户的等待时间变长;
  • 过长的等待时间可能会导致转化率的下降;

缺点的处理

为了应对这个缺陷,在做静态资源预加载处理进入页面之前,我们通常都会搭配一个可以显示进度的loading。这样可以让用户实时获取到加载进度,而不是无谓的等待。这样一定程度上可以增加用户停留在当前页面的时间,从而进入到下个页面。

静态资源预加载的实现

通过递归的方式在每一张图片onload成功之后开始加载下一个图片,直到图片全部都加载完成。

import isFunction from 'lodash/isFunction';
    /**
     * 图片资源预加载
     * @param {Array} urlArr 图片链接数组
     * @param {number} index 当前加载的是第几张图片
     * @param {function} imgLoadFn 单张图片加载成功的回调
     * @param {function} successFn 成功失败回调函数
     * @param {function} errorFn 加载失败回调函数
     */
    imgLoader(urlArr, index, imgLoadFn, successFn, errorFn) {
        if (index === urlArr.length && !urlArr[ index ]) return;
        downloadPic(...arguments);
    }
    //  加载单张图片
    downloadPic(urlArr, index, imgLoadFn, successFn, errorFn) {
        console.log('开始加载第', index, '张图')
        const img = document.createElement('img');
        img.src = urlArr[ index ];
        img.onload = () => {
            // 这里每一个图片加载完成时传入的应该是index+1,因为index是从0开始的,而这里表示的是第几张图片完成加载的回调,传入数组长度用于计算百分比
            isFunction(imgLoadFn) && imgLoadFn(index + 1, urlArr.length);
            if (index === urlArr.length - 1) {
                // 加载完最后一张时调用全部完成函数
                successFn(index);
            } else {
               //  否则开启下一张图片的加载
                index += 1;
                downloadPic(urlArr, index, imgLoadFn, successFn, errorFn);
            }
        };
        img.onError = errorFn ? () => {
            isFunction(errorFn) && errorFn({
                index,
                url: urlArr[ index ]
            })
        } : () => imgLoaderError(index);
    }
    /**
     * 图片加载失败
     * @param {number} index 数组下标
     */
    imgLoaderError(index) {
        console.error(`第${ index }张图片加载失败`);
    }

静态资源预加载的调用

// 定义好对应的处理函数
const imgLoadFn = (current, total) => {
    // 用百分比来显示当前的加载进度显示在页面loading中
    this.percent = parseInt(current / total * 100) + '%';
}
const successFn = () => {
    this.percent = '100%';
    this.showLoading = false;  // 关闭loading
    console.log('图片已全部预加载完成,可以跳转到对应页面')
}
const errorFn = () => {
    console.log('加载资源错误');
}
//  调用
imgLoader(urlArr, 0, imgLoadFn, successFn, errorFn);

静态资源预加载的应用场景

静态资源预加载常用于各种活动页面,例如双11、618、818、年度账单、周年活动等。

为了让用户在进入活动页之后的交互更流畅,通常会在进入页面前使用静态资源加载将所需的资源文件都加载到缓存中。

所以,这也是我们在进入这些页面之前经常可以看到一个加载进度loading的原因。关于如何实现一个带进度的loading,可以看我的这篇文章。

使用svg实现简单带进度的loading

以上就是JavaScript前端静态资源预加载实现示例的详细内容,更多关于JavaScript前端静态资源预加载的资料请关注其它相关文章!

原文地址:https://juejin.cn/post/7163305939523600391