前端性能优化,无非两点:
- 提升资源请求速度
- 提升界面渲染速度
所以我们在设计系统或编写代码的时候可以采取一些策略,以便于优化用户体验。例如,资源压缩合并树摇、懒加载、预加载、加载动画和骨架屏、图片以base64或icons展示、接口优化、gzip等。
具体有以下几个优化方向:
- 减小资源大小
- 减少资源请求数量
- 避免回流与重绘
- 优化高频事件
- 缓存公用数据
- 按需加载
- 预加载
webpack配置
构建优化
缓存
webpack5
cache: {
type: 'filesystem',
buildDependencies: {
config: [ __filename ],
},
},
DllPlugin
多线程
- happypack
缩小查找范围
- exclude/include
输出优化
- splitChunks - 代码合并压缩、抽取共用部分
- tree shaking - 删除不需要的额外代码
- 按需加载
从输入URL到页面展示,我们需要了解的优化点。
从输入URL到页面展示过程
- url解析
- dns解析
- tcp建立连接
- 发起http请求
- 服务器响应
- 浏览器解析并渲染
- 页面加载完成
浏览器缓存
协商缓存
- Last-Modified/If-Modified-Since:第一次请求,respone的header加上Last-Modified(最后修改时间),再次请求,在request的header上加上If-Modified-Since
- Etag/If-None-Match:这两个值是由服务器生成的每个资源的唯一标识字符串,只要资源有变化就这个值就会改变;其判断过程与Last-Modified/If-Modified-Since类似,他可以精确到秒的更高级别。
协商缓存意思是文件已经被缓存了,但是否从缓存中读取是需要和服务器进行协商,具体如何协商要看请求头/响应头的字段设置。需要注意的是协商缓存还是发了请求的。
强制缓存
- Expires: 绝对时间,判断客户端日期是否超过这个时间
- Cache-Control:相对时间,判断访问间隔是否大于3600秒
强制缓存就是文件直接从缓存中获取,不需要发送请求。
- Cache-Control 响应头表示了资源是否可以被缓存,以及缓存的有效期。
- Etag 响应头标识了资源的版本,此后浏览器可据此进行缓存以及询问服务器。
- Last-Modified 响应头标识了资源的修改时间,此后浏览器可据此进行缓存以及询问服务器。
什么时候存dist,什么时候存memoey?
- 大一点的文件会缓存在dist里面,因为内存也是有限的,磁盘的空间更大
- 小一点文件js,图片存的是memory
- css文件一般存在dist
- 特殊情况memory大小是有限制的,浏览器也会根据自己的内置算法,把一部分js文件存到dist里面
使用http2
- 多路复用
- 首部压缩
页面渲染
- 解析HTML生成DOM树。
- 解析CSS生成CSSOM规则树。
- 将DOM树与CSSOM规则树合并在一起生成渲染树。
- 遍历渲染树开始布局,计算每个节点的位置大小信息。
- 将渲染树每个节点绘制到屏幕。
重排重绘
回流/重排
回流主要指几何属性需要需要改变的渲染。渲染树节点的几何属性发生改变,导致其位置发生改变 ,从而重新计算生成新的渲染树。这就是回流为什么消耗性能的主要原因,因为他重新计算了生成了DOM。
重绘
重绘指外观属性的更改。他影响了节点外观属性的变化,但并不会影响其几何属性。但是几何属性的改变一定会影响外观属性的变化。
什么操作会导致重排?
- 添加或删除可见的 DOM 元素
- 元素位置改变
- 元素尺寸改变
- 内容改变
- 浏览器窗口尺寸改变
如何减少重排重绘?
- 用 JavaScript 修改样式时,最好不要直接写样式,而是替换 class 来改变样式。
- 如果要对 DOM 元素执行一系列操作,可以将 DOM 元素脱离文档流,修改完成后,再将它带回文档。推荐使用隐藏元素(display:none)或文档碎片(DocumentFragement),都能很好的实现这个方案。
- 使用transform translate设置位置。
内存管理
进程与线程
-
进程: 程序的一次执行,他占有一片独有的内存空间,是操作系统的基本单位。
- 一个进程中至少有一个运行的线程:主线程。进程启动后自动创建。
- 一个进程中也可以同时运行多个线程,程序是多线程运行的。
- 一个进程内的数据可以提供其中的多个线程共享,多个进程之间的数据是不能直接共享的。
-
线程: 是建成内一个独立执行的单位,是CPU调度的最小单元,程序运行的基本单位。
-
线程池:保存多个线程对象的容器,实现线程对象的反复利用。
js为什么是单线程
JavaScript采用单线程,是为了避免在执行过程中页面内容被不可预知的重复修改。
在JavaScript运行一段代码块的时候,页面中其他的事情(UI更新或者别的脚本加载执行等)在同一时间段内是被挂起的状态,不能被同时处理的,所以在执行一段js脚本的时候,这段代码会影响其他的操作。
垃圾回收
JavaScript中会被判定为垃圾:
- 对象不再被引用是垃圾
- 对象不能从根上访问到是垃圾
内存优化
- 慎用全局变量
- 避开闭包陷阱
- 选择最优的循环方法
- 垃圾回收
- 减少嵌套
- 尾递归优化
性能检测
LightHouse
其它
节流和防抖
防抖:在设定时间内只执行最后一次事件,前面的事件被clear掉
const debounce=(func,wait=60)=>{
let timer=null;
return function (...args){
clearTimeout(timer);
timer=setTimeout(()=>func.apply(this,args),wait);
};
};
节流:在设定时间内只执行第一次事件,执行后将当前时间设为开始时间
const throttle=(func,delay=60)=>{
let start=0;
return function (...args){
const current=+new Date();
if(current-start>delay){
func.apply(this,args);
start=current;
}
};
};
加载动画和骨架屏
避免页面卡顿或闪烁。
图片优化
雪碧图、base64、icons...
尾递归
若函数在尾位置调用自身(或是一个尾调用本身的其他函数等等),则称这种情况为尾递归。
尾调用由于是函数的最后一步操作,所以不需要保留外层函数的调用记录,因为调用位置、内部变量等信息都不会再用到了,只要直接用内层函数的调用记录,取代外层函数的调用记录就可以了。优化调用栈,节约内存。
memoize
const memoize=(fn,len=100)=>{
let cache=[];
return (...args)=>{
const key=JSON.stringify(args);
const cached=cache.find(v=>v.key===key);
if(!cached){
const result=fn(...args);
cache.push({key,result});
if(cache.length>len){
cache.shift();
}
return result;
}
return cached.result;
};
};
虚拟列表
只渲染可视窗口内容。
react优化
- 缩小依赖范围
- 避免派生 state
- 使用immutable数据
- 使用useMemo
关注我
微信搜索“前端道萌”