前端性能优化导向地图
前端性能优化
文章是自己导出的,没有做优先级,请配合 mindmap 食用
浏览器缓存
Memory Cache
内存中的缓存,优先级上,是浏览器最先开始尝试命中的一种缓存
与渲染进程并存,当进程结束,内存缓存也会消失
内存划分规则没有定论,且存放位置有随机性,浏览器秉承内存节约原则,大文件一般都直接进磁盘
- Base64 格式图片几乎永远可以进 Memory Cache
- 体积不大的 js、css 文件有较大几率被写入,大一点就直接进磁盘
Service Worker Cache
- 脱离于浏览器,无法直接访问 DOM
- 使用场景和具体案例有哪些
HTTP Cache
强缓存
优先级高,在命中失败的情况下,才走协商缓存
特征
expires
- 有可能服务器和客户端有时间差,导致意料之外的结果
cache-control
max-age 与 expires 我们普遍优先 max-age
s-maxage 优先与 max-age
- s-maxage 仅在代理服务器中生效,客户端中我们只考虑 max-age
no-store 与 no-cache
no-cache:绕开了浏览器,每次请求都不会询问浏览器缓存,直接向服务端去确认资源是否过期,即走协商缓存的路线
- 浏览器缓存、向服务端确认是否过期、服务端发送请求的区别?
no-store:不实用任何缓存策略,指允许向服务端发送请求
什么资源适合走强缓存
协商缓存
Last-Modified
定义
- 依赖与服务端与浏览器之间你的通信,浏览器向服务器询问缓存的相关信息,判断是否重新发起请求、下载完整响应,还是从本地获取缓存资源.如果服务端提示资源没有改动,则会被重定向到浏览器缓存,此时网络请求是 304
过程
- 每次请求会带上 If-Modified-Since 时间戳子都拿,他的值是上一次 response 返回给他的 last-modified,服务器接收到这个时间戳后,与资源在服务器上的最后修改时间进行比较,如果发生变化则发生完整响应,否则返回 304 响应
弊端
如果服务器没有正确感知文件的变化,则可能会出错
例子 🌰
- 我们编辑了文件,但文件的内容没有改变。服务端并不清楚我们是否真正改变了文件,它仍然通过最后编辑时间进行判断。因此这个资源在再次被请求时,会被当做新资源,进而引发一次完整的响应——不该重新请求的时候,也会重新请求。
- 当我们修改文件的速度过快时(比如花了 100ms 完成了改动),由于 If-Modified-Since 只能检查到以秒为最小计量单位的时间差,所以它是感知不到这个改动的——该重新请求的时候,反而没有重新请求了。
Etag
Etag 是由服务器为每个资源生成的唯一的标识字符串
与 Last-Modified 类似,优先级高于 Last-Modified
- ETag: W/“2a3b-1602480f459”
- If-None-Match: W/“2a3b-1602480f459”
Etag 的生成需要服务器额外开销,影戏服务端性能,是 Last-Modified 的补充和强化
Floating Topic
流程图
Push Cache (拓展)
- HTTP2 新特性,比较新颖,处于萌芽阶段
- Push Cache 是缓存的最后一道防线。浏览器只有在 Memory Cache、HTTP Cache 和 Service Worker Cache 均未命中的情况下才会去询问 Push Cache。
- Push Cache 是一种存在于会话阶段的缓存,当 session 终止时,缓存也随之释放
- 不同的页面只要共享了同一个 HTTP2 连接,那么它们就可以共享同一个 Push Cache
- 深入可看:https://jakearchibald.com/2017/h2-push-tougher-than-i-thought/
本地缓存
几种缓存类型
Cookie
- 特点:文本文件,运送客户端状态 - 4kb上限,存在不必要对Cookie传输开销(例如图片)
解决办法: 使用不同域名 CDN 加载资源
Web Storage
LocalStorage
- 存储内容稳定的资源,例如不变的图片 base64,不经常更新的 css、js 等
SessionStorage
- 存储会话级别的信息
indexedDB
使用场景
- 账号密码填写记录
- 长久不变的数据
- 一些日志,等空闲时间打包回传
- 键值对较多的情况,不必字符串化
- Service Workers 中开发应用,只能选 indexedDB
第三方:Dexie.js
替代方案
- 如果觉得 indexedDB 学习曲线比较陡峭,可以选择替代方案,localforage.localforage 的逻辑是这样的:优先使用 IndexedDB 存储数据,如果浏览器不支持,使用 WebSQL,浏览器再不支持,使用 localStorage,语法与 localStorage 相同,唯一的不同是 localforage 是异步的
- https://github.com/localForage/localForage
拓展
缓存和离线开发
DOM 优化
回流 Reflow/重排
修改几何属性
改变 DOM 树结构,即节点的增删移动
即时计算的属性(offsetTop、getComputedStyle 等)
思路
- 通过变量存储属性值,避免频繁改动
- 避免逐条改变样式,使用类名去合并样式
- DOM 离线,适合操作频繁的情况
重绘 Repaint
重排一定会重绘,重绘不一定重排
思路
减少 DOM 操作
- 最佳实践:DOM Fragment
DOM 更新包装成 micro 任务,可以减少一次无用 render
Webpack 性能调优
两个瓶颈
- Webpack 构建过程时间久
- Webpack 打包结果体积大
优化方案
不要让 loader 做太多事
- 善用include/exclude - 开启缓存,将转译结果缓存至文件系统,
提升 babel-loader 工作效率 2 倍
loader: ‘babel-loader?cacheDirectory=true’不要放过第三方库
- Externals、CommonsChunkPlugin 不是最好的选择,DllPlugin 会比较不错
将 loader 由单进程转为多进程
- HappyPack
- parallel
构建结果体积压缩
- webpack-bundle-analyzer
删除冗余代码
Tree-Shaking
- 更适合用来处理模块级别的冗余代码。至于粒度更细的冗余代码的去除,往往会被整合进 JS 或 CSS 的压缩或分离过程中
UglifyJsPlugin
- webpack4 已经默认使用, 通过配置 optimization.minimize 与 optimization.minimizer 来自定义压缩相关的操作
按需加载
- 核心是:require.ensure(dependencies, callback, chunkName)
- 这是一个异步方法,在跳转到目标路由到时候,异步方法的回调才会生效,真正获取目标路由下的 Content
杂项
多图页面,懒加载
防抖、节流
visibilityChange
服务端渲染
- 先理解客户端渲染,页面通过 js 运行出来,挂载到 root 下进行渲染的方式
- 服务端渲染:所见即所得,页面呈现的内容在 html 源文件中可以找到
- 优点: 对 seo 有正向效应,解决了首屏加载过慢的问题
- 客户端通常数量远超于服务端数量,除非是对性能要求很高,否则一般不会太刻意要求服务端渲染来提升性能
CSS、JS
CSS 书写经验总结
- #myList li{} ,浏览器从右开始匹配,会增大开销
- 减少嵌套,减少后代选择器
加载顺序,JS 优先 CSS,两者一般都会阻塞 DOM 渲染
性能监测与评估
Performance
LightHouse
性能上报方案
- Performance API,拿到性能数据进行二次处理
图片优化
图片类型
JPG/JPEG
- 关键字: 有损压缩、体积小、加载快、不支持透明
- 适用于呈现色彩丰富的图片,日常开发中,经常作为大的背景图、轮播图或 banner 图出现(例如旧版京东、淘宝)
PNG
- 关键字: 无损压缩、质量高、体积大、支持透明
- 比 JPG 更强的色彩表现力,优化了 JPG 的局限,但是体积大
- 适用于小的 Logo、颜色简单且对比强烈的图片或背景等
SVG
- 关键字: 文本文件、体积小、不失真、兼容性好
- 相较 PNG、JPG,体积更小,可压缩性更强,图片无限放大不失真
- 缺点是渲染成本高,其次是如果要编程的话,需要学习成本
Base64
- 关键字: 文本文件、依赖编码、小图标解决方案 - 图片base64编码后,通常会膨胀为原文件的4/3 - 适用于: 图片实际尺寸很小,4kb以下
无法以雪碧图的形式于其他小图结合
图片更新频率非常低的情况Webp
- 关键字: 年轻的全能型选手、
支持透明、支持动图 - 与 PNG 相比,无损图像缩小了 26%
有损图像比同类 JPEG 小 25-34% - 缺陷:兼容性,不过目前态势比较好
开发时需要准备降级方案
Tips
- 如果借用第三方转换图片格式,需要考虑数据持久化,例如七牛云中自动转格式,时间就会自动清除缓存,造成二次资源获取,反而翻车
- 如果是较小图片,纯色的话,可以建议换成 iconfont 会更为合适
HTTP 压缩
Gzip
- 压缩和解压的时间,相对传输过程中节省的时间开销,微不足道
- 高效,压缩后通常能帮我们减少响应 70% 左右的大小,但并不保证针对每一个文件的压缩都会使其变小
- 服务器端的 Gzip 与 webpack 的 Gzip 并不能互相替代,许哟啊结合业务实际强度以及资源权衡
CDN 缓存
目的: 提升请求响应能力
两个核心
- 缓存
- 回源
感谢阅读,勘误、纠错或其他请联系progerchai@gmail.com,或者点击这里提 issue 给我
欢迎交流 👏,你的每一次指导都可以让我进步
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!