前端图片资源优化实践

写在前面

前段时间遇到了一个比较有意思的GIF图相关的问题,下面我先简单说一下遇到的问题,然后再说一下是怎么做资源处理的。

问题

页面中有很多个相同的GIF,可以手动触发播放其中一个。(这里播放的方案就不说了,直接说一下播放过程中遇到的问题)

简单来说,预期是这样的:
image

实际是这样的
image

问题总结:触发其中一个播放的时候,其他相同的GIF动画也会被一起被影响。

分析

先看一下这些GIF图的资源结构:

前端图片资源优化实践_第1张图片

可以看到,这些GIF用的都是相同的资源路径。所以在第一个资源回来的时候,会在浏览器形成一个图片缓存,后续其他GIF资源用的都是这个缓存。所以触发其中一个GIF图播放的时候,相当于播放了存在缓存中的GIF图。这个时候,其他的引用了这个缓存资源的地方也就形成了一起播放的效果。

分析总结:这些gif图使用了相同的浏览器缓存。

初级方案

基础实现

方案核心:分化图片源
这个方案是将源文件复制成N多份,然后每个前端的引用映射一个CDN源文件,从图片源上将这些相同的图片给独立开。
举个栗子,原图的名字是“demo.gif”,然后复制“demo.gif”,生成“demo001.gif”、“demo002.gif”、“demo003.gif”... 。然后前端使用图片的时候,第一张图用“demo001.gif”、第二张用“demo002.gif”、第三张用“demo003.gif”... 。依此类推...

资源结构如下:

前端图片资源优化实践_第2张图片

预加载

const baseUrl = 'http://www.abc.com/'
// 这里包层数组是为了拓展其他图片 
const imgArr = [{len: 15, nameFrag: 'demo_00'}]
let imgData = [];
// imgData 最后变成一个 url 列表
// 如:['http://www.abc.com/demo_001.gif','http://www.abc.com/demo_001.gif' ... ]
imgArr.forEach((imgConfig) => {
    // new Array兼容性有问题 可以考虑换成for循环
    const arr = new Array(imgConfig.len).fill('').map((item,index) => `${baseUrl}${imgConfig.nameFrag}${index+1}.gif`);
    imgData = imgData.concat(arr)
})

// 拿到一个promise 列表 ,最后用 promise.all 处理所有预加载
const promiseAll = imgData.map(function (item, index) {
    return new Promise(function (resolve, reject) {
      const img = new Image();
      img.onload = function () {
        img.onload = null;
        resolve(img);
      };
      img.error = function () {
        reject('图片加载失败');
      };
      img.src = item;
    });
 });
 
Promise.all(promiseAll).then(
    function () {
        do something...
    },
    function (err) {
        do something...
    }
);

缺点:

1、资源更新成本高

举个例子,你的网页上原来有 15 张 “点赞.gif” ,UI同学突然哪天来灵感了,她要把“点赞.gif”替换成“疯狂点赞.gif”。

那这个时候麻烦就来了,你要生成15张新的“疯狂点赞.gif”,分别是“疯狂点赞001.gif”、“疯狂点赞002.gif”、“疯狂点赞003.gif”... 。然后再把原来的15张“点赞.gif”替换调。然后还要前端的引用的图片名一个个改掉。

15张可能还能接受,但是如果再来15张“ 点踩.gif”,或者其他更多相同gif呢?那麻烦就大了,这个时候就是经典的吐槽UI时间...

2、浪费存储(cdn)资源

这个很好理解,前端每个引用的图片源都是要存起来的嘛,那实际只需要存 1 张的图片,这个方案需要存15张,相当于资源消耗是原来的N倍。

还是缺点1相同的问题,简单场景下没有问题,但是场景越复杂,存储资源的浪费越大。

升级方案

基础实现

方案核心:分化浏览器缓存
前面说的方案一是生成多个图片源,让前端引用和图片源一对一映射关系。那这个方案的核心就是生成多个浏览器缓存,让前端引用和浏览器缓存形成一对一映射关系。
具体就是,在每个图片请求上加一个固定的参数编号,相当于给浏览器缓存打上一个编号。
比如:
原图片路径是:“http://www.abc.com/demo.gif
新图片路径就是:
http://www.abc.com/demo.gif?001
http://www.abc.com/demo.gif?002
http://www.abc.com/demo.gif?003
......

图片资源结构如下:

前端图片资源优化实践_第3张图片

这个方案额外需要做的一件事情就是,在实现的时候维护一个参数映射关系,维护的形式可以有很多,这里提供两种简单建议:

// 第一种
// 直接将参数编号列出来
// 每用到一个用到一个,则index++
cosnt base_url = 'http://www.baidu.com/'
const imgMap = {
    "demo.gif": {
        index: 0,
        list:['001','002','003'......]
    }
}
const url_001 = `${base_url}demo.gif?${imgMap['demo.gif'].list[imgMap['demo.gif'].index]}`
imgMap['demo.gif'].index++
const url_001 = `${base_url}demo.gif?${imgMap['demo.gif'].list[imgMap['demo.gif'].index]}`
imgMap['demo.gif'].index++
.....

// 第二种 
// 不用存list,根据index实时计算就行
// 需要保证 图片引用的时候 和 预加载的时候 的算法一致就行
// 这里个代码就不列举了

预加载

预加载和方案一的没什么大的区别,只是在生成url的时候,稍微改一下就行

const arr = new Array(imgConfig.len).fill('').map((item,index) => `${baseUrl}${imgConfig.nameFrag}.gif?${index+1}`);

全部代码:

const baseUrl = 'http://www.abc.com/'
// 这里包层数组是为了拓展其他图片 
const imgArr = [{len: 15, nameFrag: 'demo'}]
let imgData = [];
// imgData 最后变成一个 url 列表
// 如:['http://www.abc.com/demo_001.gif','http://www.abc.com/demo_001.gif' ... ]
imgArr.forEach((imgConfig) => {
    // new Array兼容性有问题 可以考虑换成for循环
    const arr = new Array(imgConfig.len).fill('').map((item,index) => `${baseUrl}${imgConfig.nameFrag}.gif?${index+1}`);
    imgData = imgData.concat(arr)
})

// 拿到一个promise 列表 ,最后用 promise.all 处理所有预加载
const promiseAll = imgData.map(function (item, index) {
    return new Promise(function (resolve, reject) {
      const img = new Image();
      img.onload = function () {
        img.onload = null;
        resolve(img);
      };
      img.error = function () {
        reject('图片加载失败');
      };
      img.src = item;
    });
 });
 
Promise.all(promiseAll).then(
    function () {
        do something...
    },
    function (err) {
        do something...
    }
);

你可能感兴趣的:(前端,javascript,优化)