note
  • Introduction
  • algorithm
    • Array
    • BinarySearch
    • Bits
    • Design
    • DFS
    • DP
    • DP_bag
    • DP_stock.md
    • Hash
    • Heap
    • NodeList
    • Number
    • SlideWindow
    • Sort
    • Stack
    • String
    • Tree
  • Backend
    • express
    • koa2
    • node
    • npm
    • npx
  • db
    • mongoDB
  • Frontend
    • CSS
      • BFC
      • flex
      • layout
      • less
      • middle
      • position
      • sass
      • selector
      • 动画相关属性
      • 响应式页面
      • 层叠上下文
      • 隐藏元素
    • JS
      • Array
      • async
      • DOM
      • ES6
      • JS-军规
      • macrotask-microtask
      • practice
      • RegExp
      • this-prototype-inherit
      • type-convert
      • 前端请求
      • 浏览器加载
      • 浏览器窗口间通信
    • TS
      • note
    • Vue
      • practice
      • Component-Communicate
      • Component
      • lifecycle
      • note
      • Pinia
      • practice
      • Vue-cli
      • Vue-Router
      • Vue-Source
      • Vue2
      • Vue3
      • Vuex
    • cross-domain
    • webpack
    • 从前端角度理解缓存
    • 前端概念
    • 页面性能分析与优化
    • 页面渲染机制
  • Linux
    • basic-command
    • docker
    • java-env
    • learn
    • manage-command
    • nginx
    • Shell
    • ssh
    • tmux
    • vi
  • chrome-devtools
  • git
  • Jenkins
Powered by GitBook
On this page
  • 网页请求过程
  • 浏览器渲染过程
  • 主要流程
  • 具体流程
  • 重绘和回流
  • 重绘:
  • 回流
  • 性能影响
  • 如何避免
  1. Frontend

页面渲染机制

Previous页面性能分析与优化NextLinux

Last updated 2 years ago

网页请求过程

  1. 浏览器进行DNS域名解析,得到对应的IP地址

  2. 根据这个IP,找到对应的服务器建立连接(三次握手)

  3. 建立TCP连接后发起HTTP请求(一个完整的http请求报文)

  4. 服务器响应HTTP请求,浏览器得到html代码(服务器如何响应)

  5. 浏览器解析html代码,并请求html代码中的资源(如js、css、图片等)

  6. 浏览器对页面进行渲染呈现给用户

  7. 服务器关闭TCP连接(四次挥手)

浏览器渲染过程

主要流程

主流的浏览器内核主要有2种,Webkit 和 Geoko ,虽然 chrome 现在的内核更换为 blink ,但其实 blink是基于webkit的,差异不大。其渲染过程分别如下:

这两个内核的渲染流程大同小异,主要的过程可以总结为下列5个:

  • DomTree: 解析html构建DOM树

  • CssdomTree : 解析CSS生成CSSOM规则树

  • RenderTree: 将DOM树与CSSDOM规则树合并在一起生成渲染对象树

  • Layout: 遍历渲染树开始布局(layout),计算每个节点的位置大小信息

  • Painting: 将渲染树每个节点绘制到屏幕

具体流程

DOM树的构建: 浏览器在接收到html文件后即开始解析和构建DOM树,在碰到js代码段时,由于js代码可能会改变dom的结构,所以为避免重复操作,浏览器会停止dom树构建,先加载并解析js代码。而对于css,图片,视频等资源,则交由资源加载器去加载,这个过程是异步的,并不会阻碍dom树的生成。这个过程需要注意的点是:

display:none的元素、注释存在于dom树中 js会阻塞dom树的构建从而阻塞其他资源的并发加载,因此好的做法是将js放在最后加载 对于可异步加载的js片段加上 async 或 defer

CSSDOM树的构建: 浏览器在碰到<link> 和 <style> 标签时,会解析css生成cssom , 当然,link标签需要先将css文件加载完成才能解析。 需要注意的是:

js 代码会阻塞cssom的构建,在webkit内核中有所优化,只有js访问css才会阻塞 cssom的构建与dom树的构建是并行的 减少css的嵌套层级和合理的定义css选择器可以加快解析速度,可参考如何提升 CSS 选择器性能

RenderTree的构建: 在cssom 和dom 树都构建完成后,浏览器会将他们结合,生成渲染对象树,渲染树的每一个节点,包含了可见的dom节点和节点的样式 。 需要注意的是:

  • renderObject树 与 dom树不是完全对应的,不可见的元素如display:none 是不会放入渲染树的。

  • visibility: hidden的元素在Render Tree中

Layout: 这一步是浏览器遍历渲染对象树,并根据设备屏幕的信息,计算出节点的布局、位置,构建出渲染布局树(render layout)。渲染布局树输出的就是我们常说的盒子模型,需要注意的是:

  • float, absolute , fixed 的元素的位置会发生偏移

  • 我们常说的脱离文档流,其实就是脱离布局树

Painting: 浏览器对生成的布局树进行绘制,由用户界面后端层将每个节点绘制出来。此时,Webkit内核还需要将渲染结果从Renderer进程传递到Browser进程。 一句话:回流必将引起重绘,重绘不一定会引起回流。

重绘和回流

前面讲到,js代码可以访问和修改dom节点和css,所以在解析js的过程中会导致页面重新布局和渲染,这就是重绘(repaint)和回流(reflow)

重绘:

概念: 重绘是指css样式的改变,但元素的大小和尺寸不变,而导致节点的重新绘制。

重绘的触发: 任何对元素样式,如background-color、border-color、visibility 等属性的改变。css 和 js 都可能引起重绘。

回流

回流(reflow)是指元素的大小、位置发生了改变,而导致了布局的变化,从而导致了布局树的重新构建和渲染。

回流的触发

  • 页面首次渲染

  • 浏览器窗口大小发生改变

  • 元素内容变化(文字数量或图片大小等等)

  • 元素字体大小变化

  • dom元素的位置和尺寸大小的变化

  • dom元素的增加和删除

  • 激活CSS伪类(例如::hover)

  • 增加和删除class样式

  • 动态计算修改css样式

  • 查询某些属性或调用某些方法

一些常用且会导致回流的属性和方法:

clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
scrollIntoView()、scrollIntoViewIfNeeded()
getComputedStyle()
getBoundingClientRect()
scrollTo()

当然,我们的浏览器不会每一次reflow都立刻执行,而是会积攒一批,这个过程也被成为异步reflow,或者增量异步reflow。但是有些情况浏览器是不会这么做的,比如:resize窗口,改变了页面默认的字体,等。对于这些操作,浏览器会马上进行reflow

性能影响

浏览器会维护一个队列,把所有引起回流和重绘的操作放入队列中,如果队列中的任务数量或者时间间隔达到一个阈值的,浏览器就会将队列清空,进行一次批处理,这样可以把多次回流和重绘变成一次。 当你访问以下属性或方法时,浏览器会立刻清空队列:

clientWidth、clientHeight、clientTop、clientLeft
offsetWidth、offsetHeight、offsetTop、offsetLeft
scrollWidth、scrollHeight、scrollTop、scrollLeft
width、height
getComputedStyle()
getBoundingClientRect()

因为队列中可能会有影响到这些属性或方法返回值的操作,即使你希望获取的信息与队列中操作引发的改变无关,浏览器也会强行清空队列,确保你拿到的值是最精确的

如何避免

css

  • 避免使用table布局。

  • 尽可能在DOM树的最末端改变class。

  • 避免设置多层内联样式。

  • 将动画效果应用到position属性为absolute或fixed的元素上。

  • 避免使用CSS表达式(例如:calc())

js

  • 避免频繁操作样式,最好一次性重写style属性,或者将样式列表定义为class并一次性更改class属性。

  • 避免频繁操作DOM,创建一个documentFragment,在它上面应用所有DOM操作,最后再把它添加到文档中。

  • 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。

  • 避免频繁读取会引发回流/重绘的属性,如果确实需要多次使用,就用一个变量缓存起来。

  • 对具有复杂动画的元素使用绝对定位,使它脱离文档流,否则会引起父元素及后续元素频繁回流。

使用chrome浏览器的开发者工具,我们很容易看到这5个过程的时间线,下面是segmentfault主页的渲染截图: