likes
comments
collection
share

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

作者站长头像
站长
· 阅读数 5

前言

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

浏览器渲染过程

浏览器渲染进程,解决前端面试 这个过程是

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

div.style.left = '10px';
div.style.top = '10px';
div.style.width = '20px';
div.style.height = '20px';

这种只需要触发一次重排

  1. 当遇到offsetWidth、offsetHeight、clientWidth、clientHeight、scrollTop、scrollWidth这些的时候,因为需要及时计算出来的,所以浏览器需要重新进行布局计算,所以之前的队列里面的任务将会清空。
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. 使用类名对样式逐条更改
const div = document.querySelector('.myDiv')
div.style.width = '100px'
div.style.height = '100px'
div.style.border = '1px solid blue'

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

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

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

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`
}

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

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操作不会引发回流和重绘。
dom.display = 'none' 
// 修改dom样式 
dom.display = 'block'

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

最后

感谢观看