在本博客中,对 JavaScript 代码和 CSS 代码进行了压缩,并且将压缩后的代码通过内联的信息放在了 HTML 中,用来节省多次 TCP 请求的时间。除了测试 JS、CSS 代码的压缩合并对博客的优化提升,还测试的使用 Base64 处理图片,以及对文章页的图片懒加载实现与优化。
JS 静态资源优化
博客使用 gulp-uglify
来压缩 JS 代码,首先安装:
1 | npm install --save-dev gulp-uglify |
然后创建 script
任务,并且添加到任务流中:
1 | gulp.task('script', function() { |
最后,原 JS 文件通过编译以后生成新的 JS 文件在 ./www/static/js
中。
CSS 静态资源优化
集成并压缩 CSS 文件
针对前一篇博客中提到的过多的 CSS 请求,会导致多次简历 TCP 连接,所以对网页的首屏渲染造成延迟的影响,所以我们要做的是,将不同的 CSS 文件合并成一个 CSS 文件,并且以内联的形式添加到 HTML 页面中。
我的博客自动化框架使用的是 Gulp,所以针对 Gulp,使用 PostCSS 框架,来进行 CSS 的自动添加前缀,CSS 文件的合并,CSS 文件的压缩和导出。
首先安装需要的插件:
1 | npm install --save-deve gulp-postcss |
并且在 gulpfile.js
中导入模块,创建 css
任务:
1 | let postcss = require('gulp-postcss'); |
根据我们的 css
任务的定义,我们要在 /view/output/
中创建结尾为 -combo.css
的 CSS 文件,用来表示合成的 CSS 文件,我们以 article-combo.css
文件为例,其中的内容为:
1 | @import '../layout/layout.css'; |
里面的内容很简单,就是使用 @import
将相关的 CSS 文件引入,然后 postcss-import
就可以根据 @import
进来的 CSS 文件路径来合成多个 CSS
文件。
除此以外,还需要创建的 CSS 文件有:
1 | admin-login-combo.css |
此时,可以删除 Pug 文件中原来的 CSS 文件,然后使用 include
来引入集成好的 CSS 文件。
最后删除原来 Pug 模板中的 CSS 文件,引入新的集成好的 CSS 文件。
我们可以对比一下 CSS 文件集成前后的区别:
首页:
因为首页传输的图片数量比较小,所以可以在 Chrome Network 里面设置网络的情况为:Regular 2G (250kb/s),并且关闭所有的缓存。当 CSS 文件没有被集成之前:
在 Load
事件被触发的时候,差不多用了 1s 的时间。
当我们设置使用集成的 CSS 文件以后,首页加载的情况如下:
从上图中,我们可以看到,网页中已经没有多余的 CSS 文件来影响首屏渲染速度,当 HTML 加载完成后,也就代表着 CSS 文件加载完成,此时的速度差不多为 650ms
,几乎是前面加载多个 CSS 文件的一半!
所以,由此我们可以看出,将多个 CSS 文件合并成一个 CSS 文件,并且通过内联模式引入到 HTML 页面中,可以节省很大一部分时间。
图片优化
在之间的前端性能测试中,图像的加载耗费了很大的时间,在首页上,涉及到的图片有 avatar.jpg
和 header-bg.jpg
,这两张图片的大小都是 303KB
,在加载的时候会消耗太长的时间,首先,我们先对这两张图片的大小进行修改。
我们将着两张图片都进行压缩,其中,对 avatar.jpg
压缩成 PNG 格式,并且将大小修改成 800px x 800px
,使用锐利压缩的方法,压缩后,avatar.png
的大小为 65KB
,header-bg.jpg
的大小为 37KB
。
下面我们进行测试:
首页:
在使用原有的图像的时候,我们看到,这两张图片的加载时间有 3.58s
。对网页的加载速度影响很大。
当我们使用新的压缩后的图片,测试的结果如下:
我们可以看到,现在图片加载的时间大约 170ms
,基本上没有图片慢慢加载出现的情况了。并且,为了让背景图片加载的时候,没有从白色到有色的“闪烁”情况出现,我们先对背景设定了一个相近的颜色,来缓冲图片加载时的效果:
1 | background-color: #DAF0FE; background-image: url('/img/header-bg.jpg'); |
文章页
因为文章页中包含了很多图片,所以对文章页的图片也需要进行压缩优化。
我们将图片的源文件存储在 /view/output/img
下,压缩以后的图片存储在 /www/static/img
下面。
我们将使用 gulp-imagemin
来实现对图片的自动压缩。首先安装 gulp-imagemin
1 | npm install --save-dev gulp-imagemin |
然后在 gulpfile.js
中添加自动压缩图片的任务:
1 | gulp.task('imagemin', function () { |
最后运行 gulp
,我们就可以在 /www/static/img/
下面得到新的被压缩后的图片。
由于我的博客的图片文件大多数都是 PNG 文件,所以我主要对比 PNG 文件的压缩效果。在当前的设置下,一般的 PNG 文件大多数的压缩率有 50% 左右,并且在显示上看不出太多的区别。
除了使用 gulp-imagemin
直接压缩图片以外,针对 PNG 文件,还可以使用 imagemin-pngquant
来进行压缩,首先我们需要安装 imagemin-pngquant
:
1 | npm install --save-dev imagemin-pngquant |
然后修改我们的 gulpfile.js
:
1 | gulp.task('imagemin', function () { |
由于 imagemin
不用每次构建都使用,一般只需要在发布或测试的时候一次性压缩就可以了。所以我将 imagemin
任务添加到 browser-sync
任务之前:
1 | gulp.task('default', ['imagemin', 'browser-sync'], function() { |
另外,使用 webp 格式,通常可以让图片文件大小大幅减小,所以,我也希望在自己的博客中添加对 webp 格式的支持。首先安装 gulp-webp
:
1 | npm install --save-dev gulp-webp |
然后定义 webp
任务,并且将该任务添加到 gulp
流程中。
1 | gulp.task('webp', function () { |
我们来看一下压缩的效果:
- 原图大小:145 KB
- 压缩后的 PNG 大小:70 KB
- 压缩后的 WebP 大小:10 KB
可以看到,压缩的效果是显著的。为什么我们有了 WebP 格式的图片后还需要使用 PNG 的图片呢?因为 WebP 并没有得到所有的浏览器的支持,所以我们还需要通过判断浏览器是否支持 WebP 来决定使用什么格式的图片。
检测 WebP 的支持
判断浏览器是否支持 webp 图片,可以检查请求头中的 Accept
字段是否包含 image/webp
如果包含 image/webp
字段,则选择图片格式为 webp
的文章内容发送给浏览器,如果不支持,则选择发送带有 png
格式图片的内容给浏览器。
下面我们可以测试一下:
使用不支持 webp
的 Safari 浏览器时,返回的 img(src)
的类型是 .png
使用支持 webp
的 Chrome 浏览器时,返回的 img(src)
的类型是 .webp
图片懒加载
图片懒加载就是将图片原有的 src
属性中的数据删除,放在一个自定义的属性中(本文使用的是 data-src
),当浏览器的显示部分加载到对应的位置时,才下载图片。
为了实现图片懒加载,首先在处理 HTML <img>
标签的时候,先把图片数据中 src
的部分提出来放在 data-src
中,本文使用的是正则表达式在上传 Markdown 文章,进行解析的时候用正则表达式进行处理。
1 | let reg = /<img([\s\S]+?)src\s*="([\s\S]+?).(png)"/g; |
由于没有图片的时候,图片的位置是被压缩的,所以在加载图片的时候,会突然出现一次页面的下移,对页面的显示效果影响不好,所以,即使在没有图片的时候,我们也需要对图片设置 width
和 height
,在页面中占据位置。
为了选择合适的 width
和 height
,我们在 Node.js 解析 Markdown 的时候就获取了图片的宽高,填写在 <img>
标签中。
1 | let reg = /<img([\s\S]+?)src\s*="([\s\S]+?).(png)"/g; |
我们使用了正则表达式来对 <img>
标签和 src
属性来进行匹配,使用了 image-size
库来获得图片的尺寸,并且将该尺寸添加到 width
和 height
属性中。
使用 Chrome 浏览器获得的 HTML 中 <img>
标签的内容如下所示:
使用 Safari 浏览器获得的 HTML 中 <img>
标签的内容如下所示:
最后,我们需要在 HTML 结构都加载完成以后,也就是 DOMContentLoaded
事件触发以后实现懒加载的功能。
1 | window.addEventListener("DOMContentLoaded", function(event) { |
然后就可以测试啦:
测试效果可见,当网页的还没有刷新到图片的区域时,图片就不会加载,节省了网络的传输资源和加载速度。
首页图片 Base64 处理
首页图片主要涉及两张图片:avatar.png
和 header-bg.jpg
,其大小分别是:36.5KB
和 63.5KB
。下面我们将对着两张图片进行 Base64 编码,并且添加到 CSS 中。
测试的网络环境是 Good 3G (40ms, 1.5Mb/s, 750kb/s)
首先,我们记录一下,不使用 Base64 编码的首页加载速度:
然后使用 Chrome Performance 查看首页渲染速度
从以上的图中我们可以统计如下:
1 | 出现文字,无图片: 300ms 左右 |
下面,我们将图片使用 Base64 编码加载,使用的库是 gulp-base64
:
首先,我们先给两张图片都添加了 Base64 编码,然后使用 Performance 进行测试,测试的结果如下:
1 | 出现文字及图片: 830ms 左右 |
将图片作为 Data URI 添加到 HTML 以后,HTML 的大小变得非常大,大小大于原来的 HTML 加上两张图片的大小之和。并且我们从 Performance 上看到,只有在所有的 HTML 加载完以后,浏览器才开始渲染,所以过大的 Data URI 影响了浏览器的关键渲染路径的速度。
所以,我们得到了以下的经验总结:
- 一般使用 Base64 处理的图片都比较小,并且整个网站的复用程度高,不然会影响页面的首屏渲染速度。
- Base64 处理的场景,大多是页面只有单个小图片,多个小图片其实可以用雪碧图来进行处理
- 针对添加了过大的 Base64 的 CSS 样式,可以用 localstorage 存储在用户的本地,但是其适用场景是该样式不经常发生变动和更新
所以用 Base64 来处理图片添加在 CSS 样式上这种方法,不适合本博客的优化场景。不过,可以在后续的优化中,将 header 中包括 Base64 处理后的 CSS 代码缓存在 localstorage 里面,用来节约博客加载的数据量。