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

前言

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

浏览器渲染过程

这个过程是

  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:重排开销比较小,不用考虑它对其他元素的影响

最后

感谢观看

相关推荐
索西引擎7 小时前
【React】Immer.js 在现代 Redux 生态中的角色:不可变性保障的工程化实现与开发体验优化
前端·javascript·react.js
大卫陈8 小时前
微信小程序虚拟支付实战:从「支付能力被限制」到沙箱调通的全过程
前端·后端
武子康8 小时前
vLLM 0.25.1:服务没有报错,为什么仍会生成垃圾 Token(5 级正确性门禁 + 自动回滚条件)
前端·人工智能·后端
jyp201211078 小时前
Vue3 Diff 算法
前端·vue.js
胡萝卜术8 小时前
抽象的三级跳:从原生 DOM 到 React 组件树,我们到底在解决什么问题?
前端·javascript·面试
黄敬峰8 小时前
从零理解React:事件、组件与响应式——一个WebGPU Demo的前端笔记
前端·面试
weedsfly8 小时前
从类继承到自定义 Hook:模板方法模式的前端演进
前端·javascript·面试
Quz8 小时前
QML 线性布局:Row/RowLayout 与 Column/ColumnLayout
前端·qt
小陈工8 小时前
第8篇:Flask轻量级框架与扩展生态深度解析(下)
后端·python·面试
你为她披上外套时我正站在窗外8 小时前
用 Rust + WebAssembly 写了个字幕工具,文件全程不离开浏览器
前端