HTML页面加载速度优化


页面加载速度优化主要有6点:[1]

  1. 减少请求数量,合并请求
  2. 减少请求文件的体积
  3. 使用内容分发网络(cdn)
  4. 合理使用缓存
  5. 了解html页面展示的原理,合理放置资源文件引入的位置
  6. 减少dns查找,避免重定向
  1. 减少请求数量,合并请求
1-1 js、css等资源文件 合并、压缩,仅有一个小css或js可写到html文件中

可使用gulp-concat合并文件,gulp-uglify压缩js文件,gulp-clean-css压缩css文件;
若压缩需要指定文件顺序,则需要引入gulp-order,

var gulp= require('gulp');
var order = require('gulp-order');
gulp.task('concat-js-inorder', function() {
    gulp.src('js/*.js')
        .pipe(order([
            'js/index.js',
            'js/detail1.js',
            'js/*.js'
        ], {base: '.'}))
        .pipe(concat('index-concat.js'))
        .pipe(uglify())
        .pipe(gulp.dest('../src/public/'))
});

若压缩需要指定任务顺序,则需要引入run-sequence,return runSequence( 'task1-name','task2-name','task3-name');

1-2 图片资源可合并为雪碧图,背景平铺类图片可base64写到html文件中
  • 合并为雪碧图

(制作雪碧图,我使用的工具是CssSprite,还可以使用gulp自动生成),用css语法background-image:url()引入图片,background-position进行定位
tips:制作雪碧图时,图片之间最好留有空白(gulp.spritesmith 可配置图片间隔),否则若页面使用rem进行自适应,可能会产生有小数点后的像素,图片边缘可能被切割,有兴趣的朋友可以看一下淘宝前端团队的这篇文章——rem 产生的小数像素问题
缺点是修改略麻烦,如果是单个图片,新增(需修改css)或修改相应图片即可,雪碧图新增或修改其中一张图则需要重新制作雪碧图 并修改css其实用gulp实现自动化生成图片和css样式语句,修改也不麻烦,详细代码可见文末

  • 图片地图

我个人认为图片地图是用来在一整张图片上做效果的,比如做个科普图,都是树和花草,点某朵花就告诉你它的简介。当然 如果用它来合并图片减少请求也可以做到。我之前二次开发的一个小网站,有个需求就是将页面上某张图中带有的几个logo,做到 点击进入相应官网,就是用的map和area,我们正常开发网站,固定的导航也可以这样做。

  • 背景平铺类图片作为内联图片

base64编码写到html img中
<img src="data:image/png;base64,xxxxxxxxxxxxxxx">
写到css中
.body-bgd {
background:url("data:image/png;base64,xxxxxxxxxxxxx")
}

1-3 字体图标

直接引入字体包,不用引入图片,与文字一样 可以用css控制其大小 颜色 方向,没有放大或缩小图片失真的问题。我用的字体图标库是阿里的iconfont,需要。还有见到有人推荐Font Awesome。

1-4 屏幕外资源延迟加载

例如图片懒加载,不在可视区域内的图片地址为占位图片地址,当其进入到可视区域后再加载真正的图片地址。[2][3]

  1. 减少请求文件的体积
2-1 发布版本时js、css等资源文件进行压缩、弱混淆

(gulp-uglify压缩js, gulp-clean-css压缩css,gulp-htmlmin压缩html)

2-2 对图片进行无损压缩[4]

(gulp-imagemin,gulp-tinypng-nokey)也可以在线在tinypng官网上在线上传,压缩。个人测试 tinypng的压缩率会大一些,但图片边缘会出现灰色线条?下图左侧是imagemin压缩的 右侧是tinypng压缩的,所以项目中我还是会继续使用imagemin。


左图imagemin压缩后放大,右图tinypng压缩后放大
2-3 服务器端进行压缩,开启gzip

request header:通知server,client端支持的文件压缩格式
Accept-Encoding:gzip,deflate,br
response header: 告诉client返回的文件压缩格式是什么
content-encoding:gzip
(apache的mod_deflate可以配置对哪些文件进行压缩或不压缩,DeflateCompressionLevel参数可以配置压缩比1到9,数字越大表示压缩比越大文件越小 但占用cpu资源越多,可考虑使用大家都使用的6)
tips: 若中间使用了代理,代理缓存了内容,若存在不支持gzip的浏览器和支持gzip的浏览器都需要访问服务器的资源,则会发生代理返回给支持gzip的浏览器未压缩的内容(不支持gzip的浏览器先访问了资源,未压缩的内容被缓存了),或代理返回给不支持gzip的浏览器压缩过的内容(支持gzip的浏览器先访问了资源,压缩过的内容被缓存了)。解决此问题需要response中增加header,Vary:Accept-Encoding ,告诉代理服务器根据一个或多个请求头来改变缓存的响应

  1. 使用内容分发网络(cdn)

自己的服务器有带宽和节点数量限制,使用云服务商提供的cdn服务,能缩短文件所在服务器与用户的距离,加快文件传输速度。
阿里云的cdn可根据目录或文件后缀名设置缓存策略(过期时间)

  1. 合理使用缓存
4-1. Cache-Control [5]

no-store // 所有内容都不缓存
no-cache // (无论本地缓存是否过期)每次都需要询问服务端是否有更改,没有更改则使用缓存, 有更改则重新拉取不缓存
must-revalidation/proxy-revalidation // 本地缓存过期了,则一定要向服务器/代理以进行有效性验证[6]
private: 只允许client端缓存,不允许代理缓存
public: 所有内容都会缓存,客户端和代理服务器都可以缓存
s-maxage // 用于共享缓存,比如CDN缓存(s -> share)。与max-age 的区别是:max-age用于普通缓存,而s-maxage用于代理缓存。如果存在s-maxage,则会覆盖max-age 和 Expires.
max-age=600 // 缓存一段时间。单位 秒
tips:pragma header头是为了向后兼容HTTP1.1之前的版本 ,现使用cache-control即可

4-2. 设置Expires

Expires: Mon, 19 Nov 2018 06:40:49 GMT
Expires优先级低于Cache-Control

4-3. 设置ETag、Last-Modified

若缓存过期了,向服务器请求验证,若上次的response中有Etag则header头中增加If-None-Match;若上次的response中有Last-Modified则header头中增加If-Modified-Since;
Etag的优先级高于Last-Modified。
服务器集群不适合开启Etag,不同服务器上生成的Etag可能不同,会造成相同的内容重复下载,导致性能下降(Apache默认通过FileEtag中FileEtag INode Mtime Size的配置自动生成ETag(当然也可以通过用户自定义的方式)[7])

  1. 合理放置资源文件引入的位置

css文件放在文件头, js文件放在文末
css文件若放在末尾易导致无样式内容闪烁?
FOUC flash of unstyles content 产生原因是没有吧样式表放在head顶部,或者使用了@import导入(即便放在前面了,样式表还是会最后下载)
js不放在文末易导致白屏
脚本放在顶部会阻塞后面内容的呈现和组件的下载。进而产生白屏现象。
放在底部将会产生最小影响和最佳效应。
(关于js单线程,html页面从无到有的流程)

  1. 减少dns查找,避免重定向

浏览器同域名最大并发连接数一般为4或6[8],如果页面需要很多资源,资源分布在不同的域名上 会增加dns查找时间,但能够减少由于浏览器最大并发链接导致的等待时间。所以要平衡二者,不要使用太多域名,如果有很多资源 也不要放在一个域名下。
页面重定向,重定向到正确页面展示之前,页面都是空白的,尽量不要使用重定向。

附录

  1. gulp实现自动化生成雪碧图和css样式语句,并压缩
var gulp= require('gulp');
var runSequence = require('run-sequence');
var spritesmith = require('gulp.spritesmith');
var pngquant = require('imagemin-pngquant');
var cache = require('gulp-cache');
gulp.task('spriteImageTask', function() {
    return gulp.src('img/test/*.png')
        .pipe(spritesmith({
            imgName: 'sprite.png',
            cssName: 'sprite.css',
            padding:4,
            algorithm: 'top-down',
            cssTemplate:function (data) {
                var arr=[];
                data.sprites.forEach(function (sprite) {
                    arr.push(".sprite-"+sprite.name+
                    "{" +
                    "background-image: url('"+sprite.escaped_image+"');"+
                    "background-size:"+sprite.total_width+'px '+sprite.total_height+"px;"+
                    "background-position: "+sprite.offset_x/2+"px "+sprite.offset_y/2+"px;"+
                    "width:"+sprite.width/2+"px;"+
                    "height:"+sprite.height/2+"px;"+
                    "}\n");
                });
                return arr.join("");
            }
            // cssTemplate: 'img/test/spriteHandler.css'
        }))
        .pipe(gulp.dest('img/test/sprite/'));
});
gulp.task('imageminTask', function() {
    return gulp.src('img/test/sprite/sprite.png')
        .pipe(cache( // 缓存,只有图片修改了才会再次执行图片压缩
            imagemin({
            // 测试中是否加以下选项对png图片无影响???
            // optimizationLevel: 5, //类型:Number  默认:3  取值范围:0-7(优化等级)
            // progressive: true, //类型:Boolean 默认:false 无损压缩jpg图片
            // interlaced: true, //类型:Boolean 默认:false 隔行扫描gif进行渲染
            // multipass: true, //类型:Boolean 默认:false 多次优化svg直到完全优化
            // use: [pngquant()], //使用pngquant深度压缩png图片的imagemin插件
            }))
        .pipe(gulp.dest('../src/public/images/test/'));
});
gulp.task('spriteImg', function() {
    return runSequence('spriteImageTask', 'imageminTask');
});

参考文章


  1. Web前端性能优化——如何提高页面加载速度

  2. 延迟加载图像和视频

  3. Offscreen Images

  4. gulp-imagemin、gulp-tinypng-compress、gulp-tinypng-nokey图片压缩优化详解及对比

  5. 彻底理解浏览器缓存机制

  6. web性能优化之:no-cache与must-revalidate深入探究

  7. HTTP响应头之ETag

  8. 浏览器同域名请求的最大并发数限制

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 200,302评论 5 470
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 84,232评论 2 377
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 147,337评论 0 332
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,977评论 1 272
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,920评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,194评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,638评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,319评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,455评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,379评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,426评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,106评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,696评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,786评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,996评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,467评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,043评论 2 341