1. 渲染优化
与浏览器为友,共进退
浏览器渲染原理和关键渲染路径
- JavaScript
用于触发视觉变化,包括脚本的触发,CSS 的变化,animation 的变化
- Style
浏览器对样式进行重新计算
- Layout
布局(回流):将元素按照样式布局到页面相应的位置
- Paint
绘制:开始绘制
- Composite
合成,将不同的层绘制在对应的位置再进行合成
- 构建 DOM 对象
HTML → DOM
- 构建 CSSOM 对象
CSS → CSSOM
回流(reflow)
页面第一次加载 Layout 这一步是叫做布局,后面的操作导致的重新布局叫做回流
影响回流的操作
- 添加/删除元素
- display: none
- 移动元素位置
- 操作 styles
- offsetLeft,scrollTop,clientWidth
- 修改浏览器大小,字体大小
避免 Layout thrashing
- 避免回流
- 比如改变元素的位置,不要直接修改元素的位置,可以使用transform,translate来改变位移
- 读写分离
- 读写的批量操作(虚拟DOM)
- 使用 FastDom
重绘(repaint)
减少重绘的方式
- 利用 will-change 创建新的图层
- 使用 transform 和 opacity
复合线程(compositor thread)与图层(layers)
复合线程做了什么
- 将页面拆分图层进行绘制再进行复合
- 浏览器按照某种规则进行拆分
- 可手动拆分
- 利用 DevTools 了解网页的图层拆分情况
- 哪些样式仅影响复合(不会影响 Layout 和 Paint )
- position
- Scale
- Rotation
- Opacity
transform: translate
transfrom: scale
transform: rotate
opacity: 0...1
高频事件防抖
React 时间调度实现
具体可看 fiber 的实现原理
2. 代码优化
如何写出真正高性能的代码
JS开销和如何缩短解析时间
开销在哪里
- 加载
- 解析和编译
- 执行
解决方案
- Code splitting 代码拆分,按需加载
- Tree shaking 代码减重
- 避免长任务
- 避免超过 1kb 的行间脚本
- 使用 rAF 和 rIC 进行时间调度
- 可见不可交互 vs 最小可交互资源集
配合V8 有效优化代码
源码 ⇒ 抽象语法树 ⇒ 字节码Bytecode ⇒ 机器码
- 编译过程会进行优化
- 运行时可能反生反优化
- 脚本流
- 字节码缓存
- 懒解析
函数优化
函数的解析方式
- lazy parsing 懒解析 vs eager parsing 饥饿解析
- Optimize.js 优化初次加载时间(Webpack 已经配备)
对象优化
对象优化的方式
- 以相同顺序初始化对象成员,避免隐藏类的调整
- 避免元素类型的转换
- 实例化后避免添加新属性
- 避免读取超过数组的长度
function foo(array) { for(let i = 0; i <= array.length; i++) { // 越界比较 if(array[i] > 1000) { // 1. 造成 undefined 跟数字进行比较 2. 沿原型链查找 console.log(array[i]) // 业务上无效、出错 } } } const array = [10, 100, 1000]
- 尽量使用 Array 代替 array-like 对象
将类数组转换为 Array 后再使用 数组的方法 效率会更高
// arrobj 为类数组 Array.prototype.forEach.call(arrobj, (value, index) => { console.log(`${index}: ${value}`) }) const arr = Array.prototype.slice.call(arrobj, 0); arr.forEach(() => {}) // 下面先转换为数组的运行速率会更快 // 转换的代码比优化影响小
HTML优化
- 减少iFrames使用
- 压缩空白符
- 避免节点深层级嵌套
- 避免 table 布局
- 删除注释
- CSS & JavaScript 尽量外链
- 删除元素默认属性
CSS优化
- 降低 CSS 对渲染的阻塞
- 利用 GPU 进行完成动画
- 使用 contain 属性
- 使用 font-display 属性
3. 资源优化
经典性能优化解决方案
资源的压缩与合并
- HTML压缩
- 使用在线工具进行压缩
- 使用 html-minifier 等 npm 工具
- CSS 压缩
- 使用在线工具进行压缩
- 使用 clean-css 等 npm 工具
- JS压缩与混淆
- 使用在线工具进行压缩
- 使用 webpack 对 JS 在构建时压缩
- CSS JS 文件合并
- 文件小的可以进行合并
- 无冲突时,服务相同的模块可以进行合并
- 只是优化加载不建议
图片格式优化
- JPEG/JPG 的优点
- PNG 的优点
- WEBP的优点
图片加载优化
- 懒加载
- 第三方图片懒加载方案
- 原生的图片的懒加载方案
<img loading="lazy" />
- 使用响应式图片
- Srcset 属性的使用
- Sizes 属性的使用
- picture 的使用
字体优化
4. 构建优化
webpack 构建优化
webpack 的优化配置
Tree Shaking
- 上下文未用到的代码(dead code)
- 基于 ES6 import export
- 局限性
// package.json "sideEffects": [ ... ]
JS压缩
- Webpack 4 后引入的 uglifyjs-webpack-plugin
- 支持 ES6 替换为 terser-webpack-plugin
- 减少 JS 文件体积
作用域提升
- 代码体积减少(依赖关系进行合并)
- 提高执行效率
- 注意 babel 中 modules 的配置
babel7 优化配置
- 在需要的地方引入 polyfill
- 辅助函数的按需引入
- 根据目标浏览器按需转换代码
webpack 的依赖优化
noparse
- 提高构建速度
- 直接通知 webpack 忽略较大的库
- 被忽略的库不能有import,require,define的引入方式
DllPlugin
- 避免打包时对不变的库重复构建(如 react,react-dom)
基于 webpack 的代码拆分
代码拆分做什么
- 把单个文件拆分成若干个bundles / chunks
- 缩短首屏加载时间
拆分的方式
- 手动定义入口
- splitChunks 提取共有代码,拆分业务代码与第三方库
基于 webpack 的资源压缩
minification
- Terser 压缩 JS
- mini-css-extra-plugins 压缩CSS
- HtmlWebpackPlugin - minify 压缩HTML
基于 webpack 的资源持久化缓存
- 每个打包的资源文件有唯一的 hash 值
- 修改后只有受影响的文件的 hash 值发生变化
- 充分利用浏览器缓存
基于 webpack 的应用大小监测与分析
- 静态代码分析与可视化图
- webpack-bundle-analyzer 进行体积分析
- speed-measure-webpack-plugin 速度分析
React按需加载的实现方式
- React router 基于 webpack 动态引入
- 使用 Reloadable 高级组件
5. 传输加载优化
启用压缩GZip
- 对传输资源进行压缩,可高达90%
- 使用 Nginx 配置 GZip