广州总部电话:020-85564311
广州总部电话:020-85564311

广州网站建设-小程序商城开发-广州小程序开发-企业微信开发公司-网站建设高端品牌-优网科技

20年
互联网应用服务商
请输入搜索关键词
知识库 知识库

优网知识库

探索行业前沿,共享知识宝库

完整攻略!让你的网页加载时间降低到 1s 内!(第一篇)
发布日期:2024-12-20 10:53:24 浏览次数: 1021 来源:网页设计

当初分析了定宽高值定宽高比这两种常见的图片延迟加载场景,也介绍了他们的应对方案,还做了一点技术选型的工作。

经过一段时间的项目实践,在先前方案的基础上又做了很多深入的优化工作。最终将好奇心日报的网页打开速度将降低到了1s内,Web端和Mobile端加载3屏数据消耗的流量也大幅降低。


模拟WIFI条件下的网页加载

该篇文章结合具体的项目实践,将围绕如何更快的访问网页展开,细化到具体的技术方案,以及实践中可能遇到的坑,希望对大家有一定的启发和帮助。

为什么要优化网页加载速度?


好奇心日报无论是设计还是内容都追求高品质,于是丰富的图文混合成了标配:首页的banner图,文章详情页的配图,研究所有趣的gif图等等。

特别严重的时候,一篇文章有十多个gif图,加载花费的时间10-20秒之长,加载消耗的流量几十M之多,严重影响了用户体验!尤其是Mobile端,一寸流量一寸金;3-5s打不开页面,用户都会直接逃离。所以网页加载速度优化势在必行!


我们都知道一个网页的加载流程大致如下:
1、解析HTML结构。
2、加载外部脚本和样式表文件。
3、解析并执行脚本代码。// 部分脚本会阻塞页面的加载
4、DOM树构建完成。//DOMContentLoaded 事件
5、加载图片等外部文件。
6、页面加载完毕。//load 事件

一句话就是:请求HTML,然后顺带将HTML依赖的JS/CSS/iconfont等其他资源一并请求过来。

那么优化网页的加载速度,最本质的方式就是:减少请求数量 与 减小请求大小。


减少请求数量

1、将小图标合并成sprite图或者iconfont字体文件
2、用base64减少不必要的网络请求
3、图片延迟加载
4、JS/CSS按需打包
5、延迟加载ga统计
6、等等...


减小请求大小

1、JS/CSS/HTML压缩
2、gzip压缩
3、JS/CSS按需加载
4、图片压缩,jpg优化
5、webp优化 & srcset优化
6、等等...


JS/CSS按需打包JS/CSS按需加载是两个不同的概念:
JS/CSS按需打包是预编译发生的事情,保证只打包当前页面相关的逻辑。
JS/CSS按需加载是运行时发生的事情,保证只加载当前页面第一时间使用到的逻辑。


接下来我们将结合两个本质的优化方式介绍具体的实践方法。

如何减少请求数量?


1、合并图标,减少网络请求

合并图标是减少网络请求的常见的优化手段,网页中的小图标特征是体积小、数量多,而浏览器同时发起的并行请求数量又是有限制的,所以这些小图标会严重的影响网页的加载速度,阻碍关键内容的请求和呈现

sprite图


合并sprite图是慢工细活儿,并没有特别大的技术含量,但却是每个前端开发都必须掌握的技术。

刚入门的前端直接手动切图拼图即可。

经验丰富的前端可以尝试利用构建工具实现自动化,推荐使用。gulp.spritesmith插件。

// 构建视图文件gulp.task('sprites', function() {    var spriteData = gulp.src(config.src)
        .pipe(plumber(handleErrors))
        .pipe(newer(config.imgDest))
        .pipe(logger({ showChange: true }))
        .pipe(spritesmith({
            cssName: 'sprites.css',
            imgName: 'sprites.png',
            cssTemplate: path.resolve('./gulp/lib/template.css.handlebars')
        }));    var imgStream = spriteData.img
        .pipe(buffer())
        .pipe(gulp.dest(config.imgDest));    var cssStream = spriteData.css
        .pipe(gulp.dest(config.cssDest));    return merge([imgStream, cssStream]);
});


sprite图不适合无线端的响应式场景,所以越来越作为一个备用方案。

iconfont字体文件


iconfont字体文件是用字体编码的形式来实现图标效果,既然是文字,那就可以随意设置颜色设置大小,相对来说比sprite方案更好。但是它只适用于纯色图标。推荐使用 阿里巴巴矢量图标库


2、用base64减少不必要的网络请求



base64码兼容性

上文提到的sprite图和iconfont字体文件,对于有些场景并不适合,比如:
1、小背景图,无法放到精灵图中,通常循环平铺小块来设置大背景。
2、小gif图,无法放到精灵图中,发请求又太浪费。



base64使用场景

注意:cssnano压缩css的时候,对于部分规则的base64 uri不能识别,会出现误伤,如下图,cssnano压缩的时候会将//压缩为/



cssnano压缩base64

原因是:cssnano会跳过data:image/data:application后面的字符串,但是不会跳过data:img,所以如果你使用的工具生成的是data:img,建议换工具或者直接将其修改为data:image


3、图片延迟加载

图片是网页中流量占比最多的部分,也是需要重点优化的部分。
图片延迟加载的原理就是先不设置img的src属性,等合适的时机(比如滚动、滑动、出现在视窗内等)再把图片真实url放到img的src属性上。更多内容请移步上一篇博文: 图片延迟加载方案


固定宽高值的图片

固定宽高值的图片延迟加载比较简单,因为宽高值都可以设置在css中,只需考虑src的替换问题,推荐使用lazysizes。

// 引入js文件<script src="lazysizes.min.js" async=""></script>// 非响应式 例子<img src="" src="image.jpg" class="lazyload" />// 响应式 例子,自动计算合适的图片<img
    data-sizes="auto"
    src="image2.jpg"
    srcset="image1.jpg 300w,
    image2.jpg 600w,
    image3.jpg 900w" class="lazyload" />// iframe 例子<iframe frameborder="0"
    class="lazyload"
    allowfullscreen=""
    src="//www.youtube.com/embed/ZfV-aYdU4uE"></iframe>


注意:浏览器解析img标签的时候,如果src属性为空,浏览器会认为这个图片是坏掉的图,会显示出图片的边框,影响市容。



第一块是初始状态,第四块是成功状态,第二块第三块是影响市容的状态

lazysizes延迟加载过程中会改变图片的class:默认lazyload,加载中lazyloading,加载结束:lazyloaded。结合这个特性我们有两种解决上述问题办法:
1、设置opacity:0,然后在显示的时候设置opacity:1。

// 渐现 lazyload.lazyload,
.lazyloading{
    opacity: 0;
}
.lazyloaded{
    opacity: 1;
    transition: opacity 500ms; //加上transition就可以实现渐现的效果}

2、用一张默认的图占位,比如1x1的透明图或者灰图。

<img class="lazyload" 
    src="data:image/gif;base64,R0lGODlhAQABAAA
       AACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==" 
    src="真实url" 
    alt="<%= article.title %>">

此外,为了让效果更佳,尤其是文章详情页中的大图,我们可以加上loading效果。

.article-detail-bd {
    .lazyload {
        opacity: 0;
    }
    .lazyloading {
        opacity: 1;
        background: #f7f7f7 url(/images/loading.gif) no-repeat center;
    }
}

固定宽高比的图片

固定宽高比的图片延迟加载相对来说复杂很多,比如文章详情页的图片,由于设备的宽度值不确定,所以高度值也不确定,这时候工作的重心反倒放到了如何确定图片的高度上。

为什么要确定图片的高度呢?因为单个图片的加载是从上往下,所以会导致页面抖动,不仅用户体验很差,而且对于性能消耗很大,因为每次抖动都会触发reflow(重绘)事件,之前的博文 网站性能优化 之 渲染性能 也分析过重绘对于性能的消耗问题。


固定宽高比的图片抖动问题,有下列两种主流的方式可以解决:
1、第一种方案使用padding-top或者padding-bottom来实现固定宽高比。优点是纯CSS方案,缺点是HTML冗余,并且对输出到第三方不友好。

<div style="padding-top:75%">
    <img src="" alt="" class="lazyload"><div>


2、第二种方案在页面初始化阶段利用ratio设置实际宽高值,优点是html干净,对输出到第三方友好,缺点是依赖js,理论上会至少抖动一次。

<img src="" alt="" class="lazyload" data-ratio="0.75">


那么,这个padding-top: 75%;data-ratio="0.75"的数据从哪儿来呢?在你上传图片的时候,需要后台给你返回原始宽高值,计算得到宽高比,然后保存到data-ratio上。


好奇心日报采用的第二种方案,主要在于第一种方案对第三方输出不友好:需要对img设置额外的样式,但第三方平台通常不允许引入外部样式。

确定第二种方案之后,我们定义了一个设置图片高度的函数:

// 重置图片高度,仅限文章详情页function resetImgHeight(els, placeholder) {    var ratio = 0,
        i, len, width;    for (i = 0, len = els.length; i < len; i++) {
        els[i].src = placeholder;

        width = els[i].clientWidth; //一定要使用clientWidth
        if (els[i].attributes['data-ratio']) {
            ratio = els[i].attributes['data-ratio'].value || 0;
            ratio = parseFloat(ratio);
        }        if (ratio) {
            els[i].style.height = (width * ratio) + 'px';
        }
    }
}


我们将以上代码的定义和调用都直接放到了HTML中,就为了一个目的,第一时间计算图片的高度值,降低用户感知到页面抖动的可能性,保证最佳效果。

// 原生代码<img alt="" 
    data-ratio="0.562500" 
    data-format="jpeg" 
    class="lazyload" 
    src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg" 
    src="">// 解析之后的代码<img alt="" 
    data-ratio="0.562500" 
    data-format="jpeg" 
    class="lazyloaded" 
    src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg" 
    src="http://img.qdaily.com/uploads/20160807124000WFJNyGam85slTC4H.jpg" 
    style="height: 323.438px;">

我们不仅保存了宽高比,还保存了图片格式,是为了后期可以对gif做进一步的优化。


注意事项

1、避免图片过早加载,把临界值调低一点。在实际项目中,并不需要过早就把图片请求过来,尤其是Mobile项目,过早请求不仅浪费流量,也会因为请求太多,导致页面加载速度变慢。

2、为了最好的防抖效果,设置图片高度的JS代码内嵌到HTML中以便第一时间执行。

3、根据图片宽度设置高度时,使用clientWidth而不是width。这是因为Safari中,第一时间执行的JS代码获取图片的width失败,所以使用clientWidth解决这个问题。


4、JS/CSS按需打包

按需打包是webpack独特的优势,如果有需要通过此种方式来管理模块之间的依赖关系,强烈推荐引入!webpack门槛较高,可以看看我之前的博客:
webpack 入门
webpack 模块化机制


好奇心日报是典型的多页应用,为了缓存通用代码,我们使用webpack按需打包的同时,还利用webpack的CommonsChunkPlugin 插件抽离出公用的JS/CSS代码,便于缓存,在请求数量和公用代码的缓存之间做了一个很好的平衡。


5、延迟加载ga统计

async & defer属性

html5中给script标签引入了async和defer属性。

带有async属性的script标签,会在浏览器解析时立即下载脚本同时不阻塞后续的document渲染和script加载等事件,从而实现脚本的异步加载。
带有defer属性的script标签,和async拥有类似的功能。并且他们有可以附带一个onload事件<script src="" defer onload="init()">

async和defer的区别在于:async属性会在脚本下载完成后无序立即执行,defer属性会在脚本下载完成后按照document结构顺序执行。


由于defer和async的兼容性问题,我们通常使用动态创建script标签的方式来实现异步加载脚本,即document.write('<script src="" async></script>');,该方式也可以避免阻塞。


ga统计代码

ga统计代码采用就是动态创建script标签方案。
该方法不阻塞页面渲染,不阻塞后续请求,但会阻塞window.onload事件,页面的表现方式是进度条一直加载或loading菊花一直转。

所以我们延迟执行ga初始化代码,将其放到window.onload函数中去执行,可以防止ga脚本阻塞window.onload事件。从而让用户感受到更快的加载速度。


将ga加载绑定到onload事件上。


本文来源于网络,小编后续进行自己认识的修改,进行了部分调整

优网科技,优秀企业首选的互联网供应服务商

优网科技秉承"专业团队、品质服务" 的经营理念,诚信务实的服务了近万家客户,成为众多世界500强、集团和上市公司的长期合作伙伴!

优网科技成立于2001年,擅长网站建设、网站与各类业务系统深度整合,致力于提供完善的企业互联网解决方案。优网科技提供PC端网站建设(品牌展示型、官方门户型、营销商务型、电子商务型、信息门户型、DIY体验、720全景展厅及3D虚拟仿真)、移动端应用(手机站APP开发)、微信定制开发(微信官网、微信商城、企业微信)、微信小程序定制开发等一系列互联网应用服务。


我要投稿

姓名

文章链接

提交即表示你已阅读并同意《个人信息保护声明》

专属顾问 专属顾问
扫码咨询您的优网专属顾问!
专属顾问
马上咨询
扫一扫马上咨询
扫一扫马上咨询

扫一扫马上咨询