基于我之前的文章,我对图片进行了最大限度的压缩,我们的图片资源体积已经非常小,虽然如此,有些图片还是很大,在pc弱网或者移动端3G网络等情况下,页面加载速度仍然很慢,我们可以利用渐进显示图片和懒加载进行优化。
先请看下效果视频,请点击观看,左边是普通网页(只做了图片优化),右边使用了渐进显示。


渐进显示图片

渐进显示图片(progress load image)就是在图片完全加载之前,先显示一张高清图的体积非常之小的图片,让用户提前先看到模糊的轮廓,同时加载高清图片,在高清图片加载完成之后替换,通过这种逐渐显示的效果来过渡,一方面提高首屏渲染速度(理论上加载完的时间跟没有做渐进显示差不多,实际上反而更慢一些,因为用来一些额外的代码和图片,只不过是通过模糊的方法,让用户先能够大概看到图片并且正常使用其他功能如文字阅读等),一方面增强用户体验。在浏览器有缓存或者网速快时,渐进显示与什么都不做没有差别,只有在没有浏览器缓存网速非常慢时,才有优势。非常知名的网站medium就使用渐进显示,效果挺好!(备注:需要翻墙才能看)。

渐进需要4个步骤
1,利用css高度坍塌原理撑起一个高度,用来放置模糊缩略小图;(如果不知道css高度坍塌的原理的请自行补充知识)
2,可以利用高清图用工具生成一张对应高宽比例低质量图片,大约几十px左右,在高清图片未加载完毕之前代替显示。可以利用阿里云OSS的图片处理功能。
3,利用CSS filter:blur(20px)来做玻璃模糊。由于低质量缩略图代替高质量大图会拉伸放大,看起来像二维码一样的,为了友好显示,做个剥离模糊。css filter属性浏览器都支持,除了天杀的IE浏览器,如果业务上需要,那么还可以选择其他方案,如svg等。
4,与缩略图并行加载一个高清图,在高清图加载完毕时隐藏或者移除缩略图,显示高清图。可以加个非常渐变效果的动画。(渐变效果时间需要权衡,否则在快速的网络情况下,渐进显示感觉反而效果更差)

html,body{
    margin: 0;
    padding: 0;
}
.wrapper{
    max-width: 1200px;
    margin: 0 auto;
}
.container{
}
.placeholder{
    max-width: 1000px;
    max-height: 787px;
    background-color: #eeeeee;
    width: 100%;
    margin: 0 auto;
    position: relative;
    top:20px;
}
.placeholder-fill{
    padding-bottom: 66.67%;
}
.placeholder img{
    width: 100%;
    position: absolute;
    top: 0;
    left: 0;
    opacity: 0;
}
.placeholder img.loaded{
    opacity: 1;
}
img.small-img{
    filter: blur(10px);
}
img.progressiveMedia-noscript{
    opacity: 1!important;
}
.page-header{
    text-align: center;
}
<div class="wrapper">
    <div class="page-header">渐进显示</div>

    <div class="container">
        <div class="placeholder" data-large="http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg">
            <img class="small-img" src="data:image/webp;base64,UklGRpAAAABXRUJQVlA4IIQAAADQBACdASobABIAP02QwViwqScjsBgIAhApiWQAtRuOwZoudohiZVUG1HgtlPO8AAD+JPAIh8Dn1Dhzpi5Bws40al+95ZJK1ZZHzE1eIJPGTuc/kn1KbYd6JEUekj7rSNnmd9/c0mhnW5mqMaWSboVCgGZaCDQ+QlH/jOa9ylvZcQAAAAA=" alt="">
            <div class="placeholder-fill"></div>
            <noscript class="js-progressiveMedia-inner"><img class="progressiveMedia-noscript" src="http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg"></noscript>
        </div>

        <div class="placeholder" data-large="https://cdn-images-1.medium.com/max/2000/1*aKquCUexLfsmATfT3WdYFw.jpeg">
            <img class="small-img" src="data:image/jpeg;base64,/9j/2wCEACgcHiMeGSgjISMtKygwPGRBPDc3PHtYXUlkkYCZlo+AjIqgtObDoKrarYqMyP/L2u71////m8H////6/+b9//gBKy0tPDU8dkFBdviljKX4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+Pj4+P/AABEIAC8APAMBIgACEQEDEQH/xAGiAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgsQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+gEAAwEBAQEBAQEBAQAAAAAAAAECAwQFBgcICQoLEQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2gAMAwEAAhEDEQA/ANmiiori5itl3SvjPQdz9BQBLWfqs5WBRDNtkEig7TyM561BPd3FzwmYIvb75/wrPiAMcgGQPOQf+hUrgatrqgyI7sBG7OPun6+laVYEuY2BYA9unDCrULS2yhoDvhPWMnp9DVWA1aKit7mK5XMZ5HVTwR9RUtICG7d47ZmjOGyADjOMkCszy1D7my7nqzHJNaGoZ+xvtxuyuM/7wqkpBGeTkfkaiQmNIO0ZPBHFUSCqSjv5sf8AJqvudi5Jqi7blmJ6ebH/ACaiO4LYnL+Yu2Qc9mpYZTC5BO4f0qEkfwj86kjhaYOcexyK1tYnmuWVZWnicLtYOo3DjOTjFa1c/HC8VzEGPAkXH5iugqSkV74MbY7QCcjgnHes0rddzCoJxzk1tEBhggEe9R/uR2X8qVkMyzYzy/fugPYJT10pRCymZ9xYNux0Izj+dW32s3y7l+lWQox0FMDJaxn24+15HvGKia2uY+A8R54JyDWtLHlsgHHscUBo8AFOfcCgDN/fs8avEhw4O4N71sUxUjPIQflT6AP/2Q==" alt="">
            <div class="placeholder-fill"></div>
        </div>
    </div>
</div>

1,<div class=“placeholder-fill”></div> 目的就是利用css坍塌撑高父div。

2,html中 <img class=“small-img” … >的代码就是模糊缩略图,这里是缩略图的Base64编码,直接跟随document一起请求回来。此处本是使用阿里云OSS的缩略图生成方法,但是由于需要document请求回来,再去阿里云服务器上请求,经历了二次请求,耗时太多。经我实践,chrome disabled cache并且network为3g弱网时,阿里云OSS生成缩略图返回用来 2s,效果不佳。所以此处直接用base64编码,大概缩略图为200-300byte左右;(注意:此处缩略图用webp优化有,生成base64,体积可大大减小,上面html中第二个base64需要优化);

3,<div class=”placeholder” data-large=”…” > data-large 用来存放高清图片url,稍后会讲述其原理。

4,  noscript 标签用来在javascript被禁止的浏览器的做fallback的处理,这样就算javascript被禁用,页面仍能正常显示图片,用处不是很大。如无需要可以忽略。

 

对应的javascript

<script type="application/javascript">
        var placeholder = document.querySelectorAll('.placeholder');

        placeholder.forEach(function(item,index,arr){
            (function(){
                var component = item,img = new Image(),
                small = component.querySelector('.small-img');
                img.src = small.src;
                img.onload = function(){
                    small.classList.add('loaded');
                }

                //load large
                var large = new Image();
                large.src = component.dataset.large;
                component.append(large);
                large.onload = function () {
                    small.remove();
                    large.classList.add('loaded');
                }
            }())
        })
    
</script>

此处js是封装的通用方法,获取所有class为.placeholder的元素,闭包实现每个元素加载高清图片,并且高清图片加载完成之后移除模糊缩略图。此js代码加载时序优先级需要非常高,也就是说要最早加载,否则会导致高清图片加载阻塞,相比不做任何处理的图片加载耗时更久。如果封装成通用js,建议独立,并且优先加载。

此处建议读者自行模拟一边,copy我的代码稍作调试,亲自感受下

原理

如果你亲自尝试了,原理也不难理解了。实际上就是在document加载完成之后,并行做两件事情,一个是获取并显示模糊缩略图,并且下载高清图,等待高清图下载完成后移除缩略图。实际上我们的高清图片的显示时间并不回比普通页面的时间提早,差别不大,实际上还有一点微弱的延后,因为多了base64的编码导致document体积增加,高清图的加载是在那段js代码之后执行。但体验上效果好了很多。对于特别大的背景图,或者文章主图等占据巨大位置的图片,如果不用渐进显示,会导致页面的一部分空白一段时间,渐进显示会提示用户此处有图,逐渐清晰也能理解。同时也有人觉得效果反而不好,见仁见智了,如果你打算采用上述方法,一定注意文章中说的注意事项,以及如果集成到react,angular等mvvm框架中还需要测试,否则可能会适得其反。

图片懒加载

渐进显示适用于文章主图,背景图,大图等的过渡显示,改进用户体验。但一个页面可能有几十甚至上百张图片(如apple官网找一个iphonex的网页);我们知道浏览器对一个host的同一时间的并发通道数是有限的,通常十来个左右
lazyload示意图
也就是说,如果100个向同一个服务器的请求,就是要排队了,节约带宽有限让用户看到首屏是关键。所以需要做懒加载集中流量让用户先看到关键信息,未滚动的部分分步骤加载。这里有一个成熟的方案,lazysizes.js,用法非常简单,如何使用请参考github说明。但是需要注意的是,lazyload应该作用在需要lazyload的地方,也就是第3屏及以后的地方,额外可以配合渐进显示使用。下面看下配合渐进显示的代码

懒加载配合渐进显示一起使用

详细细节读者可自行搭建环境尝试,效果略好!

总结:
到此,回顾一下,1,我们极致的优化了图片资源,使其体积极小而又不失真;2,我们使用了渐进显示前两屏,让用户非常短的时间内能够看到大概的页面,随后逐渐清晰正常使用;3,我们使用懒加载,后续页面给首屏图片让路,集中流量渲染首屏,提高首屏渲染速度。使用了此3个优化点,在网速快的时候不明显。网络较差的环境下,页面再复杂庞大,理论打开也是比较愉快的事情了。当然,除此之外,我们还可以使用雪碧图减少http请求,使用CDN,关键图片优先显示等等其他手段,请看极致性能 – 图片章的后期内容

 

Categories: 前端性能优化

发表评论

电子邮件地址不会被公开。 必填项已用*标注