1. DNS 优化
1.1. DNS 预解析
以上面的代码为例,因为a.com 和b.com 是不同的域名,所以他会先对a.com 进行 dns 解析,拿到 ip 地址后去请求 1.js, 然后对 b.com 进行 dns 解析,拿到 ip 地址后去请求 2.js
正常流程下 b.com 的解析肯定是要等到1.js下载并执行完成才开始;
优化:我们可以先对a.com 和 b.com 同时进行2次的 DNS 查询,然后再依次的下载 1.js 和 2.js,这样就省掉了一个单独的 b.com 的 DNS 查询时间
具体实现:
1). 在 对应页面的 html 的 <head> 里写
<!-- href 里的是对应的域名,所以无需具体到文件名 -->
<link rel="dns-prefetch" href="http://a.com/">
<link rel="dns-prefetch" href="http://b.com/">
2). 在对应页面的 响应头里写
Link: <https://a.com/>; rel=dns-prefetch
2. TCP 连接优化
2.1. 连接复用
Connection:keep-alive
正常情况下我们每发送一个请求,都会创建一个 TCP 连接,请求完成后响应然后会关闭 TCP 连接,所以如果有多个请求就会是
开启 TCP 连接 -> 请求 -> 响应 -> 关闭 TCP 连接 -> 开启 TCP 连接
优化:上面的 关闭 TCP 连接 -> 开启 TCP 连接 明显就是很多余的,那么我们可不可以不让它关闭直接再次请求,这样就不会每次先关闭再开启
具体实现:在 http 的请求头和响应头里添加:Connection:keep-alive(通过添加 http 的字段可以实现 TCP连接的复用)
问题拓展:我们对 TCP 连接进行了优化后我们现在的流程就是
开启 TCP 连接 -> 请求1 -> 响应1 -> 请求2 -> 响应2
那么响应1和请求2中间隔了多少秒才会被认为是需要复用的那?
所以我们需要设置一个时间我们两次的 http 请求间隔是多少秒,可以不关闭复用,超过这个时间就关闭,这时候我们就需要用到KeepAlive: timeout=5,max=1000
这个意思就是如果你 5秒钟还不发起第二次请求,复用就会被关闭,max是最多请求 1000 次,请求头和响应头都可以设置KeepAlive,默认以响应头的为主
**注意:如果你使用的协议的版本号 是HTTP/1.1 及以上那么 Connection:keep-alive 是自动加的
2.2. 并行连接
同时发送多个请求,每个请求都重开一个 TCP 连接
并行连接有最大数量限制,一般是4-12个,这个限制跟域名有关
具体实现:尽量将 css 和 js 拆成 2-3 个
2.3. 管道化
在并行的同时还复用之 TCP 连接
问题:请求之间的顺序依赖,导致你这个并行没有办法达到最快的速度
解决办法:协议升级为 HTTP/2.0
3. HTTP/2 多路复用
HTTP/1.1 基于字符串
HTTP/2 基于帧(Frame),而帧是有9个字节+1个数据构成的
上图中左侧是 HTTP/1.1 的请求,右侧是2的
END_STREAM 前面如果是一个 + 表示'true',-是 false,所以+ END就表示结束了
:method和:scheme和:path 叫做伪头,这三个伪头代表1.1的请求行
多路复用
只建立一个 TCP 链接的情况下,发送多个并行的请求
4. 资源合并
css雪碧图
iconfont
svg
5. 资源内联
小图片 -> data URL
小 css 文件 -> <style>代码</style>
小 js 文件 -> <scirpt>代码</script>
6. 资源压缩
原理:把响应传给浏览器之前,先把响应压缩成一个 gzip 包,浏览器拿到这个包后解压
Nginx上面写个 gzip on,gzipTypes 可以指定对应的文件类型
7. 资源精简
HTML -> 删空格、删闭合
CSS -> 删除未用的
JS -> 改名、treeShaking
SVG -> 删无用标签、属性
图片 -> 减少体积
8. 减少 cookie 体积
cookie-free:使用新域名来避免使用本域名的cookie
9. 使用 cdn
- 优点:
- cookie-free
- 并行请求/多路复用
- 下载速度快
- 缺点
- 可控性差
- 跨域
cdn 会产生什么跨域问题?
- Canvas 虽然可以加载跨域图片,但是在调用 getImageData() toBlob() toDataURL() 时会产生报错,解决办法是启用 CORS 头,并给图片添加 crossorigin=anonymous 属性。详见 MDN
- window.addEventListener('error', ...) 无法捕获跨域 JS 的错误详情。解决办法有两个,一个是启用 CORS 头并给 script 标签添加 crossorigin=anonymous 属性,另一个比较开脑洞,是重写 addEventListener,详见《解决 "Script Error" 的另类思路》。
10. 代码优化
10.1. 代码位置
css引入写在head 里
原因:
1). css 不会阻塞 html 解析,尽早下载(如果 css 下载慢可能会出现白屏/闪烁)
2). 防止外部 js 阻塞
js 引入写在 body末尾
原因:
1). 可直接访问 dom,无须监听 dom ready
2). 避免阻塞 HTML 解析
10.2. js 动态导入
普通 js写法
const array = [1,2,3]
import('lodash').then( _ => {
const clone = _.cloneDeep(array)
})
vue 写法
const router = new VueRouter({
routes: [
{ path: '/home', component: () => import('./Home.vue') },
{ path: '/about, component: () => ({
component: import('./About.vue'),
loading: LoadingComponent,
error: ErrorComponent,
})},
]
})
react 写法
import React, { Suspense, lazy } from 'react'
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'
const Home = lazy(() => import('./routes/Home'))
const About = lazy(() => import('./routes/About'))
const App = () => (
<Router>
<Suspense fallback={LoadingComponent}>
<Switch>
<Route exact path="/" component={Home}/>
<Route path="/about" component={About}/>
</Switch>
</Suspense>
</Router>
)
10.3. 图片懒加载
懒加载就是一开始不加载,但需要用到的时候再加载。这听起来跟动态导入很像,不过懒加载一般指的是非 JS 资源,比如图片和样式
常见的懒加载思路举例:
1). 页面中有大量商品图片需要展示。假设代码为
<img src="product.jsp" >
2). 可以用一个 1k 大小的占位图片代替所有商品图片。代码改为
<img src="placeholder.png" data-src="product.jpg">
3). 在某个时刻(如页面加载的一秒钟后、用户滚动页面且快要看到下一页的产品时)使用 JS 去 加载商品图片,替换掉占位图片。
10.4. 预加载
懒加载导致一些资源的加载被推迟,影响了用户体验。所以我们需要提前对用户即将访问的资源进行加载
10.5. css 优化
1). 使用 uncss 删掉无用的 CSS
2). 使用更高效的选择器
3). 减少重排(reflow)。在比较多种样式修改方案时,尽量选择不会引起重排的方案。比如在做动画时,修改 transform 永
远比修改 left、top、bottom、right 更好,因为 transform 不会引起重排。
4). 不要使用 @import url.css; 因为被加载的 CSS 不能与当前文件并行下载。
10.6. js 优化
• 尽量不用全局变量,因为全局变量太多会使变量查找变慢。
• 尽量少操作 DOM,可以使用 Fragment 一次性插入多个 DOM 节点。
• 不要往页面中插入大量的 HTML,一定会卡。
• 尽量少触发重排,可以使用节流和防抖来降低重排频率。
• 尽量少用闭包,减少内存占用,避免内存泄漏(只有 IE 有内存泄露问题)。
11. 缓存
11.1. DNS 缓存
当我们访问一个网站,会先通过浏览器去问 windows,windows 会去问电信,电信知道 ip 就会告诉 windows,windows 就会把这个 ip 缓存下来,一般是一天,然后告诉浏览器,浏览器也会把这个 ip 缓存下来
11.2. HTTP 缓存
Cache-Control
public: 公开内容 (指定中间设备能不能对它进行缓存)
max-age=3600 缓存时间
must-revalidate 必须重新校验(缓存结束后的处理)/内容协商/弱缓存
11.2.1. 内容协商
协商的是缓存过期后还能不能重用
具体流程:在第一次请求的时候拿到 服务器给的Etag(文件 md5 后的一个字符串),下一次请求的时候在请求头设置一个If-None-Match: xxx(上面Etag的值),等文件的缓存到期了,我们就把这个ETag发给服务器,服务器如果发现Etag没变就返回给浏览器一个 304 和空的响应,如果发现 ETag 变了,就返回一个 200 和新的文件内容
11.3. 禁用缓存
11.3.1. 服务器发起
在有些情况下即使我们不设置Cache-Control 浏览器也会缓存
比如:
1). get 请求得到的响应一般会被缓存
2). 状态码是 200 203(非权威信息) 206(部分内容) 300(多选) 301(永久重定向) 410(已迁移) 都会被缓存
解决方法:
在响应头里设置 Cache-Control: max-age=0,must-revalidate
// 等价于上面的写法
Cache-Control: no-cache 不缓存可以协商
Cache-Control: no-store 不缓存不协商
11.3.2. 浏览器发起
1). 在请求的url后面加一个随机数
比如: a.js?11111
2). 请求的时候请求头设置 'Cache-Control', 'no-cache, no-store, max-age=0'
网友评论