让页面更加快速的加载,运行更加流畅,并且动画和交互也应该顺滑如丝,以顺应更多网络用户的需求。而编写高性能的网站应用就需要了解浏览器如何处理HTML、CSS 和 JavaScript,确保代码尽可能地高效运行

浏览器的渲染

通常浏览器渲染分以下几步进行

  1. 根据HTML构建HTML树(DOM),根据CSS构建CSS树(CSSOM),将两棵树合并在一起,形成渲染树(render tree)
  2. 样式计算,根据匹配选择器,计算出哪些元素应该用那些CSS规则的过程,知晓规则后将应用并计算每个元素的最终样式
  3. 布局,即根据文档流、盒模型等计算出元素的大小以及所在位置
  4. 绘制,将CSS中的边框、背景等样式进行适配
  5. 合成,根据层叠关系等,将元素样式共同展现出来

如果指定帧发生变化,即使用 JavaScript | CSS 或者 网络动画 ,实现视觉变化的时候,针对变化通常由以下三种运行方式.

  1. JS / CSS > 样式 > 布局 > 绘制 > 合成

    image.png
    如果修改了元素的 “layout” 属性,例如修改了元素的宽度,高度或者位置等,那么浏览器就必须检查其他的所有元素,然后自动重新排版页面,任何受影响的部分都将重新绘制,最终绘制好的元素再进行合成

  2. JS / CSS / > 样式 > 绘制 > 合成

image.png
如果修改的是背景图片 或者 文字颜色阴影,这些不影响页面排版布局的属性,那么浏览器会跳过布局,执行绘制,最后再合成

  1. JS / CSS > 样式 > 合成

image.png
如果更改的是一个不要布局也不要绘制的属性,那么浏览器就将跳转到执行合成,如改变transform
[最后这个版本浏览器执行的步骤最少,适用于动画或者滚动]

渲染优化

优化JavaScript

  • 对于动画效果的实现,使用requestAnim``ationFrame来代替 setTimeout 或 setInterval
  • 将长时间运行的JavaScript 从主线移到 Web Worker
  • 使用微任务来执行对多个帧的DOM更改
  • 使用Chrome的开发者工具里面的Timeline和JavaScript分析器评估JavaScript的影响

缩小样式计算的范围并降低其复杂性

通过添加和删除元素,更改属性 类,或者通过动画来更改DOM,都会导致浏览器重新计算元素样式,很多时候会对页面或者其中一部分进行自动重排.

  • 降低选择器的复杂性;使用以类为中心的方法,例如BEM.
  • 减少必须计算器央视的元素数量

避免大型 复杂的布局和布局抖动

  • 布局的作用范围一般为整个文档
  • DOM元素的数量将影响性能; 尽可能避免触发布局
  • 评估布局模型的性能;
  • 避免强制同步布局和布局抖动; 先读取样式值,然后进行样式更改

简单绘制的复杂度 减小绘制区域

  • 除了transform或者 opacity 属性外,更改任何属性始终都会触发绘制
  • 绘制通常是像素管道中开销最大的部分, 所以尽可能避免绘制
  • 通过层的提升和动画的编排来减少绘制区域
  • 使用开发者工具中绘制分析器评估绘制的复杂性和开销, 尽可能降低复杂性并且减少开销

坚持仅合成器的属性和管理层计数

  • 使用 transformopacity属性更改来实现动画
  • 使用 will-change或者 translateZ提升移动的元素
  • 避免过度使用提升规则; 各层都需要内存和管理开销

使输入处理程序去除抖动

  • 避免长时间运行输入处理程序; 它们可能组织滚动
  • 不要再输入处理程序中进行更改样式
  • 使处理程序去除抖动 ; 存储事件值并在下一个 requestAnimationFrame回调中处理样式更改

CSS动画

利用transform来实现动画效果,其常用属性为以下四个

translate: 这是一个控制元素移动的属性,有如下的表达方式 [支持的单位可以是像素,也可以是容器的%]
translateX( px | % )
translateY( px | % )
translate( px|% , px|% )[X 和 Y的缩写]
left: 50%;
top: 50%;
transform: translate( -50%,-50% ); 可以用来实现绝对定位元素的垂直水平居中
translateZ( px | % ); [父容器需要设定十点距离:perspective(px) ]

scale: 此为控制元素缩放的属性
scaleX( number ); 在X方向上缩放()倍
scaleY( number ); ``在Y方向上缩放()倍
scale( number , number ); 将X和Y合并

rotate:让元素旋转,单位可以是度数[deg] | 圈数[turn]
rotateX(angle|zero);绕X轴旋转多少度
rotateY(angle|zero);``绕Y轴旋转多少度
rotate( turn ); 旋转多少圈
rotateZ(angle|zero);``绕Z轴旋转多少度

skew: 倾斜,单位也是度数[deg] | 圈数[turn]
skewX( angle | zero );在X轴方向倾斜
skewY( angle | zero );在Y轴方向倾斜
skew( angle|zero , angle|zero );综合X和Y

需要注意的是inline元素并不支持transform,需要先将其转换成block.
再着通常会添加transition让变化更加平滑.

transition相关属性
transition:过度属性名 时长 过渡方式 延迟执行时间

  1. - 可以用 逗号 分隔开两个不同的过度属性来分开设置
  2. - 可以用 `all` 来代表所有属性
  3. - 过渡的方式有如下几种
  4. - `linear[线性] | ease[灵活] | ease-in[淡入] | ease-out[淡出] | ease-in-out[淡入淡出] | cubic-bezier[贝塞尔曲线] | step-start | step-end | steps`
  5. - 如有`display: none过渡到block` ,则需要将其改为 `visibility: hidden==>visible`
  6. - 如果有多次过渡变动的话
  7. - 可以使用多次transform,
  8. - `a== transform ==b`

b== transform ==c

  1. - `setTimeout `或者监听` transitionend `事件
  2. - 使用`animation`
  3. - 声明关键帧
  4. - 添加动画

animation
先为animation定义关键帧,通过@keyframes定义关键帧来控制CSS动画序列中的中间步骤,通常有两种写法
1. 定义初始 from状态 和to结束状态

  1. @keyframes slidein {
  2. from {
  3. margin-left: 100%;
  4. width: 300%;
  5. }
  6. to {
  7. margin-left: 0%;
  8. width: 100%;
  9. }
  10. }
  1. 2 . 以%形式定义对应帧数的样式
  1. @keyframes identifier {
  2. 0% { top: 0; left: 0; }
  3. 30% { top: 50px; }
  4. 68%, 72% { left: 50px; }
  5. 100% { top: 100px; left: 100%; }
  6. }

animation语法
缩写为: animation: 时长 | 过渡方式 | 延迟 | 循环次数 | 方向 | 填充模式 | 是否暂停 | 动画名

  1. - 时长: 可以是 s 或者 ms
  2. - 过渡方式: 线性 | 淡入 | 淡出等,类似于 transition取值
  3. - 延迟: 写入时间控制动画延时执行
  4. - 次数: 可以指定循环多少次,可以是 infinite无限次
  5. - 方向:
  6. - `reverse`[反向运行动画,周期结束动画由尾到头运行]
  7. - `alternate`[交替反向运行]
  8. - `alternate-reverse`
  9. - 填充模式:
  10. - `none` [动画未执行时,不会应用任何样式]
  11. - `forwards `[动画结束之后将保留动画的最后一个关键帧]
  12. - `backwards`[动画将在应用于目标时,立即应用第一个关键帧的值, 在延时时会会保留这个第一帧的值]
  13. - `both `[遵循`forwards` ` backward `的规则, 在两个方向上都扩展动画属性]
  14. - 是否暂停: `paused | running`
  15. - 动画名: 即是上面@keyframes定义的动画名称