前端的一些八股
明天是第一次面试, 但是几乎完全没背八股, 昨天的 ai 面试差点把我问住了. 在这里浅浅写一下目前知道的八股.
性能优化
性能优化我个人认为有三个方面, 编译性能, 加载性能和运行性能. 第一个主要面向开发者/运维, 后两个主要面向普通用户.
编译性能
这一块我不太了解, 也没怎么遇到过编译性能的问题.
加载性能
加载又分为首次访问和再次访问.
首次访问
对于首次访问, 加载性能的优化有这些办法:
- CDN 加速
根据用户地区选择最佳 CDN 来获取静态资源.
- 静态资源放在多个域名下
浏览器对同一个域名有最大连接数, 在静态资源很多的情况下, 可以考虑将静态资源放在不同的域名下, 这样可以一次加载更多资源.
- DNS prefetching
这个是昨天 ai 面试问到的, 差点被问住, 好在有一点点了解.
众所周知, 在访问一个网页时需要先进行 DNS 解析(如果是域名访问), 然后根据 ip 建立 tcp 连接, 发送 http 请求, 拿到一个 html 并解析.
在解析过程中, 浏览器有一个主解析线程和一个预加载扫描器, 这个预加载扫描器会提前扫描 html, 遇到静态资源就会进行下载. 这里不同资源对主解析线程的影响不同.
- css 阻塞渲染但不阻塞解析
- js 默认阻塞解析
- 图片等媒体文件 不阻塞渲染也不阻塞解析
反正这个预加载扫描器遇到一个静态资源就会进行下载. 如果域名没在 dns 缓存里找到就会进行 dns 解析, 这是个耗时操作. 我们没有办法加速这个 dns 解析速度, 但是可以提前这个 dns 解析, 只需要在 head 里加上一个这样的标签
<link
rel="dns-prefetch"
href="https://example.com"
/>
这样预加载扫描器扫描到这个就会进行 dns 解析.
这里还有一个工程化的问题, 人工插入这个东西是不太可能的, 最好是使用什么插件, 打包后就在 index.html 里插入. 大概的思路是分析打包产物, 遍历所有 html, js, css 文件, 用正则匹配出一个 https 链接, 然后就可以一键插入了.
- preload
以一张图片为例, 一般预加载扫描器扫描到一张图片时才会开始下载, 使用 preload 的话可以在扫描到这个 preload 时就开始下载, 无论后面有没有这个图片. 使用这个可以提前下载某些资源.
<link
rel="preload"
href="/preload.png"
as="image"
/>
还有一个prefetch, 当浏览器空闲的时候就会试图加载这个东西. 这会把该资源的加载优先级放到最低.
- 异步的js
在script标签里加上async或者defer字段, 或者将该标签放在body最后面, 避免阻塞主解析线程.
在js代码中, 将一些耗时操作异步执行.
- 异步导入
可以使用异步组件等只进行必要的导入. 在开发者工具中也可以查看覆盖率来找出没有使用的资源进行异步导入或者删除.
再次访问
- http缓存
在浏览器第一次请求某份静态资源时, 服务端可以在响应头中加上一些字段, Etag
, last-modified
, Cache-Control
, Expires
等字段, 来说明这份资源的标识符和上一次修改的时间. 浏览器收到这份响应后会将相关字段保存起来.
再次请求某份静态资源时, 浏览器会检查Cache-Control
的内容, 里面一般会有max-age, 检查这个max-age值判断缓存是否过期, 如果没过期那么就使用缓存. 这就是 强缓存.
当浏览器判断过期或者失效时, 就会再次发起一个http请求, 里面会带上之前保存起来的Etag和last-modified, 只是名字不太一样, 叫If-None-Match
和If-Modified-Since
. 服务端检查这些字段并判断资源是否过期, 如果没有过期, 那么返回一个304, 表示缓存依旧有效, 并更新相关字段; 如果过期了, 那么响应200并返回相关内容. 这就是 协商缓存.
- service worker缓存
相比http缓存, sw缓存需要开发者自己控制, 粒度更细, 更加自由.
http从0.9到3
http0.9
HTTP/0.9应该不会问, 毕竟没什么东西. 这个时候的http请求没有版本号, 请求只有一条指令.
GET /index.html
不支持虚拟主机.
http1.0
增加了版本号, 还引入了标头(Header), 数据类型(Content-Type)也不局限于html, 响应有了状态码.
这个版本使用的还是非持续连接, 每一次请求都需要建立一个TCP连接.
http1.1
引入了Host标头, 支持虚拟主机, 允许一个ip部署多个网站了.
这个版本使用了持续连接, 多次请求可以共用一次连接.
这个版本存在队头阻塞的问题, 后面的请求必须等前面的请求完成才能进行, 虽然有管道化技术但是不推荐.
http2
这个版本不再串行处理请求了, 而是进行并行处理.
http2将一个请求转为一个数据帧, 帧里面有首部帧的数据帧, 还带有一个标识符. 这个标识符帮助浏览器判断是哪一个请求的响应.
http2使用了头部压缩, 减少重复的头部.
然而由于TCP的限制, 存在TCP队头阻塞. 其中一个数据帧丢失了, 会导致其它数据帧必须等待它到达, 才能交付给应用层处理.
http3
http3使用QUIC协议, 它糅合了很多东西, 既有TCP的流量控制, 阻塞控制, 也有TLS的认证, 并且建立在UDP上, 没有TCP队头阻塞问题.
QUIC允许用户切换网络也能继续接收请求而不需要重传. 在理想的情况下重连甚至能减少TLS握手次数.
HTTPS的TLS握手
是否绝对安全
并非, 如果有人在电脑上安装了恶意的根证书仍会遇到http的问题.
是否绝对隐私
并非, 在第一次TLS握手时需要发送请求的网址, 这个网址是明文发送的.
TLS握手流程
浏览器发送第一个握手包Client Hello
, 这个握手包有一个浏览器生成的随机数.
服务端接收握手包, 发送第二个握手包Server Hello
, 这个握手包有一个服务端生成的随机数. 服务端还会把自己的证书发给浏览器.
证书上面由两部分组成, 一部分是网站信息, 里面有公钥和使用的算法等; 另一部分是公证人对网站信息哈希后使用私钥加密的数字签名. 浏览器接收到证书后, 会对网站信息进行哈希计算, 得到一个哈希值; 根据证书链找到根证书的公钥, 使用这个公钥对数字签名进行加密, 也得到一个哈希值. 比对这两个哈希值, 如果相同说明对方是真的服务端; 不同就说明不是.
浏览器在验证成功后, 生成一个随机数并使用服务端的公钥进行加密, 然后发给服务端.
此时双方都有三个相同的随机数, 计算出一个相同的密钥. 之后使用这份密钥进行加密通话.
牢骚
唉唉, 写完了又开始紧张了. 有时候会不禁问自己是否真的做好了准备, 是否真的尽力了, 也确实感觉自己有点摆了. 事已至此, 先睡觉吧.