# 前端性能优化篇
# 一、前言
# 知识体系: 从一道面试题说起
在展开性能优化的话题之前,我想先抛出一个老生常谈的面试问题:
从输入 URL 到页面加载完成,发生了什么?
- 这个问题非常重要,因为我们后续的内容都将以这个问题的答案为骨架展开。我希望正在阅读这本小册的各位可以在心里琢磨一下这个问题——无须你调动太多计算机的专业知识,只需要你用最快的速度在脑海中架构起这个抽象的过程——我们接下来所有的工作,就是围绕这个过程来做文章。
- 我们现在站在性能优化的角度,一起简单地复习一遍这个经典的过程:首先我们需要通过 DNS(域名解析系统)将 URL 解析为对应的 IP 地址,然后与这个 IP 地址确定的那台服务器建立起 TCP 网络连接,随后我们向服务端抛出我们的 HTTP 请求,服务端处理完我们的请求之后,把目标数据放在 HTTP 响应里返回给客户端,拿到响应数据的浏览器就可以开始走一个渲染的流程。渲染完毕,页面便呈现给了用户,并时刻等待响应用户的操作(如下图所示)。

我们将这个过程切分为如下的过程片段:
DNS解析TCP连接HTTP请求抛出- 服务端处理请求,
HTTP响应返回 - 浏览器拿到响应数据,解析响应内容,把解析的结果展示给用户
大家谨记,我们任何一个用户端的产品,都需要把这 5 个过程滴水不漏地考虑到自己的性能优化方案内、反复权衡,从而打磨出用户满意的速度。
# 从原理到实践:各个击破
- 我们接下来要做的事情,就是针对这五个过程进行分解,各个提问,各个击破。
具体来说,DNS 解析花时间,能不能尽量减少解析次数或者把解析前置?能——浏览器 DNS 缓存和 DNS prefetch。TCP 每次的三次握手都急死人,有没有解决方案?有——长连接、预连接、接入 SPDY 协议。如果说这两个过程的优化往往需要我们和团队的服务端工程师协作完成,前端单方面可以做的努力有限,那么 HTTP 请求呢?——在减少请求次数和减小请求体积方面,我们应该是专家!再者,服务器越远,一次请求就越慢,那部署时就把静态资源放在离我们更近的 CDN 上是不是就能更快一些?
以上提到的都是网络层面的性能优化。再往下走就是浏览器端的性能优化——这部分涉及资源加载优化、服务端渲染、浏览器缓存机制的利用、DOM 树的构建、网页排版和渲染过程、回流与重绘的考量、DOM 操作的合理规避等等——这正是前端工程师可以真正一展拳脚的地方。学习这些知识,不仅可以帮助我们从根本上提升页面性能,更能够大大加深个人对浏览器底层原理、运行机制的理解,一举两得!
我们整个的知识图谱,用思维导图展示如下:

# 二、网络篇 1:webpack 性能调优与 Gzip 原理
从现在开始,我们进入网络层面的性能优化世界。
大家可以从第一节的示意图中看出,我们从输入 URL 到显示页面这个过程中,涉及到网络层面的,有三个主要过程:
DNS解析TCP连接HTTP请求/响应
对于 DNS 解析和 TCP 连接两个步骤,我们前端可以做的努力非常有限。相比之下,HTTP 连接这一层面的优化才是我们网络优化的核心。因此我们开门见山,抓主要矛盾,直接从 HTTP 开始讲起。
HTTP 优化有两个大的方向:
- 减少请求次数
- 减少单次请求所花费的时间
这两个优化点直直地指向了我们日常开发中非常常见的操作——资源的压缩与合并。没错,这就是我们每天用构建工具在做的事情。而时下最主流的构建工具无疑是 webpack,所以我们这节的主要任务就是围绕业界霸主 webpack 来做文章。
# webpack 的性能瓶颈
相信每个用过 webpack 的同学都对“打包”和“压缩”这样的事情烂熟于心。这些老生常谈的特性,我更推荐大家去阅读文档。而关于 webpack 的详细操作,则推荐大家读读这本 关于 webpack 的掘金小册,这里我们把注意力放在 webpack 的性能优化上。
webpack 的优化瓶颈,主要是两个方面:
webpack的构建过程太花时间webpack打包的结果体积太大
# webpack 优化方案
1. 构建过程提速策略
1.1 不要让 loader 做太多事情——以 babel-loader 为例
babel-loader 无疑是强大的,但它也是慢的。
最常见的优化方式是,用
include或exclude来帮我们避免不必要的转译,比如webpack官方在介绍babel-loader时给出的示例:
