前端性能优化


2019/9/17

# 必备知识

# 雅虎军规

前端性能优化之雅虎35条军规

# 缓存策略

  • localStorage 同步
  • sessionStorage 同步
  • indexed DB 异步
  • web SQL 异步
  • Cookies 同步

# 对于业务逻辑:

业务逻辑代码 缓存进 localStorage :

  • 存:
{
    'script/a.js': 'script/a.55ggkk.js',
    'script/a.55ggkk.js': '......'
}
  • 首页 index.html -> no-cache
  • 取:激活js文件
    • 缓存中有
      1. 从缓存中取
      2. 'script/a.js' 取 'script/a.55ggkk.js'
      • 未过期
        • 'script/a.55ggkk.js' 取 code
        • add script 注入 header <script id="xxx">code...</script>
    • 缓存中没有 / 过期
      1. script/a.js 请求回来,add script 注入 header <script id="xxx"></script>
      2. script/a.js 将请求头设置为 text ,再请求一次,拿到 code
      3. 'script/a.js' 存 'script/a.55ggkk.js'
      4. 'script/a.55ggkk.js' 存 code

basket.js 统一了浏览器缓存的 API ,统一暴露 get\set .... ,使用起来很方便,但是也推荐自己实现

不懂就问: 当前我们已经有了HTTP缓存(HTTP状态码 304)了,为什么还要用浏览器离线缓存?

答:

  1. HTTP缓存并不是完全的离线,他还是需要跟网络通讯的,还是会消耗客户端以及服务器资源
  2. 移动端浏览器的差异性,或者设备装的一些软件(某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 握手次数,但是还是存在效率问题

  1. 串行文件传输
  2. 连接数过多

# 渲染中的性能优化

# 渲染过程

首先要清楚几个概念:

  • 渲染 : 模型 -> 位图 ,即根据 样式信息大小信息 ,为每个元素在内存中渲染它的图形,并且把它绘制到对应的位置
  • 位图 : 在内存里建立一张二维表格,把一张图片的 每个像素对应的颜色 保存进去
  • 位图信息保存在 DOM 树中,并且是 DOM 树种占据浏览器内存最多的内容,优化内存占用的时候优先考虑
  • 一个元素可能对应多个盒(比如 inline 元素,可能会分成多行)
  • 每一个盒对应着一张位图

浏览器渲染的步骤:

  1. JavaScript : js 操作 dom 元素
  2. Style : 计算样式,对每个图层节点进行样式计算,浏览器中表现为 Recalculate Style
  3. Layout : 布局,具体计算每个 DOM 元素最终在屏幕上显示的 大小位置
  4. Paint : 绘制,本质上就是填充像素的过程。包括绘制文字、颜色、图像、边框和阴影等,也就是一个 DOM 元素所有的可视效果,填充到图层中去
  5. 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 主要负责和 显示 相关的计算

# 页面加载性能优化

# 名词

  1. TTFB
    • Time To First Byte 首字节时间
  2. FP
    • First Paint 首次渲染
  3. FCP
    • First Contentful Paint 首次有内容的渲染
  4. FMP
    • First Meaningful Paint 首次有意义的渲染
    • 用户意识上认为可用
    • 无 API 可获取事件
    • 可以手动的打点记录:performance.mark('start'); performance.getEntriesByType('start');
  5. TTI
    • Time To Interactive 可交互时间
    • 应用完全可用
    • 可用使用 tti-polyfill 去检测
  6. Long tasks
    • 超过了50ms的任务
  7. SSR & CSR
    • 服务端渲染 & 客服端渲染
  8. Isomorphic Javascript
    • 同构化

# 浏览器性能检测对象

利用浏览器性能检测对象检测应用性能:

  1. 获取应用的 FPFCP:(页面需要有实际内容渲染)
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
  1. 检测页面的 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

网页加载的时间轴 时间轴

# 白屏问题

原因:

  1. css & js 文件获取
  2. js 文件解析
  3. dom 生成
  4. cssom 生成
  5. 首帧 HTML 包含内容: 基本的 dom & css
Last Updated: 12/2/2019, 9:23:57 AM