优化博客的网页性能

Photo by rawpixel.com from Pexels

未优化前的性能指标

使用 Google 的 PageSpeed Insights 评估,移动设备得分:91,各项指标如下:

指标时间(估算值)
首次内容绘制时间1.9 秒
速度指数3.5 秒
可交互前的耗时3.6 秒
首次有效绘制时间3.0 秒
首次 CPU 闲置时间3.4 秒
输入延迟(估算值)30 毫秒

优化建议

这些优化建议可以加快网页加载速度。

优化建议有望节省的总时间(估算值)
1. 适当调整图片大小4.05 s
2. 采用新一代格式提供图片2.4 s
3. 移除阻塞渲染的资源0.93 s
4. 移除未使用的 CSS0.3 s

使用 gulp 压缩 html, css, js, images 等

gulp 让你用代码实现一组任务的串行或者并行执行。

一个典型的 gulpfile.js 可能是这样的:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const { src, dest, parallel } = require('gulp')
const minifycss = require('gulp-minify-css')
const uglify = require('gulp-uglify')
const htmlmin = require('gulp-htmlmin')
const htmlclean = require('gulp-htmlclean')
const imagemin = require('gulp-imagemin')

const minifyHtml = () =>
src('./public/**/*.html')
.pipe(htmlclean())
.pipe(
htmlmin({
removeComments: true,
minifyCSS: true,
minifyURLs: true,
minifyJS: true
})
)
.pipe(dest('./public'))

const minifyCss = () =>
src('./public/**/*.css')
.pipe(
minifycss({
compatibility: 'ie8'
})
)
.pipe(dest('./public'))

const minifyJs = () =>
src('./public/**/*.js')
.pipe(uglify())
.pipe(dest('./public'))

const minifyImage = () =>
src('./public/images/*.*')
.pipe(
imagemin(
[
imagemin.gifsicle(),
imagemin.jpegtran(),
imagemin.optipng(),
imagemin.svgo()
],
{ verbose: true }
)
)
.pipe(dest('./public/images'))

const build = parallel(minifyHtml, minifyCss, minifyJs, minifyImage)

module.exports = {
default: build
}

移除阻塞渲染的资源

PageSpeed tools 给出的优化建议如下:

资源阻止了系统对您网页的首次绘制。请考虑以内嵌方式提供关键的 JS/CSS 并推迟提供所有非关键的 JS/样式。

网址大小有望节省的时间
…dist/jquery.fancybox.min.css(cdn.jsdelivr.net)4 KB780 ms
/css?family=…(fonts.googleapis.com)1 KB780 ms
…css/font-awesome.min.css(cdn.jsdelivr.net)7 KB930 ms
/css/main.css?v=7.1.0(cddbysj.github.io)10 KB330 ms

从上面的表格可以看出,都是形如 <link rel="stylesheet" href="**/*.css"> 这样的标签发起请求 CSS 文件导致阻塞渲染。

前置知识

在所有的 HTML 标签中,涉及到引用或者请求外部资源的,主要有以下几个标签:

  • <script src='script.js'></script>
  • <link href='stylesheet' ref='style.css'/>
  • <img src='image.png'/>
  • <a href='https://somethere.com'>To somewhere<a/>

那么 hrefsrc 这两个属性有何区别呢?

以下说明来自 Stackoverflow 的一个对 difference-between-src-and-href 问题的回答:

There is a differentiation between src and href and they can’t be used interchangeably. We use src for replaced elements while href for establishing a relationship between the referencing document and an external resource.

href (Hypertext Reference) attribute specifies the location of a Web resource thus defining a link or relationship between the current element (in case of anchor a) or current document (in case of link) and the destination anchor or resource defined by this attribute. When we write:

<link href="style.css" rel="stylesheet" />
The browser understands that this resource is a stylesheet and the processing parsing of the page is not paused (rendering might be paused since the browser needs the style rules to paint and render the page). It is not similar to dumping the contents of the css file inside the style tag. (Hence it is advisable to use link rather than @import for attaching stylesheets to your html document.)

src (Source) attribute just embeds the resource in the current document at the location of the element’s definition. For eg. When the browser finds

<script src="script.js"></script>
The loading and processing of the page is paused until this the browser fetches, compiles and executes the file. It is similar to dumping the contents of the js file inside the script tag. Similar is the case with img tag. It is an empty tag and the content, that should come inside it, is defined by the src attribute. The browser pauses the loading until it fetches and loads the image. [so is the case with iframe]

This is the reason why it is advisable to load all JavaScript files at the bottom (before the tag)

需要注意的是,上面的回答由于是几年前的,<script> 标签的 asyncdefer 属性还没有加入到标准中。它们的作用如下:

  1. <script src='script.js' async>
    脚本会异步执行,即不会中断浏览器解析页面,在可行的时候才执行脚本。
  2. <script src='script.js' defer>
    脚本会在页面解析完成后才执行。
  3. <script src='script.js'>
    浏览器会中断页面的解析,转而下载、编译、执行该脚本,完成后再继续解析页面。

分析

以第一项 CSS 资源为例,这是 fancybox 的 CSS 样式表,博客首页完全用不到,可以推迟提供。通常的做法是将 <link> 标签转化为 <script 标签,然后稍后动态地创建 <link> 标签。

1
2
3
// 获取 <head> 标签内的 <link> 标签
const styleLinks = [...document.querySelectorAll('link[rel=stylesheet]')]
const links = styleLinks.filter(el => el.parentNode === document.head)

对于其他的 CSS 资源,更合理精细的做法是只保留影响首页的那些 CSS 规则,其余的都推迟提供。

参考链接