性能优化
追求的是什么, 是你的网页可以 以最快的速度 打开, 比如说用户点一下啪的就开了
点哪里哪里开, 什么操作都是立刻有反馈.
关键字是:速度
试想未来有一天, 到了 18G 时代, 每个人的网速都是 1000M 的, 那你还优化什么
你的网站就算是 100M 大小也不怕.
可是那是遥远的未来, 当下网速还没有这么快。
所以我们的问题是: 在一个比较低一点的网速上面, 尽可能的快的加载出来我们的页面.
固定一个变量: 网速.
所以问题变成: 在固定网速为 p 的条件下, 如何尽可能的让我们的页面快一点加载出来.
假设我们的资源大小是 n, 网速为 p, 理论上将资源, 从服务器拿到客户端需要的时间
是:
time = n/p
p 固定, 那么 n 越小, time 越小.
也就是说: 我们的资源的体积越小, 时间越短.
所以问题变成: 如何减少我们资源的大小?
先说我们的资源的种类有哪些?
images
js
css
html
fonts
others
依次看看怎么减小它们的体积:
图片压缩
js 压缩
css 压缩
html 压缩
fonts 删减
others 不知道
这些都是建立在你当前已经存在的资源的上面, 还可以做预处理:
切图的时候就不要切高清无码图
不要引入没有必要的 js, 或者说你为了兼容 Object.assign() 直接引了一个 babel-polyfill这样
不要闭着眼就把 bootstrap 引入进来了
...
上面的讨论, 实际上都是有一个前提的, 就是说:
所有的资源都从 server 到 client 之后, client 才能看见页面
但是这并不是真的:
有时只要你拿回来一部分资源的时候, 页面就可以显示出来了.
所以我们可以对资源做一些区分, 将 '页面显示出来所需要的最小资源集合' 优先返回回去,
剩下的资源再说, 这样也是一种思路。
所以我们的问题变成: 找到那个最小的资源集合, 或者说, 合理的安排资源的顺序。
how?
依旧是那些资源类型:
images
js
css
html
fonts
others
images 完全可以做懒加载, 不需要那么早就扔出去.
第一次要展示的页面是 pageA, 那么 pageB 相关的资源, 你就不要先返回啊.
这方面相关的一些概念是:
首屏渲染
按需加载
code spliting
critial css
...
其实这句话说的好像是 server 去安排资源的顺序一样, 但是你也知道不是的, server说我就是无脑
扔, 你自己安排。
所以我们才会去让 link 提前加载, 让 script 在后面加载.
上面算是第一个阶段的结束, 现在我们更进一步
前面说了:
time = 资源大小 / 网速
实际上这个模型可以比喻成这样:
小明从 A 点到 B 点拿一堆总重量为 100kg 的东西, 小明需要多久才能把东西全部从 B ->A?
和小明每次拿多少, 以及他一趟要多久相关, 是不是?
所以上面提及到的 '网速' 这个概念, 是一个混合变量
'网速' = ('带宽','请求相应时间')
带宽, 也就是每次拿多少, 一般都是假设是一个固定的值
那么就剩下请求响应时间了, 这个涉及到的点,
就是: 接口响应要快.
前面讨论的时候, 都是假设, 只有小明一个人在搬, 但是如果有 10个人呢?
这里涉及到的就是:
浏览器的并行加载
这玩意是浏览器提供的, 我们谈我们的性能优化手段, 貌似和他也没有关系, 这个是浏览器做的事情.
不不不, 我们可以利用.
从这个角度来看, 是不是并行加载的数量越多越好? 就是的.
那么我们可以尝试去提高并行加载的数量啊, 比如说从 1 提高到 100
这里涉及到的是:
浏览器的并行加载是以 domain 为区分而限制的, 一个 domain 最多可以支持 4个(不同浏览器不同)
并行加载.
那么我们把资源分成多个域名不就可以了吗? 好像是这个道理.
你有100个资源, 划分成 25个域名, 同一个域名下面支持 4个并行, 那你不是一瞬间100个都发出去了吗
那速度不是唰的一下就上来了吗? (这块没弄明白)
那再想想, 还有什么办法能让时间更短一点?
让 A 跟 B 近一点啊.
这个就是 CDN.
也就是说, 我们可以上 CDN, 这个的确是我们做的.
上面的都是说第一次加载, 第一次访问的时候, 还有第二次第三次访问的情况, 这个时候就涉及到缓存了。
我们说下缓存
以我记忆里面的概念, 在性能优化的时候提到的缓存一般有三种情况:
浏览器缓存
通过使用 storage 进行缓存
Ajax 缓存
为什么要缓存?
因为当你第一次访问一个网站, 请求了 100个资源.
然后你第二次又访问了这个网站, 又请求了 100个资源.
把这两个 '100个资源'做并集, 就会发现并集里面有很多资源.
也就是说你在两次访问这个网站的时候, 对于同一个资源, 你请求了两次.
这肯定是不必要的, 浪费的. 所以我们完全可以搞起来.
怎么搞?
拿到多次访问的时候, 每次请求的资源, 作为一个集合
做这些集合的并集
得到的集合, 就是在这多次访问中都没有变化的资源, 称为 A
我们的目标就是: 让 A 中所有的资源都仅仅请求一次就好了.
1 得到 A
我们要怎么得到 A ? 资源是我们弄出来的啊, 代码是你写的,
你对资源都很清楚的, 所以你自己就知道哪些资源是一直都不变的, 哪些资源是一段时间就变的
哪些是每次都变的, 你很清楚啊.
好吧, 那我给你一个所有的不变的资源 A
2 再说下缓存是谁缓存? 浏览器啊, 那浏览器怎么知道这一堆资源里面哪个要缓存, 哪个不要?
你知道, 所以你要去告诉他.
你怎么告诉他? http 协议啊, 那只能是 http headers 字段里面标记了啊.
这里不具体说 协议头长什么样子, 说下方案:
server 告诉 client, 这个文件要缓存, 这个文件什么时候过期
在下一次访问的时候, 浏览器请求的时候呢发现了这个文件, 看看它什么时候过期, 发现没有过期
那就用, 哎一发现过期了, 那么就去重新请求. 就是保质期的意思.
第二种方案是 storage 做缓存, 我印象中记得看见过两个:
微信
美团的 LsLoader
第三种就是 Ajax 请求缓存, 通常见于 query 类型的接口的缓存, 比如说商品列表等.
最后一个讨论点, 我们之前只是说资源拿回来, 页面显示, 但是这两者之间, 还有个事情:
浏览器需要去渲染资源
我们说浏览器的渲染过程, 到底在说什么, 是在说整个流程, 输入资源, 输出页面.
步骤大体上可以这么描述
输入html -> 解析
-> 合并成 render tree -> layout -> paint ->结束.
输入css -> 解析
layout: 就是安排 render tree 上面的每一个节点的位置, 大小
paint: 就是绘制每一个节点.
流程是这样, 那还有啥问题.
(1) 下载和解析是不是同步的, 不是, 是边下载边解析, 不需要等到所有的资源都下载结束才解析
也就是说: 来了 html 就解析html, 来了 css 就解析 css
(2) 在渲染的过程中, 如果遇见 js 怎么办? 是继续渲染还是停下, 先执行 js?
其实就两个答案, 要么继续, 要么停下来.
继续的话, 就是说让 js 在我渲染之后再执行, 别着急, 好的, 渲染结束之后, js 里面唰的一下执行一下:
document.write('');
这意味着, 我所有的渲染的努力都白费了.
与此相比, 在渲染的时候遇见 js 等一下, 等它执行结束, 再继续执行, 这样防止全盘努力白费,
风险小点, 所以还是等吧.
所以在渲染的过程中, 如果遇见 js, 那么就停下来, 执行它.
那能不能让 js 告诉我它到底有没有就是说, 更改 DOM, 有时候它的确不会啊, 这个时候我就白等了.
所以有 defer 和 async
这个就相当于说,
defer, defer啊, 告诉你, 你不用等我, 你继续渲染, 我不会改你的, ok
这个时候, js 就会在浏览器渲染之后再执行.
async 呢?
这个就坑爹了, 这个说, 你别等我, 我也不等你, 我反正下下来之后就执行
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。