最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 前端图片资源优化实践

    正文概述 掘金(superjoe)   2020-12-31   473

    写在前面 

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

    问题 

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

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

    前端图片资源优化实践

    实际是这样的: 

    前端图片资源优化实践

    分析 

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

    前端图片资源优化实践

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

    初级方案 

    基础实现 

    **方案核心:**分化图片源 

    这个方案是将源文件复制成N多份,然后每个前端的引用映射一个CDN源文件,从图片源上将这些相同的图片给独立开。 

    举个栗子,原图的名字是“demo.gif”,然后复制“demo.gif”,生成“demo001.gif”、“demo002.gif”、“demo003.gif”... 。

    前端使用图片的时候,第一张图用“demo001.gif”、第二张用“demo002.gif”、第三张用“demo003.gif”... 。依此类推...  

    资源结构如下:

    前端图片资源优化实践

    预加载 

    const baseUrl = 'http://www.abc.com/'
    // 这里包层数组是为了拓展其他图片 
    const imgArr = [{len: 15, nameFrag: 'demo_00'}]
    
    // imgData 最后变成一个 url 列表
    // 如:['http://www.abc.com/demo_001.gif','http://www.abc.com/demo_001.gif' ... ]
    let imgData = [];
    
    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”。然后还要前端的引用的图片名一个个更改。 

    2、浪费存储(cdn)资源

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

    升级方案

    基础实现 

    方案核心:分化浏览器缓存 

    前面说的方案一是生成多个图片源,让前端引用和图片源一对一映射关系。那这个方案的核心就是生成多个浏览器缓存,让前端引用和浏览器缓存形成一对一映射关系。 

    具体就是,在每个图片请求上加一个固定的参数编号,相当于给浏览器缓存打上一个编号。 

    比如: 

    原图片路径:“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”

    ......

    图片资源结构如下:

    前端图片资源优化实践

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

    // 第一种
    // 直接将参数编号列出来
    // 每用到一个用到一个,则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'}]
    
    // imgData 最后变成一个 url 列表
    // 如:['http://www.abc.com/demo_001.gif','http://www.abc.com/demo_001.gif' ... ]
    let imadata = [];
    
    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...
        }
    );
    

    起源地下载网 » 前端图片资源优化实践

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元