Webcourse/14-前端面试/10-页面性能优化.md

278 lines
10 KiB
JavaScript
Raw Normal View History

2019-04-13 17:28:47 +08:00
> 本文最初发表于[博客园](https://www.cnblogs.com/smyhvae/p/8550195.html),并在[GitHub](https://github.com/qianguyihao/Web)上持续更新**前端的系列文章**。欢迎在GitHub上关注我一起入门和进阶前端。
2018-03-12 21:01:35 +08:00
> 以下是正文
## 前言
提升页面性能优化的方法有哪些
- 1资源压缩合并减少http请求
- 2**非核心代码异步加载** --> 异步加载的方式 --> 异步加载的区别
如果回答出`非核心代码异步加载`就会层层深入
- 3利用浏览器缓存 --> 缓存的分类 --> 缓存的原理
**缓存**是所有性能优化的方式中最重要的一步这个一定要答好重要
有的人可能会回答local storage 和session storage其实不是这个浏览器缓存和存储不是一回事
- 4使用CDN
浏览器第一次打开页面时缓存是起不了作用的CDN这一条一定要说出来
- 5DNS预解析
## 资源压缩合并减少http请求
- 合并图片css spritesCSS和JS文件合并CSS和JS文件压缩
- 图片较多的页面也可以使用 lazyLoad 等技术进行优化
- 精灵图等
## 非核心代码异步加载
异步加载的方式这里不说框架只说原理
- 动态脚本加载
- defer
- async
### 动态脚本加载
使用document.createElement创建一个script标签`document.createElement('script')`然后把这个标签加载到body上面去
参考链接
- [javascript 异步加载](https://www.jianshu.com/p/13cf23a90328) 动态脚本加载的那部分代码,看不太懂。
### defer
通过异步的方式加载defer1.js文件
```html
<script src="./defer1.js" defer></script>
```
### async
> HTmL5新增特性
通过异步的方式加载async1.js文件
```html
<script src="./async1.js" async></script>
```
### defer和async的区别
- defer在HTML解析完之后才会执行如果是多个则按照加载的顺序依次执行
- async在加载完之后立即执行如果是多个执行顺序和加载顺序无关
代码举例
```html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
<!--通过异步的方式引入两个外部的js文件-->
<script src="./defer1.js" defer></script>
<script src="./defer2.js" defer></script>
</head>
<body>
<script>
console.log('同步任务');
</script>
</body>
</html>
```
上方打印的结果是
```
同步任务
defer1
defer2
```
因为defer的加载是有顺序的所以两个引入defer文件按顺序执行如果把引入的文件改为async的方式加载打印的结果可能是
```
同步任务
async2
async1
```
参考链接
- [浅谈script标签的defer和async](https://segmentfault.com/a/1190000006778717)
## 利用浏览器缓存
2018-03-12 21:01:35 +08:00
**缓存**资源文件比如图片**本地的硬盘**里存有副本浏览器下次请求的时候可能直接从本地磁盘里读取而不会重新请求图片的url
缓存分为
- 强缓存
- 协商缓存
### 强缓存
**强缓存**不用请求服务器直接使用本地的缓存
2018-03-11 17:49:50 +08:00
强缓存是利用 http 响应头中的**`Expires`****`Cache-Control`**实现的重要
浏览器第一次请求一个资源时服务器在返回该资源的同时会把上面这两个属性放在response header中比如
2018-03-11 17:49:50 +08:00
![](http://img.smyhvae.com/20180310_2310.png)
2018-03-11 17:49:50 +08:00
**注意**这两个response header属性可以只启用一个也可以同时启用当response header中Expires和Cache-Control同时存在时**Cache-Control的优先级高于Expires**
下面讲一下二者的区别
2018-03-11 17:49:50 +08:00
**1`Expires`**服务器返回的**绝对时间**
2018-03-11 17:49:50 +08:00
是较老的强缓存管理 response header浏览器再次请求这个资源时先从缓存中寻找找到这个资源后拿出它的Expires跟当前的请求时间比较如果请求时间在Expires的时间之前就能命中缓存否则就不行
2018-03-11 17:49:50 +08:00
如果缓存没有命中浏览器直接从服务器请求资源时Expires Header在重新请求的时候会被更新
2018-03-11 17:49:50 +08:00
**缺点**
2018-03-11 17:49:50 +08:00
由于`Expires`是服务器返回的一个绝对时间存在的问题是服务器的事件和客户端的事件可能不一致在服务器时间与客户端时间相差较大时缓存管理容易出现问题比如随意修改客户端时间就能影响缓存命中的结果所以在http1.1提出了一个新的response header就是Cache-Control
2018-03-11 17:49:50 +08:00
**2`Cache-Control`**服务器返回的**相对时间**
2018-03-11 17:49:50 +08:00
http1.1中新增的 response header浏览器第一次请求资源之后在接下来的相对时间之内都可以利用本地缓存超出这个时间之后则不能命中缓存重新请求时`Cache-Control`会被更新
### 协商缓存
**协商缓存**浏览器发现本地有资源的副本但是不太确定要不要使用于是去问问服务器
2018-03-11 17:49:50 +08:00
当浏览器对某个资源的请求没有命中强缓存也就是说超出时间了就会发一个请求到服务器验证协商缓存是否命中
2018-03-11 17:49:50 +08:00
协商缓存是利用的是两对Header
2018-03-11 17:49:50 +08:00
- 第一对`Last-Modified``If-Modified-Since`
2018-03-11 17:49:50 +08:00
- 第二对`ETag``If-None-Match`
2018-03-12 21:01:35 +08:00
ETagEntity Tag被请求变量的实体值
2018-03-11 17:49:50 +08:00
**1`Last-Modified``If-Modified-Since`**过程如下
1浏览器第一次请求一个资源服务器在返回这个资源的同时会加上`Last-Modified`这个 response header这个header表示这该资源在服务器上的最后修改时间
![](http://img.smyhvae.com/20180311_1715.png)
2浏览器再次请求这个资源时会加上`If-Modified-Since`这个 request header这个header的值就是上一次返回的`Last-Modified`的值
![](http://img.smyhvae.com/20180311_1716.png)
3服务器收到第二次请求时会比对浏览器传过来的`If-Modified-Since`和资源在服务器上的最后修改时间`Last-Modified`判断资源是否有变化如果没有变化则返回304 Not Modified但不返回资源内容此时服务器不会返回 Last-Modified 这个 response header如果有变化就正常返回资源内容继续重复整个流程这是服务器返回304时的response header
2018-03-11 17:49:50 +08:00
![](http://img.smyhvae.com/20180311_1720.png)
2018-03-11 17:49:50 +08:00
4浏览器如果收到304的响应就会从缓存中加载资源
2018-03-11 17:49:50 +08:00
**缺点**
2018-03-12 21:01:35 +08:00
`Last-Modified``If-Modified-Since`一般来说都是非常可靠的但面临的问题是
- **服务器上的资源变化了但是最后的修改时间却没有变化**
- 如果服务器端在一秒内修改文件两次但产生的`Last-Modified`却只有一个值
这一对header就无法解决这种情况于是下面这一对header出场了
2018-03-11 17:49:50 +08:00
**2`ETag``If-None-Match`**过程如下
1浏览器第一次请求一个资源服务器在返回这个资源的同时会加上`ETag`这个 response header这个header是服务器根据当前请求的资源生成的**唯一标识**这个唯一标识是一个字符串只要资源有变化这个串就不同跟最后修改时间无关所以也就很好地补充了`Last-Modified`的不足如下
![](http://img.smyhvae.com/20180311_1735.png)
2浏览器再次请求这个资源时会加上`If-None-Match`这个 request header这个header的值就是上一次返回的`ETag`的值
![](http://img.smyhvae.com/20180311_1737.png)
3服务器第二次请求时会对比浏览器传过来的`If-None-Match`和服务器重新生成的一个新的`ETag`判断资源是否有变化如果没有变化则返回304 Not Modified但不返回资源内容此时由于ETag重新生成过response header中还会把这个ETag返回即使这个ETag并无变化如果有变化就正常返回资源内容继续重复整个流程这是服务器返回304时的response header
![](http://img.smyhvae.com/20180311_1740.png)
4浏览器如果收到304的响应就会从缓存中加载资源
提示如果面试官问你与浏览器缓存相关的http header有哪些你能写出来吗这是一个亮点
参考链接
- [浏览器缓存知识小结及应用](https://www.cnblogs.com/lyzg/p/5125934.html)[荐]
## 使用CDN
怎么最快地让用户请求资源一方面是让资源在传输的过程中变小另外就是CDN
要注意浏览器第一次打开页面的时候浏览器缓存是起不了作任何用的使用CDN效果就很明显
## DNS预解析dns-prefetch
通过 DNS 预解析来告诉浏览器未来我们可能从某个特定的 URL 获取资源当浏览器真正使用到该域中的某个资源时就可以尽快地完成 DNS 解析
2018-03-12 21:01:35 +08:00
**第一步**打开或关闭DNS预解析
你可以通过在服务器端发送 X-DNS-Prefetch-Control 报头或是在文档中使用值为 http-equiv 的meta标签
```html
<meta http-equiv="x-dns-prefetch-control" content="on">
```
需要说明的是在一些高级浏览器中页面中所有的超链接`<a>`标签默认打开了DNS预解析但是如果页面中采用的https协议很多浏览器是默认关闭了超链接的DNS预解析如果加了上面这行代码则表明强制打开浏览器的预解析如果你能在面试中把这句话说出来则一定是你出彩的地方
2018-03-12 21:01:35 +08:00
**第二步**对指定的域名进行DNS预解析
如果我们将来可能从 smyhvae.com 获取图片或音频资源那么可以在文档顶部的 <head> 标签中加入以下内容
```html
<link rel="dns-prefetch" href="http://www.smyhvae.com/">
```
当我们从该 URL 请求一个资源时就不再需要等待 DNS 解析的过程该技术对使用第三方资源特别有用
参考链接
- [前端性能优化 - 资源预加载](http://bubkoo.com/2015/11/19/prefetching-preloading-prebrowsing/)[荐]
- [DNS预解析详解](https://www.xuanfengge.com/dns-prefetching-analysis.html)
2018-03-12 21:01:35 +08:00
## 我的公众号
2019-02-03 14:06:03 +08:00
想学习<font color=#0000ff>**代码之外的技能**</font>****id`qianguyihao`
2018-03-12 21:01:35 +08:00
扫一扫你将发现另一个全新的世界而这将是一场美丽的意外
![](http://img.smyhvae.com/2016040102.jpg)