使用 PageSpeed Insights 给博客提速

使用 PageSpeed Insights 给博客提速

写在前面

最近一直处于空闲期,由于暂时没有工作压力,所以也不太想更一些技术博客(在非钻研时期强行写技术博客确实是一件比较无聊的事),就想着不如趁着有空给博客优化一下吧😑,没准能一劳永逸呢。然后就惊讶地发现...这工作还真的不简单,且容我慢慢道来。

现在主流的测速评分网站当属标题上提到的 Google PageSpeed Insights 了,这个是 Google 提供的检测工具,可以通过分析网页的内容提供网站加载速度优化建议,检测内容大概分为以下几个部分:

  1. 优化缓存:让你应用的数据和逻辑完全避免使用网络从而加速访问
  2. 减少回应时间:减少一连串请求-响应周期的数量
  3. 减小请求大小:减少上传大小
  4. 减小有效负荷大小:减小响应、下载和缓存页面的大小
  5. 优化浏览器渲染:改善浏览器的页面布局

然后我就对着评分报告优化了一天,结果就是从原来的 50 多分优化到 70 多分,然后由于使用了某种奇怪的操作又跌到了 60 多分,心情就像坐过山车一样😈,这个结果对我来说真的满(la)意(ji)的。

我的友链中已经有部分小伙伴的博客测速评分接近满分了,抛去🍋羡慕的因素,我仔细审视了一下差距,发现接近满分的绝大多数首页都比较「素」,或者说非常「简陋」的,当然这不是贬义,这更多是体现了做博客的审美观念不同,在我目前的审美观念中,确实很难为了提升分数去抛弃现有的外观。

另外一个原因,就是因为使用了 Google Fonts 托管的思源宋体😭,导致博客首次渲染超过 1s 以上的请求都是来自于字体加载,这个极大的拖慢了首次加载速度。

如何给博客提速

由于我的站点是基于 Java + FreeMarker 的动态博客,虽然 halo 框架在后台已经做了很多的优化,但是始终是比不上 Hexo、Hugo 等一系列静态框架的加载速度。如果你使用的是静态框架,在首页加载上略做优化超过 70 分应该是非常轻松的一件事。此外,由于我的博客使用了 Pjax,虽然可以保证整体浏览上比较迅速,但是所有需要用到的 js 文件必须放到公共 head 中加载或者在 Pjax 加载 complete 后进行动态嵌入,这导致 js 加载的十分集中,且无法将某个页面使用的局部 js 单独放到该页面中延迟加载。虽然可以使用 async 异步加载和 defer 延迟加载延缓一下,但是仍然解决不了根本问题,目前看来我对这个问题基本无解,所以这也是优化不上去的原因之一吧😒。

不过我折腾了一天多,也收获了一些提速方法,以下方法如果有误,还请在评论区指正😁。

图片懒加载

图片懒加载可以在较长的页面中延迟加载图片,直到它们由于用户滚动而出现到视窗中,可以将它看做图像预加载技术的反向运用。在包含很多大图片且较长页面中使用延迟加载,能使页面载入更快。浏览器在只加载可见区域的图片后就达到绪状态。在某些情况下,它也能帮助减少服务器端的负载。

如果你的站点首页跟我的一样,每篇文章都带一张图,那么懒加载手段应该是必须要使用的,图片加载请求耗时是相对很长的,这极大的影响了首页的加载速度。当然首页没图的就不用看这条了(悄悄告诉你,首页没图的在 PageSpeed 上评分都很高哦😜)。很多博客框架实际上都自带了懒加载插件,一般还是不需要我们操心的,比如 Sakura 这种带图的主题都内置了懒加载。但由于我这个是自己改的首页模板(模仿 Sakura 主题),所以我需要自己使用懒加载插件。

开源懒加载插件在 Github 上提供了很多,我这里为了方便大家,就推荐一个傻瓜式的懒加载插件,它的名字就叫 lazyload,使用方法非常简单,我们只需要在博客需要使用图片懒加载的页面引入如下 js 文件:

//CDN的lazyload插件
<script src="https://cdn.jsdelivr.net/npm/lazyload@2.0.0-rc.2/lazyload.js"></script>

然后在对应的图片 <img> 标签内做出如下修改,就是把图片真实的 src 路径放入 data-src 中,并给 img 标签增加 classlazyload 的属性, src 处不填时,默认初始加载时填充一张灰色的背景图,填写 src 时默认就加载你给定的图片,然后加载完成后会用 data-src 中的值替换 src 中的值实现懒加载的效果。

<img class="lazyload" data-src="img/example.jpg" width="765" height="574">
<img class="lazyload" src="img/example-thumb.jpg" data-src="img/example.jpg" width="765" height="574">

最后,在页面的 <script> 中还要加载一下这个插件,方式如下:

$("img.lazyload").lazyload();

注意使用懒加载时,最好在 <img> 标签中指定图片的 widthheight,这样页面在渲染时不用去计算图片的宽度和高度,而是能够直接响应,从而提升图片渲染速度。

图片压缩

图片压缩的重要性我在之前的博客中已经提到过,它的重要性是不言而喻的,除此之外,Google PageSpeed Insights 在测速报告中也给出了另外一个建议:

也就是采用新一代图片格式,给出的几个中使用相对广泛的应该是 WebP 格式,那么什么是 WebP 格式呢?

WebP 是由 Google 于 2010 年推出的新一代图片格式,在压缩方面比当前 JPEG 格式更优越。与 JPEG 相同,WebP 是一种有损压缩。但 Google 表示,这种格式的主要优势在于高效率。他们发现,在质量相同的情况下,WebP 格式图像的体积要比 JPEG 格式图像小 40%。(来源于维基百科)

我们可以对比一下 WebP 图片的大小和其它格式图片的大小差异。

一般而言,WebP 格式的图片体积都较小,这更有利于网页速度的加载,但不是绝对的,如果原始图片很小(比如 10k、20k 左右),有时候转换成 WebP 反而体积会变大,这说明不是所有时候都需要使用 WebP。但是在一些固定的背景图片中,使用 WebP 格式图片无疑是一种更好的选择。

推荐几个在线转换 WebP 的网站:又拍云智图iSparta

诚然,WebP 的确很优秀,但是也不是没有缺点,它最大的缺点应该就是兼容性不算优秀。但是能看到这篇博客的朋友们,至少使用的应该都是 Chrome、Firefox 或者 Safari 浏览器,而这些主流浏览器都已经支持了 WebP 格式图片,所以影响并不是很大。

保证字体始终可见

注:以下内容来自参考文章 [1] 和 [2]

在 PageSpeed 给出的建议有这么一条:

我们知道,为了网站的文字排版丰富便于阅读,我们使用一些在线字体是难以避免的,而因为中文字体体积很大,为了避免网络阻塞一般都是拆分成很多个小文件再进行加载,这样虽然解决了字体下载的一些痛点,但是仍然难免会受到网络状况的影响,继而干扰用户的体验。说实话,如果连页面展示都费劲,谁还会在乎你用多炫的字体呢。

为了减轻字体因为下载慢而加载不出来的风险,大多数浏览器都采用了超时处理。一旦超时,就使用后备字体。理想很美好,现实很无奈,浏览器在实现上各有自己的一套。

  • Chrome 和 Firefox 超时时间为 3 秒,超时后使用后备字体。若字体最终勉强加载成功,它将替换后备字体,重新渲染文本。
  • IE 浏览器超时时间为 0 秒,也就是说,会立即渲染文本。若所请求的字体尚不可用,则使用后备字体。一旦请求字体可用,将重新渲染文本。
  • Safari 没有超时行为(或者说,至少在基准网络超时之前什么也没干)。

更糟糕的是,这些规则对应用造成的影响,在很大程度上不受开发者控制。因此 CSS 工作组为此提出了新方案:为 @font-face 增加新的 font-display 声明,用于控制字体下载完成之前的渲染行为。与一些浏览器目前使用的字体超时行为类似,font-display 将字体下载过程划分为三个阶段。

  1. 首先是字体阻塞阶段(font block period)。在此期间,如果字体未完成加载,则尝试使用它的任何元素,必须以不可见的后备字体形式呈现;否则正常使用该字体。

  2. 紧接着字体阻塞阶段的是字体交换阶段(font swap period)。在此期间,如果字体未完成加载,则尝试使用它的任何元素,必须使用后备字体进行渲染;否则正常使用该字体。

  3. 紧接着是字体故障阶段(font failure period)。此时若字体未完成加载,则将其标记为下载失败,并使用常规后备字体;否则正常使用该字体。

理解以上三个阶段,根据字体是否下载成功、何时下载完成等情况,就可以使用 font-display 来决定如何渲染字体了。font-display 中可以选择如下属性:

  • auto:默认值。典型的浏览器字体加载的行为会发生,也就是使用自定义字体的文本会先被隐藏,直到字体加载结束才会显示。
  • swap:后备文本立即显示直到自定义字体加载完成后再使用自定义字体渲染文本。在大多数情况下,这就是我们所追求的效果。
  • fallback:这个可以说是 auto 和 swap 的一种折中方式。需要使用自定义字体渲染的文本会在较短的时间(100ms according to Google)不可见,如果自定义字体还没有加载结束,那么就先加载无样式的文本。一旦自定义字体加载结束,那么文本就会被正确赋予样式。
  • optional:效果和 fallback 几乎一样,都是先在极短的时间内文本不可见,然后再加载无样式的文本。不过 optional 选项可以让浏览器自由决定是否使用自定义字体,而这个决定很大程度上取决于浏览器的连接速度。如果速度很慢,那你的自定义字体可能就不会被使用。

举个例子:

@font-face { 
    font-family: "Open Sans Regular"; 
    font-weight: 400; 
    font-style: normal; 
    src: url("fonts/OpenSans-Regular-BasicLatin.woff2") format("woff2"); 
    font-display: swap; 
}

在这个例子里我们通过只使用WOFF2文件来缩写字体。另外我们使用了swap作为font-display的值,页面的加载情况将如下图所示:

那后备文字是什么呢?当你给一个元素指定font-family时,你可以指定一系列的字体,并通过逗号来分隔这些字体。在自定义字体后的那些字体如果能得到浏览器的支持,那么这个字体就是后备字体

p { 
    font-family: "Open Sans Regular", "Helvetica", "Arial", sans-serif; 
}

在上面这个例子中,自定义字体是 Open Sans Regular,系统字体是 HelveticaArial。当我们将 font-display 的值设置为 swap 的时候,最开始会先使用系统字体来显示文字,当自定义字体下载完毕后,自定义字体就会取代系统字体。当 font-display 的值设置为 fallbackoptional 时,显示的字体将会取决于在决定如何处理自定义字体时系统堆栈中的后备字体是什么字体。

关于 CSS 和 JS 的优化

压缩 CSS 和 js 应该是最基本的方法,这个对网站加载速度的提升是巨大的,可以使用在线工具或者其它编辑器压缩插件完成压缩,除此之外,我们还需要注意一点小细节,就是在外部引入的 CSS 和 JS 的位置关系。

所谓外部引入,就是我们使用 <link rel="stylesheet" href="/xxx.min.css"> 引入的 CSS 和 <script type="text/javascript" src="/xxx.min.js"></script> 引入的 JS 文件。

外部引入的 CSS 应该放在 head 头部内,而外部引入的 JS 应该放在 结束标签之前的位置。

举个例子:

<html>
<head>
   <title>Sanarous's Blog</title>
   //所有外部 CSS 全部放在这里,最好不要放 js
   <link rel="stylesheet" href="/xxx.min.css"></link>
</head>
<body>
  <div>主体内容</div>

//所有外部引入的 js 全部放在这里
<script type="text/javascript" src="/xxx.min.js"></script>
</body>
</html>

按照上述规范可以提升一定的浏览器加载速度。

开启 gzip 压缩

gzip 是一种压缩技术。经过 gzip 压缩后页面大小可以变为原来的 30% 甚至更小,这样,用户浏览页面的时候速度会快得多。gzip 的压缩页面需要浏览器和服务器双方都支持,实际上就是服务器端压缩,传到浏览器后浏览器解压并解析。浏览器那里不需要我们担心,因为目前的大多数浏览器都支持解析 gzip 压缩过的资源文件。在实际的应用中我们发现压缩的比率往往在 3 到 10 倍,也就是本来 50k 大小的页面,采用压缩后实际传输的内容大小只有 5 至 15k 大小,这可以大大节省服务器的网络带宽,同时如果应用程序的响应足够快时,网站的速度瓶颈就转到了网络的传输速度上,因此内容压缩后就可以大大的提升页面的浏览速度。

如果你的网站使用的是 Nginx 作为服务器,那么可以在 Nginx 端开启 gzip 压缩,在 nginx.conf 配置文件中的 http{} 之间添加如下配置:

# 打开gzip压缩
gzip on;

# 不压缩临界值,大于1K的才压缩,一般不用改
gzip_min_length 1k;

# 设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流,这里设置以16k为单位的4倍申请内存
gzip_buffers 4 16k;

# 默认为http 1.1,现在99.99%的浏览器基本上都支持gzip解压了,所有无需设置此项
# gzip_http_version 1.0;

# gzip压缩比,1 最小处理速度最快,9 最大但处理最慢(传输快但比较消耗cpu)
gzip_comp_level 2;

# 要压缩的文件类型,注意"text/html"类型无论是否指定总是会被压缩的
gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/javascript application/x-httpd-php image/jpeg image/gif image/png;

# on的话会在Header里增加"Vary: Accept-Encoding",给代理服务器用的,有的浏览器支持压缩,有的不支持,所以避免浪费不支持的也压缩,所以根据客户端的HTTP头来判断,是否需要压缩
# 我这里的浏览器肯定支持gzip压缩,所以就不开启此功能了
gzip_vary off;

# IE6对Gzip不怎么友好,不给它Gzip压缩了
gzip_disable "MSIE [1-6]\.";

如果是 SpringBoot 构建的博客或网站,那么开启 gzip 更加简单,可以直接在 application.yml 或者 application.properties 中添加配置:

server:
   compression:
       enabled: true
       mime-types: application/javascript,text/css,application/json,application/xml,text/html,text/xml,text/plain

其它优化项(待更新)

其它的优化方面在 PageSpeed 报告中应该已经详细给出了,我们只需要根据报告修改就行,如果我发现了一些有意思的加速手段,我会在后面继续更新。

Nginx 使用 PageSpeed 模块加速网站访问

以下只针对使用 Nginx 作为网站服务器的站点,如果你是静态博客并且托管在 Github Pages 上的话,以下都可以忽略。本来这部分我是准备单独写一篇博客的,考虑到都是 Google 出品而二者本身就有关系,那么不妨写在一起。

PageSpeed 是一个款 Google 开源用来自动优化网站的神器!通过改写 HTML、CSS、JS 文件源码以及一些简单配置就可以达到加速网站的效果,几乎涵盖了 Google PageSpeed Insights 所有的优化建议。支持 Apache 和 Nginx,鉴于 Apache 已是日薄西山,以下基于 Nginx 的 PageSpeed 扩展模块做介绍。

Github 官方地址

官方文档

在 Nginx 中安装 PageSpeed 模块

如果你能看到这里的话,说明你对 Nginx 的安装和使用具备了一定的基础,所以过于啰嗦的话我这里也不再细说,只说重点。

以我的使用环境为例:

  • Ubuntu 16.04
  • Nginx 1.8.0

我用的 Nginx 版本比较老,出的问题也比较多,如果是高版本的话出的问题会少很多。另外,你应该是正在使用 Nginx 作为服务器,所以不需要卸载原来的 Nginx,而是只进行替换。所以你需要重新下载并解压一个与你正在使用的 Nginx 版本相同的 tar.gz 压缩包。

//此处与你正在使用的 nginx 版本相同
sudo wget --no-check-certificate -c http://nginx.org/download/nginx-1.13.9.tar.gz
sudo tar xzf nginx-1.13.9.tar.gz

下载 ngx_pagespeed 最新的扩展模块,使用以下命令,另外,编译 pagespeed 时依赖 psol 模块,不过后面会提示如何下载,所以先不管。

sudo wget https://github.com/pagespeed/ngx_pagespeed/archive/latest-beta.tar.gz
sudo tar xzf latest-beta.tar.gz

然后我们准备编译 Nginx,在此之前你需要先去你正在使用的 Nginx 版本中拿到原来的编译信息。比如我正在使用的 Nginx 路径为 /usr/local/nginx ,当前准备替换的路径为 /home/ubuntu/nginx,那么需要去 /usr/local/nginx/sbin 中使用 sudo ./nginx -V 获取编译信息:

ubuntu@VM-0-8-ubuntu:/usr/local/nginx/sbin$ sudo ./nginx -V 
nginx version: nginx/1.8.0
built by gcc 7.4.0 (Ubuntu 7.4.0-1ubuntu1~18.04.1) 
built with OpenSSL 1.0.1o 12 Jun 2015
TLS SNI support enabled

//这里就是原来的编译信息
configure arguments: --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_ssl_module --with-openssl=/usr/local/openssl-1.0.1

复制之后回到 /home/ubuntu/nginx ,也就是刚解压的 Nginx 中,使用命令:

sudo ./configure --add-module=/home/ubuntu/ngx_pagespeed-latest-beta --with-http_stub_status_module --with-http_ssl_module --with-openssl=/usr/local/openssl-1.0.1

其中 --add-module=/home/ubuntu/ngx_pagespeed-latest-beta 后面的路径就是解压的 ngx_pagespeed 文件夹位置,而后面的 with-xxx 就是复制的编译信息,注意不要带 --prefix,如果带了的话不要覆盖正在使用的 Nginx 文件夹,可以指定为其它路径。

这时会提示 checking for psol ... not found,并提示你如何操作,操作如下,注意版本号可能不同。

cd ngx_pagespeed-latest-beta/
sudo wget https://dl.google.com/dl/page-speed/psol/1.12.35.2-x64.tar.gz
sudo tar xzf 1.12.34.2-x64.tar.gz

这意思就是我们进入到解压后的 ngx_pagespeed 文件夹中,在这个里面下载 psol 依赖库并解压放到里面即可。注意 psol 依赖库文件夹一定要放到解压后的 ngx_pagespeed 中。

重新 ./configure,这时候应该不会再报错。

然后,我们使用 sudo make 命令编译(注意不要 make install,不然就覆盖安装了)。

如果出现 objs/Makefile:460: recipe for target 'objs/src/core/ngx_murmurhash.o' failed 错误,直接去 Nginx 目录下的 objs 文件夹下找到 MakeFile 文件,使用 sudo vim MakeFile 将第三行的 -Werror 去掉就可以了。然后回去重新 make,直到没有报错信息并且编译成功。

make 成功后,我们首先将原来的 Nginx 进行备份,进入 /usr/local/nginx/sbin 中,使用以下命令备份:

cp -r nginx nginx.bak

然后进入到编译好的 Nginx 文件夹中的 objs 文件夹,找到 nginx,将它覆盖掉原来的 Nginx。

cp -r /home/ubuntu/objs/nginx /usr/local/nginx/sbin/

这样就更新完成了,验证方法如下:

//进入目录
cd /usr/local/nginx/sbin

//查看编译信息
sudo ./nginx -V

如果出现了你刚才新增的模块信息,就说明添加成功了。

配置 PageSpeed 模块

这个可以参考官方文档的说明,配置项非常多,需要根据自己的站点情况进行分析。

PageSpeed 配置官方文档

首先在 /usr/local/nginx/conf 中新建一个 pagespeed.conf 文件,使用 vim 或其它编辑器在其中写入以下配置信息(供参考):

# on 启用,off 关闭
pagespeed on;

# 重置 http Vary 头
pagespeed RespectVary on;

# html字符转小写
pagespeed LowercaseHtmlNames on;

# 压缩带 Cache-Control: no-transform 标记的资源
pagespeed DisableRewriteOnNoTransform off;

# 相对URL
pagespeed PreserveUrlRelativity on;

# 开启 https
pagespeed FetchHttps enable;

# 配置服务器缓存位置和自动清除触发条件,缓存文件夹如果不存在则需要自行创建,建议放在内存中
pagespeed FileCachePath “/dev/shm/ngx_pagespeed/”;
pagespeed FileCacheSizeKb 2048000;
pagespeed FileCacheCleanIntervalMs 43200000;
pagespeed FileCacheInodeLimit 500000;

# 过滤规则
pagespeed RewriteLevel PassThrough;

# 过滤WordPress的/wp-admin/目录(可选配置,可参考使用)
pagespeed Disallow "*/wp-admin/*";
pagespeed Disallow "*/wp-login.php*";

# 移除不必要的url前缀,开启可能会导致某些自动加载功能失效
#pagespeed EnableFilters trim_urls;

# 移除 html 空白
pagespeed EnableFilters collapse_whitespace;

# 移除 html 注释
pagespeed EnableFilters remove_comments;

# DNS 预加载
pagespeed EnableFilters insert_dns_prefetch;

# 压缩CSS
pagespeed EnableFilters rewrite_css;

# 合并CSS
pagespeed EnableFilters combine_css;

# 重写CSS,优化加载渲染页面的CSS规则
pagespeed EnableFilters prioritize_critical_css;

# 压缩js
pagespeed EnableFilters rewrite_javascript;

# 合并js
pagespeed EnableFilters combine_javascript;

# 优化内嵌样式属性
pagespeed EnableFilters rewrite_style_attributes;

# 压缩图片
pagespeed EnableFilters rewrite_images;

# 不加载显示区域以外的图片
pagespeed LazyloadImagesAfterOnload off;

# 图片预加载
pagespeed EnableFilters inline_preview_images;

# 移动端图片自适应重置
pagespeed EnableFilters resize_mobile_images;

# 图片延迟加载
pagespeed EnableFilters lazyload_images;

# 扩展缓存 改善页面资源的可缓存性
pagespeed EnableFilters extend_cache;

然后进入 nginx.conf 中,在配置的 server{} 块中增加配置:

server {
    listen       443 ssl;
    server_name  localhost;
+    include pagespeed.conf;
   ......

接着进入到 /usr/local/nginx/sbin 中,使用 ./nginx -t 命令测试配置文件是否有错误,如果没有错误就可以使用 ./nginx -s reload 重新加载 Nginx,此时就可以刷新博客页面查看效果了。

参考文章

  1. 使用 font-display 提升使用自定义字体(font-face)时的性能与用户体验
  2. font-display的用法
  3. 前端性能优化之gzip
  4. Nginx 部署 PageSpeed 模块

本文由 Sanarous 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可,转载前请务必署名
本文链接:https://bestzuo.cn/posts/pagespeed.html
最后更新于:2020-07-03 22:22:28

切换主题 | SCHEME TOOL