性能优化
大约 4 分钟约 1123 字...
前端性能优化有哪些方式
综合性问题,待深挖
原则
多使用内存、缓存
减少 CPU 计算量,减少网络加载耗时 空间换时间
加载更快
- 减少资源体积:压缩代码
- 减少访问次数:合并代码、SSR 服务端渲染、缓存
- 使用更快的网络:CDN
渲染更快
为什么 css 放在 head,js 放在 body 最下面
希望在 DOM 树生成前就将 css 加载完,当 DOM 树加载完之后结合 CSSOM 树一次生成渲染树,一步完成渲染
js 可能会修改 DOM 结构
- css 放在 head,js 放在 body 最下面
- 尽早开始执行 js,用 DOMContentLoaded 触发
- 懒加载(图片懒加载,上滑加载更多,虚拟列表)
- 对 DOM 查询进行缓存 ?
- 频繁 DOM 操作,合并到一起插入 DOM 结构
- 节流防抖
讲一下浏览器渲染优化
针对 javaSCript
js 的加载会阻塞页面的渲染,
- 尽量将 javaSript 放在 body 的最后
- body 中间尽量不要写 script 标签
- 使用 async、defer 异步加载执行脚本
针对 css
使用 css 的三种方式:link、@import、内联样式、其中 link 和@import 都是导入外部样式,它们之间的区别:
- link:浏览器会派发一个新的线程(HTTP 线程)去加载资源文件,与此同时 GUI 渲染线程会继续向下渲染代码
- @import:GUI 渲染线程会暂时停止渲染,去服务器加载资源文件,资源文件没有返回之前不会继续渲染
- style:GUI 直接渲染
外部样式如果长时间没有加载完毕,浏览器为了用户体验,会使用浏览器的默认样式,确保首次渲染速度,所以 css 一般写在 header 中,让浏览器尽快发送请求去获取 css 样式
所以在开发过程中,导入外部样式使用 link,而不用@import,如果 css 少,尽可能采用内嵌样式,直接写在 style 标签中
针对 DOM 树、CSSOM 树
可以通过一下方式来减少渲染的时间:
- html 文件的代码层级尽量不要太深
- 使用语义化的标签,来避免不标准语义化的特殊处理
- 减少 css 的代码层级,因为选择器是从左到右进行解析的
减少回流和重绘
- 操作 DOM 时,尽量在低层级的 DOM 节点进行操作
- 不要使用 table 布局, 一个小的改动可能会使整个 table 进行重新布局
- 不要频繁操作元素的样式,对于静态页面,可以修改类名,而不是样式
- 使用 absolute 或者 fixed,使元素脱离文档流,这样他们发生变化就不会影响其他元素
- 避免频繁操作 DOM,可以创建一个文档片段 documentFragment,在它上面应用所有 DOM 操作,最后再把它添加到文档中
- 将元素先设置 display: none,操作结束后再把它显示出来。因为在 display 属性为 none 的元素上进行的 DOM 操作不会引发回流和重绘
- 将 DOM 的多个读操作(或者写操作)放在一起,而不是读写操作穿插着写。这得益于浏览器的渲染队列机制
手写防抖
防抖就类似回城,打断就得重新回
在事件被触发 n 秒后再执行回调,如果在这 n 秒内又被触发,则重新计时
场景:
- search 搜索联想,用户在不断输入值时,用防抖来节约请求资源
- window 触发 resize 的时候,不断的调整浏览器窗口大小会不断的触发这个事件,用防抖来让其只触发一次
function debouce(fn, delay = 500) {
let timer = null
return function () {
if (timer) clearTimeout(timer)
timer = setTimeout(() => {
fn.apply(this, arguments)
}, delay)
}
}
手写节流
节流就类似技能需要冷却时间到了才能用
规定在一个单位时间内,只能触发一次函数。如果这个单位时间内触发多次函数,只有一次生效
场景:
- 鼠标不断点击触发,mousedown(单位时间内只触发一次)
- 监听滚动事件,比如是否滑到底部自动加载更多,用 throttle 来判断
function throottle(fn, delay = 500) {
let timer = null
return function () {
if (timer) return
timer = setTimeout(() => {
fn.apply(this, arguments)
timer = null
}, delay)
}
}