浏览器渲染进程,解决前端面试

前言

复习浏览器原理这一块的浏览器渲染过程

浏览器渲染过程

这个过程是

  1. 解析HTML,生成Dom树
  2. 解析CSS,生成Style Rules
  3. 接着HTML和CSS进行结合,生成渲染树Render Tree
  4. 之后通过计算每一个元素的大小,位置,给出每个节点所应该出现的屏幕精确坐标,从而得到基于渲染树的 布局渲染树Layout of the render tree)。
  5. 遍历渲染树,将每个节点用 UI 渲染引擎来绘制,从而将整棵树绘制到页面上,这个步骤叫 绘制渲染树Painting the render tree

重绘

元素样式发生改变,并不涉及到位置发生改变。这个影响的是上面的步骤五

会引起重绘的: color、background-color、visibility

回流(重排)

元素的尺寸、结构、位置发生了改变会引起回流。涉及一些width、height、padding、margin、left、top之类的会触发回流。

引起回流的:

  • 页面首次渲染
  • 浏览器窗口大小发生改变
  • 元素尺寸或位置发生改变
  • 元素内容变化(文字数量或图片大小等等)
  • 元素字体大小变化
  • 添加或者删除可见DOM元素
  • 激活CSS伪类(例如::hover
  • 查询某些属性或调用某些方法

注意

  1. 回流的代价比重绘的要大,当一个元素的位置发生了改变,旁边的元素可能会相应地位置发生改变,但是当一个元素的样式发生改变,不会引起其他元素样式的改变。

  2. 但是现在浏览器对这个进行了优化: 浏览器会把所有的重绘和重排这些任务放到一个队列里面,等到了一定的时间或者数量的话就会对这些任务进行批量处理。这样就大大减少了重绘和重排的次数。

ini 复制代码
div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

这种只需要触发一次重排

  1. 当遇到offsetWidth、offsetHeight、clientWidth、clientHeight、scrollTop、scrollWidth这些的时候,因为需要及时计算出来的,所以浏览器需要重新进行布局计算,所以之前的队列里面的任务将会清空。
ini 复制代码
div.style.left = '10px';
console.log(div.offsetLeft);
div.style.top = '10px';
console.log(div.offsetTop);
div.style.width = '20px';
console.log(div.offsetWidth);
div.style.height = '20px';
console.log(div.offsetHeight);

他就是需要每次立即执行重排或者重绘,所以需要触发4次重排+重绘

降低重绘和回流的方法

CSS:

  1. 避免使用Table布局
  2. 避免设置多层内联样式
  3. 避免使用CSS表达式(例如:calc()
  4. 使用 visibility 替换 display

JS:

  1. 使用类名对样式逐条更改
ini 复制代码
const div = document.querySelector('.myDiv')
div.style.width = '100px'
div.style.height = '100px'
div.style.border = '1px solid blue'

这样会多次触发重绘和重排,应该

css 复制代码
.myDiv{
    width:100px;
    height:100px;
    border:1px solid blue;
}
duv.classList.add('myDiv')
  1. 缓存对敏感属性值的计算

有些场景我们需要多次计算来获取元素在页面的布局位置

ini 复制代码
const list = document.querySelector('.list')
for(let i = 0;i < 10;i++){
    list.style.top = `${list.offsetTop + 10}px`
    list.style.left = `${list.offsetLeft + 10}px`
}

这个计算属性会导致触发布局重新计算,所以消耗的性能会很大,需要对它进行缓存

ini 复制代码
const list = document.querySelector('.list')
let offsetTop = list.offsetTop,offsetLeft = list.offsetLeft
for(let i = 0;i < 10;i++){
    offsetTop += 10
    offsetLeft += 10
}
list.style.top = offsetTop
list.style.left = offsetLeft
  1. 也可以先为元素设置display: none,操作结束后再把它显示出来。因为在display属性为none的元素上进行的DOM操作不会引发回流和重绘。
ini 复制代码
dom.display = 'none' 
// 修改dom样式 
dom.display = 'block'

4.position属性为absolute或fixed:重排开销比较小,不用考虑它对其他元素的影响

最后

感谢观看

相关推荐
kyriewen8 小时前
Anthropic 估值逼近万亿美元,Claude Sonnet 5 + Claude Science 一天两连发
前端·ai编程·claude
小徐_23339 小时前
Wot UI 2.2.0 发布:Button 新增 subtle,VideoPreview 预览体验继续增强
前端·微信小程序·uni-app
天蓝色的鱼鱼11 小时前
关于 CSS 你可能不知道的属性,但关键时刻很有用
前端·css
泯泷12 小时前
第 2 篇:设计第一套字节码:Opcode、Instruction 与 Constant Pool
前端·javascript·安全
妙码生花12 小时前
从 PHP 到 AI + Golang,程序员自救转型手记(十五):优化细节、网络请求封装
前端·后端·ai编程
泯泷12 小时前
第 1 篇:从 1 + 2 开始:亲手写出第一台 JSVM
前端·javascript·安全
团团崽_七分甜12 小时前
Spring Boot 核心知识点总结
前端
lichenyang45313 小时前
从一个按钮开始,理解 ASCF 框架到底在做什么
前端
古夕13 小时前
第三方 SSO 接入实践:redirect_uri 编码、回调一致性与跨项目联调
前端·vue.js