渲染优化

  • 浏览器渲染原理和关键渲染路径
    • 浏览器的渲染流程
      • 1.浏览器构建对象模型(两棵树)
      • 2.浏览器构建渲染树
  • 回流与重绘, 如何避免布局抖动
    • Layout(布局)与Paint(绘制)
    • 影响回流的操作
    • 避免布局抖动(layout thrashing)
  • 使用FastDom【防止布局抖动的利器】
    • 使用FastDom批量对DOM的读写操作
  • 复合线程与图层【深入渲染流水线的最后一站】
    • 复合线程(compositor thread)与图层(layers)
    • 复合图层做什么
  • 避免重绘【必学,加速页面呈现】
    • 减少重绘的方案
  • 高频事件防抖【解救页面卡顿的秘药】
    • 高频 事件处理函数 防抖
  • React时间调度实现【中高级前端需要了解的React调度原理】
    • 基本原理

浏览器渲染原理和关键渲染路径

浏览器是怎么把页面渲染出来的,渲染过程分很多环节,就是关键渲染路径,只有理解了渲染经历了什么步骤,才知道针对性的进行优化
网络资源被加载过来后,脚本、css都要进行解析,解析完之后浏览器要进行理解,如何把内容画到页面上,这就是渲染的过程

主线程有Recalculate style(计算样式)、Layout(布局)、Paint(绘制),这几个就是渲染过程中几个非常重要的阶段

浏览器的渲染流程

Javascript(触发视觉变化) 》Style(浏览器对样式重新进行计算) 》Layout(布局) 》Paint(绘制)》Composite(合成),这就是关键渲染路径,一共5步,无论是首次加载还是后面页面发生了样式上的变化,都要经历这几个步骤,最终把页面呈现给用户,理论上这5个步骤都是会被经历的,但是有些样式不会影响布局,也不会影响绘制,所以浏览器就进行了优化,如果是这样的样式,实际上可以不经历布局和绘制的过程,这样渲染就可以大大的被加速
Javascript,是可以通过Javascript实现一些页面视觉上的变化,例如添加dom元素,实现动画,还可以用css做动画,web animation api实现动画,这些都会触发视觉上变化
Style,浏览器对样式重新进行计算,这个过程会根据选择器进行重新匹配,计算哪些元素css受到影响,新的规则是什么样的,应该绘制成什么样子,每个元素绘制成什么样我们就清楚了
Layout,布局把你的元素按照你说的样式绘制到页面上,要把它绘制到页面上,这实际上是几何问题,需要知道元素的大小、位置
Paint,绘制,真正把内容画到页面上,画文字、图片、颜色、阴影等
Composite,合成,绘制会和这个合成联系在一起,浏览器为了提高效率,并不是把所有的东西都花在同一个层里,类似ps里,会建多个图层,最后再把它们组合起来,形成我们最后的一张图,浏览器为了提高效率也会把不同的东西画在不同的层上,最终再把它们合成在一起显示出来
当用户在地址栏输入一个地址,然后回车后,到页面显示出来之前,都经历了哪些过程
当浏览器拿到服务端返回的资源后,做了什么事?
无论js、css、html,都是代码,是文本,计算机理解不了文本,所以第一步它要通过一些解释器,把这些文本翻译成它能理解的数据结构
html是如何被转换的?
首先浏览器下载完html文档,就要把代码读进去,读进去的是文本,它先把这些文本转换成单个的字符;第二步,html里面有很多标签,标签是通过一对箭括号标记出来的,这个箭括号就可以用作识别,就可以把一些字符串理解成有含义的标记,这些标记最终被换成节点对象,放在链形数据结构里,链形数据结构就类似下图中的树,这就可以把html描述的嵌套关系很好的表达出来,通过这样一棵树,就可以把html的内容、属性、节点之间所有的关系都给表达清楚,这就叫DOM(文档对象模型),描述了html的结构

css部分如何被转换?
一样的道理,当解释器遇到你可能引用了web的css样式表,先把资源下载过来,下载完成后对这个资源进行文本处理,把里面的标记全部识别出来,看样式描述的是哪个节点的样式,然后也用树形结构把这个关系存储起来,如下图:除了描述节点之间的联系之外,还把每个节点所关联的样式给挂载起来

1.浏览器构建对象模型(两棵树)

构建DOM对象:HTML>DOM
构建CSSOM对象:CSS>CSSOM

2.浏览器构建渲染树

DOM(描述的是内容)和CSSOM(描述的是样式)合并成Render Tree,把内容和样式合在一起,让浏览器理解最终我们要把什么画在页面上,合并的结果如下图,把真正需要显示的东西留下,不需要显示的东西去掉,比如span节点的样式是diaplay:none,不需要显示在页面上,构造成渲染树后,span节点就会被去掉,最终只会留下需要显示到页面上的,有了这颗渲染树后,浏览器利用这棵树,知道每个节点什么尺寸,画在什么位置,

回流与重绘, 如何避免布局抖动

Layout(布局)与Paint(绘制)

是关键渲染路径中最重要的两个步骤,也是开销最高的两个步骤,如何减少或避免布局和绘制的发生?

  • 渲染树只包含网页需要的节点
  • 布局根据渲染树布局,计算每个节点精准的位置和大小-“盒模型”,关心位置和大小
  • 绘制是像素化每个节点的过程,把节点画在屏幕上
    在我们页面中,这个关键路径至少会被走完一次,也就是最开始整个页面的加载
    布局关心的是位置和大小,元素的几何信息,所以你的样式例如修改背景颜色不会修改位置和大小,是不会触发布局,关键渲染路径中Layout就可以跳过,直接到重绘
    有没有即不会发生布局也不会发生重绘,是有的,有些动画是可以利用gpu加速,这种动画可以直接走复合的过程,不需要进行布局和重绘

影响回流的操作

布局也叫做回流,通常页面第一次加载完之后,把东西放在页面上我们叫做布局,如果是由于你之后页面上发生了视觉上的变化又导致再次布局,通常叫做回流

  • 添加/删除元素
  • display:none
  • 移动元素的位置
  • 操作styles
  • offsetLeft,scrollTop,clientWidth
  • 修改浏览器大小,字体大小

写个load事件,更改卡片的宽度,然后在performance里Timings那一栏load事件之后主线程有发生layout

如果一个回流操作不只影响本身,还会导致其他元素,甚至整个页面所有元素的位置都发生变化,这个消耗是非常高的,甚至页面会出现卡顿的状况

避免布局抖动(layout thrashing)

  • 避免回流
    比如想改变元素位置,千万不要修改top、left这样的值,可以使用transform或者translate,通过translate做位移,这个3d动画既不会发生回流也不会发生重绘,只会触发复合的过程;
    减少回流:react的v-dom减少回流,把一些你要会导致回流发生的操作,进行批量处理,积攒一些之后进行统一的计算,最后应用我们真正的dom上
  • 读写分离
    批量的读操作完再进行批量写的操作

页面上所有的图片都开始进行动画的变化,发现动画并不流畅

性能分析里右上角红色三角形是表示发生了长任务
提示了强制回流,我们应该引起重视,这种是一个问题,问题出现在for循环里,给我们width进行赋值时,先取了offsetTop,浏览器为了提高布局的性能,会尽量把修改布局相关属性的操作推迟,但是什么情况是无法推迟呢?当你获得布局相关属性比如offsetTop时是无法推迟,不得不立即进行最新的计算,以保证你能取得最新的结果,所以在布局前它就被强制进行了一次计算,所以会先读这个值再对width写的操作,这是个循环,有连续的读写,而且每次读的步骤都会强制我们的布局立即进行重新的计算,导致有连续不断的回流发生,会导致页面的抖动,结果页面非常卡顿。

使用FastDom【防止布局抖动的利器】


measure测量(读),mutate修改(写),把一些读和写的操作进行分离,然后再通过调度函数去安排,批量进行读,批量进行写,达到消除页面抖动的效果
FastDom有提供相关的例子

使用Fastdom修改上面代码

运行后发现load之后没有再出现右上角红色三角形警告的长任务了,也未出现有问题的layout

使用FastDom批量对DOM的读写操作

什么是FastDom
如何使用FastDom的APIS

复合线程与图层【深入渲染流水线的最后一站】

复合线程(compositor thread)与图层(layers)

复合主要把我们的页面拆解成不同的图层,当我们的页面发生一些视觉变化时,有时这个变化可以只影响一个图层的变化,其他图层不需要受到影响,这样绘制的过程可以更高效完成,

复合图层做什么

  • 将页面拆分图层进行绘制再进行复合
  • 利用DevTools了解网页的图层拆分情况
    页面是怎样拆成不同图层的,拆的规则是什么
    默认情况下它是由浏览器决定的,浏览器会根据一些规则来判断是否要将页面去拆分成多个图层,又把哪些元素拆分成一个单独的图层,主要分析的是元素和元素之间是否有相互的影响,如果某些元素对其他元素造成的影响非常多,它就会被提取成一个单独的图层,这样的好处是如果它发生变化,只对它这个图层进行相关的重绘,而不会影响其他部分
    我们也可以主动的把一些元素提取成一个单独的图层,我们知道这些元素会影响其他部分,我们把它提取出来,让它的变化变得更独立


左下角显示有两个图层,点击页面会显示出范围

  • 哪些样式仅影响复合,不触发布局和重绘

    当我们使用这些属性时,如果可以所涉及到的元素提取到单独的图层,这些元素在发生动画或者视觉上变化时,就只会触发复合,不会触发布局和重绘
    如下图,把卡片动画都拆成单独的图层,让gpu进行单独的处理

performance录制,移到卡片上的效果动画,如下图,发现会对样式进行重新计算,更新图层树,并没有发生布局和重绘,直接触发复合

但是如果把所有东西都拆到单独的图层中,也不行,图层越多,开销越高,会适得其反,会使页面所有操作非常慢,所以只把特定的,我们真正需要的,能达到这种效果的元素才提取到单独的图层

避免重绘【必学,加速页面呈现】




录制动画进行分析,4s左右点击触发了动画,触发动画后主线程开始繁忙,放大仔细看,有很多组,因为动画是持续不断的,我们可以看其中一组,首先会进行重新计算样式、更新图层树、再进行复合,所有任务执行得很快,没有长任务,也没有导致强制布局和布局抖动的问题


还有种方式看页面有没有发生重绘,修改样式如下,预计会发生布局和重绘

command+shift+p,勾选上Paint flashing,如果页面发生重绘,所重绘的区域会用绿色标记出来,非常方便我们观察页面有没有发生重绘


虽然tranform和opacity只影响复合,但是不要忘记做一件事,把它所影响到的元素提取到一个单独的图层,那是怎么做的?
在卡片的root样式类里有做个声明,利用了willChange属性,willChange值设置为transform,willChange: ‘transform’,这样浏览器就知道这个元素应当被提取到一个单独的图层里去进行,

减少重绘的方案

利用Devtools识别paint的瓶颈
利用will-change创建新的图层

import React from 'react';
import MaterialUICard from '@material-ui/core/Card';
import CardActions from '@material-ui/core/CardActions';
import CardContent from '@material-ui/core/CardContent';
import Button from '@material-ui/core/Button';
import Typography from '@material-ui/core/Typography';
import {withStyles} from '@material-ui/core/styles';
import './animation.css';
import {LazyLoadImage} from 'react-lazy-load-image-component';
import 'react-lazy-load-image-component/src/effects/blur.css';const styles = theme => ({root: {margin: theme.spacing(1),willChange: 'transform'},card: {width: 300},cardSpinning: {width: 300,animation: '3s linear 1s infinite running rotate'},media: {height: 200,width: 300,objectFit: 'cover',}
});class MyCard extends React.Component {constructor(props) {super(props);this.state = {spinning: false,}}spin = () => {this.setState({spinning: true});}render() {/*根据Spinning进行判断决定是否旋转*/let cardClass = this.state.spinning ? this.props.classes.cardSpinning : this.props.classes.card;return (<div className={this.props.classes.root}>{/*添加点击事件,触发动画*/}<MaterialUICard className={cardClass} onClick={this.spin}><LazyLoadImageclassName={this.props.classes.media}src={this.props.image}effect="blur"rel="preconnect"/><CardContent><Typography gutterBottom variant="h6" component="h2">{this.props.title}</Typography><Typography component="p">{this.props.description}</Typography></CardContent><CardActions className={this.props.classes.actions}><Button size="small" color="primary" variant="outlined">联系方式:												

(三)渲染优化 (与浏览器为友,共进退)相关推荐

  1. 【前端性能优化】浏览器渲染原理与性能优化

    目录 1. 浏览器渲染基本步骤 2. 构建DOM树.CSSOM树 3. 构建渲染树 4. 计算渲染树的布局 5. 将布局渲染到屏幕上 6. 渲染优化 1. 浏览器渲染基本步骤 浏览器主要有以下步骤: ...

  2. 前端性能优化之浏览器渲染原理和关键渲染路径、复合线程、图层及优化、JS 开销及优化和 HTML 和 CSS 优化

    一.浏览器渲染原理和关键渲染路径 浏览器构建渲染树,DOM 树和 CSSDOM 树合成为 Render Tree 渲染树. 浏览器的渲染流程,如下所示: JavaScript -> Style ...

  3. 图片渲染延迟_2D Canvas 的渲染优化

    简介 HTML 上的图形渲染主要有两种方案 SVG 和 Canvas,前者更易于使用,而后者潜力更大,本文主要关注如何使用 Canvas 绘制出更多的图形,提供更加流畅的交互.本文的内容有: 渲染机制 ...

  4. 前端性能优化(二)01-页面性能优化之浏览器——浏览器的主要作用 浏览器的组成结构

    前端性能优化(二)01-页面性能优化之浏览器--浏览器的主要作用 & 浏览器的组成结构 页面性能优化 前端性能优化可以分为两大部分:浏览器部分.代码部分. 浏览器部分又可以分为: 网络层面 浏 ...

  5. 前端性能优化之渲染优化

    1.知识体系 1.1从URL输入到页面加载 打开浏览器从输入网址到网页呈现在大家面前,背后到底发生了什么?经历怎么样的一个过程?现在带 大家来看看流程. 首先我们需要通过 DNS(域名解析系统)将 U ...

  6. 前端性能优化之资源传输优化、渲染优化、Web 加载和渲染原理

    一.资源传输优化 使用压缩 Gzip,如下所示: 对传输资源进行体积压缩,可高达 90% 配置 Nginx 启用 Gzip 启用 Keep Alive,如下所示: 一个持久的 TCP 连接,节省了连接 ...

  7. 【前端优化之渲染优化】大屏android手机动画丢帧的背后

    前言 上周我与阿里的宇果有一次技术的交流,然后对天猫H5站点做了一些浅层次的分析,后面点时间基本天天都会有联系,中途聊了一些技术细节.聊了双方团队在干什么,最后聊到了前端优化.因为我本身参与了几次携程 ...

  8. 【Android 性能优化】布局渲染优化 ( CPU 渲染优化 | 减少布局的嵌套 | 测量布局绘制时间 | OnFrameMetricsAvailableListener | 布局渲染优化总结 )

    文章目录 一. 减少布局嵌套 二. 布局渲染时间测量 1. FrameMetrics 使用流程 2. FrameMetrics 参数解析 3. FrameMetrics 代码示例 三. 布局渲染优化总 ...

  9. 【Android 性能优化】布局渲染优化 ( GPU 过度绘制优化总结 | CPU 渲染过程 | Layout Inspector 工具 | View Tree 分析 | 布局组件层级分析 )

    文章目录 一. GPU 过度绘制优化总结 二. CPU 渲染过程 三. CPU 渲染性能调试工具 Layout Inspector 四. Layout Inspector 组件树 DecorView ...

最新文章

  1. jQuery:动态改变html表单的目标页(Target)
  2. 源码剖析Django REST framework的认证方式及自定义认证
  3. MacBook Pro新版上市
  4. m_Orchestrate learning system---二十、如何写代码不容易犯错
  5. 2009年浙江大学计算机及软件工程研究生机试真题
  6. PWA(Progressive Web App)入门系列:安装 Web 应用
  7. Python基础----Pandas
  8. NLP最新趋势,7个主流业务场景!
  9. 51Nod-1031 骨牌覆盖【递推】
  10. python是什么专业学的-Python开发专业学校排名是什么样的
  11. 数据采集及采集工具八爪鱼的使用
  12. 机器学习基础:吴恩达vs贪心科技AI哪个更值得你选择?
  13. 分类--ROC 和曲线下面积
  14. 相片打印机原理_手机照片打印机的原理是什么 便携式照片打印机到底好不好...
  15. win32Day06:控件
  16. 苹果零售店十大秘诀:禁止纠正顾客发音错误
  17. 为什么你宁愿吃生活的苦,也不愿吃学习的苦
  18. 创新电影院5G的未来
  19. Python爬虫随笔:爬取iciba上的单词发音文件
  20. 智慧公寓管理系统解决方案

热门文章

  1. GitHub项目协作基本步骤
  2. 领域驱动设计(DDD)的精髓
  3. HBase phoenix二级索引
  4. MySQL事务控制语句
  5. (C#)Windows Shell 外壳编程系列4 - 上下文菜单(iContextMenu)(二)嵌入菜单和执行命令...
  6. tomcat和apache的结合   看最重要的   文档有点乱
  7. redis之(十一)redis实现缓存的功能
  8. 【Oracle Database 12c新特性】ASM Scrubbing Disk Groups
  9. PHP Fatal error: Class 'Mongo' not found
  10. WorkFlow4.0--入门到精通系列-专题索引