前端性能优化
pr1mavera 2019/9/17
# 必备知识
# 雅虎军规
# 缓存策略
- localStorage 同步
- sessionStorage 同步
- indexed DB 异步
- web SQL 异步
- Cookies 同步
# 对于业务逻辑:
将 业务逻辑代码 缓存进 localStorage :
- 存:
{
'script/a.js': 'script/a.55ggkk.js',
'script/a.55ggkk.js': '......'
}
- 首页 index.html -> no-cache
- 取:激活js文件
- 缓存中有
- 从缓存中取
- 'script/a.js' 取 'script/a.55ggkk.js'
- 未过期
- 'script/a.55ggkk.js' 取 code
- add script 注入 header
<script id="xxx">code...</script>
- 缓存中没有 / 过期
- script/a.js 请求回来,add script 注入 header
<script id="xxx"></script>
- script/a.js 将请求头设置为 text ,再请求一次,拿到 code
- 'script/a.js' 存 'script/a.55ggkk.js'
- 'script/a.55ggkk.js' 存 code
- script/a.js 请求回来,add script 注入 header
- 缓存中有
basket.js 统一了浏览器缓存的 API ,统一暴露 get\set .... ,使用起来很方便,但是也推荐自己实现
不懂就问: 当前我们已经有了HTTP缓存(HTTP状态码 304)了,为什么还要用浏览器离线缓存?
答:
- HTTP缓存并不是完全的离线,他还是需要跟网络通讯的,还是会消耗客户端以及服务器资源
- 移动端浏览器的差异性,或者设备装的一些软件(某60手机管家。。)导致一些缓存的失效,我们为了性能做的缓存也就没有用了
# 对于SDK(库文件):
一般使用HTTP缓存
- last-modified / if-modified-since
- 最后一次修改时间
- etag / if-none-match
- 验证毫秒级的文件变化
- expires
- 纯粹校验过期时间(具体年月日)
- 存在缺陷:服务器时间与客户端时间存在不一致,因此 HTTP/1.1 加入了 cache-control
- cache-control
- 设置过期的时间长度(秒)
优先级: cache-control > expires > etag > last-modified
# 网站协议
HTTP2 解决 HTTP1 的对手阻塞问题
HTTP3 解决 HTTP2 的 tcp 层面的对手阻塞
HTTP1 开启 keep-alive 之后是可以减少 tcp 握手次数,但是还是存在效率问题
- 串行文件传输
- 连接数过多
# 渲染中的性能优化
# 渲染过程
首先要清楚几个概念:
渲染
: 模型 -> 位图 ,即根据 样式信息 和 大小信息 ,为每个元素在内存中渲染它的图形,并且把它绘制到对应的位置位图
: 在内存里建立一张二维表格,把一张图片的 每个像素对应的颜色 保存进去- 位图信息保存在 DOM 树中,并且是 DOM 树种占据浏览器内存最多的内容,优化内存占用的时候优先考虑
- 一个元素可能对应多个盒(比如 inline 元素,可能会分成多行)
- 每一个盒对应着一张位图
浏览器渲染的步骤:
JavaScript
: js 操作 dom 元素Style
: 计算样式,对每个图层节点进行样式计算,浏览器中表现为 Recalculate StyleLayout
: 布局,具体计算每个 DOM 元素最终在屏幕上显示的大小
和位置
Paint
: 绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个 DOM 元素所有的可视效果,填充到图层中去Composite
: 合成,渲染层按一定规则合并,最终将图层作为纹理上传至 GPU
关于浏览器渲染的详细过程,有两个博文写的不错:
- 重排 一定会引起 重绘 ,但 重绘 不一定会引起 重排
- 重排 是将结构打乱重新排列,重绘 是对元素进行重新绘制,它俩没有可比性
引起重排的情况:
- 添加或删除元素
- 元素位置改变
- 盒子模型变化
- 页面初始化
- 脚本读取某个属性 offset / scroll / client / width / height ...
在读写dom操作频率比较高的场景尽量使用 requestAnimationFrame API
# 浏览器分层
css会将页面内容分层,然而存在一些默认会分层的元素:
- 根元素
- position !== static
- transform
- 半透明
- canvas
- video
- overflow
# 硬件加速
以下属性会开启GPU硬件加速
- css 3D
- video
- canvas
- webGL
- 滤镜
# CPU 与 GPU 渲染的区别
- 相同 : 两者都有总线的概念和外界联系,有自己的缓存体系,以及数字和逻辑的运算单元,都是为计算而生的
- 不同 : CPU 主要负责 操作系统 相关的计算, GPU 主要负责和 显示 相关的计算
# 页面加载性能优化
# 名词
TTFB
- Time To First Byte 首字节时间
FP
- First Paint 首次渲染
FCP
- First Contentful Paint 首次有内容的渲染
FMP
- First Meaningful Paint 首次有意义的渲染
- 用户意识上认为可用
- 无 API 可获取事件
- 可以手动的打点记录:
performance.mark('start'); performance.getEntriesByType('start');
TTI
- Time To Interactive 可交互时间
- 应用完全可用
- 可用使用 tti-polyfill 去检测
Long tasks
- 超过了50ms的任务
SSR
&CSR
- 服务端渲染 & 客服端渲染
Isomorphic Javascript
- 同构化
# 浏览器性能检测对象
利用浏览器性能检测对象检测应用性能:
- 获取应用的
FP
和FCP
:(页面需要有实际内容渲染)
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log(entry.name);
console.log(entry.startTime);
console.log(entry.duration);
}
});
observer.observe({entryTypes: ["paint"]});
// first-paint
// 141.321235
// 0
// first-contentful-paint
// 141.321235
// 0
- 检测页面的
Long tasks
(大的、耗时较长的循环)
const observer = new PerformanceObserver(list => {
for (const entry of list.getEntries()) {
console.log(entry.name);
console.log(entry.startTime);
console.log(entry.duration);
}
});
observer.observe({entryTypes: ["longtask"]});
for(var i = 0; i <= 100000000; i++) {}
// self
// 72.321235
// 2925.32735
网页加载的时间轴
# 白屏问题
原因:
- css & js 文件获取
- js 文件解析
- dom 生成
- cssom 生成
- 首帧 HTML 包含内容: 基本的 dom & css