基础知识
DNS、TCP、三次握手、四次挥手、HTTP/2、keepalive、浏览器渲染原理、CDN、懒加载、预加载、按需加载...
DNS 与 TCP
三次握手
SYN 是 synchronize (同步)的缩写,同步不是“同步异步”中的同步,而是“同步信息”中的同步。x 和 y 一般都是从 0 开始的。ACK 是 acknowledge(知道,获悉)的缩写。 为什么需要第三次?防止错误的情况发生,B 需要确认 A 没有挂。
四次挥手
关闭时 x 和 y 一般都不是 0
HTTP 请求与响应
关于 Content-Type 的格式可以查询 MIME Type。
浏览器渲染原理
JS 下载和执行会阻塞 HTML 的解析,为什么? 一是因为 JS 执行中可能会修改 DOM 树;二是以前浏览器处理 script 标签时会先下载 JS(为什么以前浏览器这样做,因为以前的 HTML 文件还小,解析非常快,即使做了这块优化意义不大)。
script 标签上添加 async 和 defer 的区别? https://zh.javascript.info/script-async-defer
defer 不会阻塞 HTML 解析,总是要等到 DOM 解析完成才会执行 script ,而且 JS 执行完后不久才会 DOMContentLoaded,多个 defer script 的执行会按照源码书写的顺序来。 async 也不会阻塞 HTML 解析,async script 不用等待其他 script,与 DOMContentLoaded 也不会彼此等待,但是如果 HTML 还没有解析完成遇到 async script 下载完成要执行时会被阻塞。 另外 type="module" 几乎等价于 defer,实际工作中 defer 较多点,async 少一点。
JS 的执行被 CSS 阻塞
JS 要等紫色的两条线(CSS 的下载和解析)完成后才能执行。为什么? 因为 JS 需要读取 CSS 的结果。比如 CSS 里面有个 h1 的样式高度 200px,JS 需要获取 h1 的高度。
如何判定 reflow 和 repaint? https://csstriggers.com/ 重新 Layout 就叫 reflow,重新 Paint 就叫 repaint。 reflow 有改变元素宽度,repaint 有改变元素背景颜色。
自说:CSS 动画很卡的话,请避免 Layout 的计算,如使用 transform 。
Web 性能指标
- 优化什么?可测量的指标 比如用户按下回车,有内容出现,DOM ready(DOMContentLoaded) 事件发生,页面可交互时,onLoad 事件发生,动态资源加载完成。
DNS 预解析
该技巧稍微起效果
在 index.html 的 <head>
里面写:
<link rel="dns-prefetch" href="https://abc.com/">
在 index.html 的响应头里写:
Link: <https://abc.com/>; rel=dns-prefetch
TCP 连接复用
Connection: keep-alive
如果是 HTTP/1.1 这是默认设置,如果是 HTTP/1.0 则需要手动添加。 上一次的响应和这一次的请求间隔时间是多少,怎么做?服务器设置 KeepAlive: timeout=5, max=1000
,浏览器请求方也可以设置。
并行化连接
多写几个 link ,并行连接有最大连接数。限制是跟域名有关(通过添加域名突破限制)。如果有个连接没有发起,就会接入某已完成的连接进行复用(可通过 Connection ID 查看),一般都是 http/1.1 ,推荐网址: https://www.mop.com/
HTTP 管道化(pipelining)
此功能会有 bug ,所以浏览器会关闭。bug 是什么?某些连接使用 pipelining ,其中有一个连接需要等某一个连接完成才能输出。所以没办法达到提升速度的目的,还不如 并行化连接,最好的方法是升级到 HTTP/2 。
多路复用
HTTP/2的帧是什么
使用二进制,前面 9 个字节(Length、Type、Flags、StreamID)一共 72 位,最后一部分 Payload 可以很大,最大达到 16 M 。所有数据(比如响应体,响应头等等)都放在 Payload 里面,其它的仅仅是用来做标记。
HTTP/2 的请求与响应
HTTP/2的流与多路复用 有多个流(Stream),每个流有 n 个 Frame。可以有多少个流,没有限制。每个流里面只有一次请求和一次响应。
服务器推送ServerPush
配置起来比较繁琐。 第一种:
location / {
root /usr/share/nginx/html;
index index.html index.htm;
http2_push /style.css;
http2_push /example.png;
# ... 还有很多需要添加
}
第二种:
location / {
# ...
http2_push_preload on;
}
然后,后端在 index.html 响应头里加 Link:</style.css>;rel=preload;as=style
。
Cookie Free 与 CDN
资源合并 CSS Sprites、Icon Font(iconfont.cn)、 SVG Symbols
资源内联 小图片 -> data URL (url-loader) 小 CSS 文件 ->
<style>代码</style>
(html-webpack-inline-source-plugin) 小 JS 文件 -><script>代码</script>
资源压缩(gzip) Nginx Apache Node.js
资源精简 HTML -> 删空格、删闭合 CSS -> 删未用(不推荐使用) JS -> 改名、tree shaking SVG -> 删无用标签、属性 图片 -> 减少体积(有损/无损)
减少 Cookie 体积 因为历史原因(以前没有 LocalStorage 功能),导致一些开发者喜欢往 Cookie 添加数据。 可以通过沟通协商,一起发力去组织这件事,没有办法的话就使用 新的域名 去解决(这叫 Cookie-free)。
CDN (内容分发网络) 如何把文件上传到 CDN ?可以花钱购买服务;发布时用命令行上传文件;改写 HTML(修改资源路径)。 优点 cookie-free,天然支持并行请求/多路复用(启用 HTTP/2),下载速度快。 缺点:花钱,部署复杂,可控性差(自己搭建 CDN 服务),跨域问题(CORS 解决)
CDN 会出现什么样跨域问题?
- Canvas 虽然可以加载跨域图片,但是在调用 getImageData() toBlob() toDataURL() 时会产生报错,解决办法是启用 CORS 头,并给图片添加 crossorigin=anonymous 属性。详见 MDN
- window.addEventListener('error', ...) 无法捕获跨域 JS 的错误详情。解决办法有两个,一个是启用 CORS 头并给 script 标签添加 crossorigin=anonymous 属性,另一个比较开脑洞,是重写 addEventListener,详见《解决 "Script Error" 的另类思路》。
- Nginx 上两种 gzip 模式的区别:
gzip on;
和gzip_static on;
前者会在每次请求时压缩文件,有一点浪费 CPU;
而后者会在遇到 /path/to/file 请求时,主动寻找 /path/to/file.gz 作为压缩版本,找不到就直接返回未压缩的版本。 后者的优先级更高,且由于使用的是程序员预先压缩好的 gz 文件,所以更节省 CPU 性能,但是它可能并不是 Nginx 默认就有的功能,需要在编译 Nginx 的时候添加扩展选项。
HTTP 缓存与内容协商
俗称强缓存和弱缓存。
Cache-Control 释义 public,公开内容,与之相反是 private max-age=3600,缓存时间 must-revalidate,必须重新校验,这里的校验也叫内容协商。
内容协商 缓存过期之后还能重用吗?
根据 If-None-Match: ETag 值判断是否更新对应文件。
两套缓存(一旧一新)
- 服务器禁用缓存 不加 Cache-Control 浏览器还会缓存吗? 会,根据 HTTP 状态码而来,比如 301 永久重定向会缓存结果。
如何完全禁用?手动在响应添加 Cache-Control: max-age=0, must-revalidate
,意思是文件到达浏览器时缓存失效,能不能进行内容协商?可以(must-revalidate)。根据 MDN 说明,上面写法跟 Cache-Control: no-cache
一样的。怎样可以不进行内容协商?Cache-Control: no-store
,既不能缓存又不能协商的效果达到了。
浏览器禁用缓存 如何服务端发起的请求使用了缓存,导致该请求无法使用最新的,怎么办?一种是规避这个请求(使用不一样的 URL 即可),可以添加随机数进行规避。一种在 header 添加
Cache-Control: 'no-cache, no-store, max-age=0'
Pragma 仅需了解即可,用来兼容很久的设备。 https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Headers/Pragma
代码层面的优化
代码位置、代码拆分、动态导入、图片懒加载、预加载、CSS 优化、JS 优化
代码位置 CSS 放在 head 标签里,一是不阻塞 HTML 解析,尽早下载,二是防止被外部 JS 阻塞;JS 放在 body 标签末尾,一是可直接访问 DOM,无需监听 DOM ready 事件,二是避免阻塞 HTML 解析;内联 JS 尽量放在 CSS 前面。
白屏和闪烁 CSS 文件下载慢到底是白屏还是闪烁?不同的浏览器出现的效果不一样,大多数浏览器会等到 CSS 文件下载好才会显示页面,这个过程中是白屏(Chrome),但是有的情况是闪烁(Firefox)。
代码拆分 可以差分成 vendor、main、comon 等等文件格式。
动态导入 按需加载,缺点是耗一点点时间。 Vue 文档给出的规范 https://v2.vuejs.org/v2/guide/components-dynamic-async.html
图片懒加载
可以提前加载资源(预加载)
CSS 优化
第一条不推荐使用,因为 JS 会添加新的 class name 。
JS 优化
虚拟滚动 https://juejin.cn/post/6844904183582162957