本文最初发表于博客园,并在GitHub上持续更新前端的系列文章。欢迎在GitHub上关注我,一起入门和进阶前端。

以下是正文。

前言

提升页面性能优化的方法有哪些:

  • 1、资源压缩合并,减少http请求
  • 2、非核心代码异步加载 —> 异步加载的方式 —> 异步加载的区别

如果回答出非核心代码异步加载,就会层层深入。

  • 3、利用浏览器缓存 —> 缓存的分类 —> 缓存的原理

缓存是所有性能优化的方式中最重要的一步,这个一定要答好。【重要】
有的人可能会回答local storage 和session storage,其实不是这个。浏览器缓存和存储不是一回事。

  • 4、使用CDN

浏览器第一次打开页面时,缓存是起不了作用的。CDN这一条,一定要说出来。

  • 5、DNS预解析

    一、资源压缩合并,减少http请求

  • 合并图片(css sprites)、CSS和JS文件合并、CSS和JS文件压缩

  • 图片较多的页面也可以使用 lazyLoad 等技术进行优化。
  • 精灵图等

二、非核心代码异步加载

异步加载的方式:(这里不说框架,只说原理)

  • 动态脚本加载
  • defer
  • async

动态脚本加载

使用document.createElement创建一个script标签,即document.createElement('script'),然后把这个标签加载到body上面去。
参考链接:

  • javascript 异步加载 动态脚本加载的那部分代码,看不太懂。

    defer

    通过异步的方式加载defer1.js文件: ```
  1. ### async
  2. > HTmL5新增特性。
  3. 通过异步的方式加载async1.js文件:
<script src="./async1.js" async></script>
### defer和async的区别

- 
defer:在HTML解析完之后才会执行。如果是多个,则按照加载的顺序依次执行。


- 
async:在加载完之后立即执行。如果是多个,执行顺序和加载顺序无关。



代码举例:

<!DOCTYPE html>

上方打印的结果是:

同步任务 defer1 defer2

因为defer的加载是有顺序的,所以两个引入defer文件按顺序执行。如果把引入的文件改为async的方式加载,打印的结果可能是:

同步任务 async2 async1

参考链接:

- [浅谈script标签的defer和async](https://segmentfault.com/a/1190000006778717)
## 三、利用浏览器缓存
**缓存**:资源文件(比如图片)在**本地的硬盘**里存有副本,浏览器下次请求的时候,可能直接从本地磁盘里读取,而不会重新请求图片的url。<br />缓存分为:

- 
强缓存


- 
协商缓存


### 强缓存
**强缓存**:不用请求服务器,直接使用本地的缓存。<br />强缓存是利用 http 响应头中的**`Expires`**或**`Cache-Control`**实现的。【重要】<br />浏览器第一次请求一个资源时,服务器在返回该资源的同时,会把上面这两个属性放在response header中。比如:<br />![](http://img.smyhvae.com/20180310_2310.png)<br />**注意**:这两个response header属性可以只启用一个,也可以同时启用。当response header中,Expires和Cache-Control同时存在时,**Cache-Control的优先级高于Expires**。<br />下面讲一下二者的区别。<br />**1、`Expires`**:服务器返回的**绝对时间**。<br />是较老的强缓存管理 response header。浏览器再次请求这个资源时,先从缓存中寻找,找到这个资源后,拿出它的Expires跟当前的请求时间比较,如果请求时间在Expires的时间之前,就能命中缓存,否则就不行。<br />如果缓存没有命中,浏览器直接从服务器请求资源时,Expires Header在重新请求的时候会被更新。<br />**缺点:**<br />由于`Expires`是服务器返回的一个绝对时间,存在的问题是:服务器的事件和客户端的事件可能不一致。在服务器时间与客户端时间相差较大时,缓存管理容易出现问题,比如随意修改客户端时间,就能影响缓存命中的结果。所以,在http1.1中,提出了一个新的response header,就是Cache-Control。<br />**2、`Cache-Control`**:服务器返回的**相对时间**。<br />http1.1中新增的 response header。浏览器第一次请求资源之后,在接下来的相对时间之内,都可以利用本地缓存。超出这个时间之后,则不能命中缓存。重新请求时,`Cache-Control`会被更新。
### 协商缓存
**协商缓存**:浏览器发现本地有资源的副本,但是不太确定要不要使用,于是去问问服务器。<br />当浏览器对某个资源的请求没有命中强缓存(也就是说超出时间了),就会发一个请求到服务器,验证协商缓存是否命中。<br />协商缓存是利用的是两对Header:

- 
第一对:`Last-Modified`、`If-Modified-Since`


- 
第二对:`ETag`、`If-None-Match`



ETag(Entity Tag):被请求变量的实体值”。<br />**1、`Last-Modified`、`If-Modified-Since`**。过程如下:<br />(1)浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上`Last-Modified`这个 response header,这个header表示这该资源在服务器上的最后修改时间:<br />![](http://img.smyhvae.com/20180311_1715.png)<br />(2)浏览器再次请求这个资源时,会加上`If-Modified-Since`这个 request header,这个header的值就是上一次返回的`Last-Modified`的值:<br />![](http://img.smyhvae.com/20180311_1716.png)<br />(3)服务器收到第二次请求时,会比对浏览器传过来的`If-Modified-Since`和资源在服务器上的最后修改时间`Last-Modified`,判断资源是否有变化。如果没有变化则返回304 Not Modified,但不返回资源内容(此时,服务器不会返回 Last-Modified 这个 response header);如果有变化,就正常返回资源内容(继续重复整个流程)。这是服务器返回304时的response header:<br />![](http://img.smyhvae.com/20180311_1720.png)<br />(4)浏览器如果收到304的响应,就会从缓存中加载资源。<br />**缺点:**<br />`Last-Modified`、`If-Modified-Since`一般来说都是非常可靠的,但面临的问题是:

- 
**服务器上的资源变化了,但是最后的修改时间却没有变化**。


- 
如果服务器端在一秒内修改文件两次,但产生的`Last-Modified`却只有一个值。



这一对header就无法解决这种情况。于是,下面这一对header出场了。<br />**2、`ETag`、`If-None-Match`**。过程如下:<br />(1)浏览器第一次请求一个资源,服务器在返回这个资源的同时,会加上`ETag`这个 response header,这个header是服务器根据当前请求的资源生成的**唯一标识**。这个唯一标识是一个字符串,只要资源有变化这个串就不同,跟最后修改时间无关,所以也就很好地补充了`Last-Modified`的不足。如下:<br />![](http://img.smyhvae.com/20180311_1735.png)<br />(2)浏览器再次请求这个资源时,会加上`If-None-Match`这个 request header,这个header的值就是上一次返回的`ETag`的值:<br />![](http://img.smyhvae.com/20180311_1737.png)<br />3)服务器第二次请求时,会对比浏览器传过来的`If-None-Match`和服务器重新生成的一个新的`ETag`,判断资源是否有变化。如果没有变化则返回304 Not Modified,但不返回资源内容(此时,由于ETag重新生成过,response header中还会把这个ETag返回,即使这个ETag并无变化)。如果有变化,就正常返回资源内容(继续重复整个流程)。这是服务器返回304时的response header:<br />![](http://img.smyhvae.com/20180311_1740.png)<br />(4)浏览器如果收到304的响应,就会从缓存中加载资源。<br />提示:如果面试官问你:与浏览器缓存相关的http header有哪些?你能写出来吗?这是一个亮点。<br />参考链接:

- [浏览器缓存知识小结及应用](d984b908fbd1b26d43078822d952a32f)[荐]
## 四、使用CDN
怎么最快地让用户请求资源。一方面是让资源在传输的过程中变小,另外就是CDN。<br />要注意,浏览器第一次打开页面的时候,浏览器缓存是起不了作任何用的,使用CDN,效果就很明显。
## 五、DNS预解析(dns-prefetch)
通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源,当浏览器真正使用到该域中的某个资源时就可以尽快地完成 DNS 解析。<br />**第一步**:打开或关闭DNS预解析<br />你可以通过在服务器端发送 X-DNS-Prefetch-Control 报头。或是在文档中使用值为 http-equiv 的meta标签:
<meta http-equiv="x-dns-prefetch-control" content="on">
需要说明的是,在一些高级浏览器中,页面中所有的超链接(`<a>`标签),默认打开了DNS预解析。但是,如果页面中采用的https协议,很多浏览器是默认关闭了超链接的DNS预解析。如果加了上面这行代码,则表明强制打开浏览器的预解析。(如果你能在面试中把这句话说出来,则一定是你出彩的地方)<br />**第二步**:对指定的域名进行DNS预解析<br />如果我们将来可能从 smyhvae.com 获取图片或音频资源,那么可以在文档顶部的  标签中加入以下内容:
<link rel="dns-prefetch" href="http://www.smyhvae.com/">

``` 当我们从该 URL 请求一个资源时,就不再需要等待 DNS 解析的过程。该技术对使用第三方资源特别有用。
参考链接:

我的公众号

想学习代码之外的技能?不妨关注我的微信公众号:千古壹号(id:qianguyihao)。
扫一扫,你将发现另一个全新的世界,而这将是一场美丽的意外:
10-01.页面性能优化 - 图1