import{_ as s,o as a,c as n,R as e}from"./chunks/framework.a304f0f7.js";const h=JSON.parse('{"title":"深入理解浏览器运行原理","description":"","frontmatter":{},"headers":[],"relativePath":"article/深入理解浏览器运行原理.md","lastUpdated":1682669214000}'),l={name:"article/深入理解浏览器运行原理.md"},p=e(`

深入理解浏览器运行原理

网页解析过程

输入域名 => DNS解析为IP => 目标服务器返回index.html

DNS:Domain Name System

HTML解析过程

浏览器解析HTML过程浏览器是和如何工作的

How browsers work

生成CSS规则

在解析的过程中,如果遇到<link>元素,那么会由浏览器负责下载对应的CSS文件

浏览器下载完CSS文件后,就会对CSS文件进行解析,解析出对应的规则树:

构建Render Tree

有了DOM Tree和CSSOM Tree之后,就可以将二者结合,构建Render Tree了

此时,如果有某些元素的CSS属性display: none;那么这个元素就不会出现在Render Tree中

布局和绘制(Layout & Paint)

第四步是在渲染树(Render Tree)上运行布局(Layout),以计算每个节点的几何体

第五步是将每个节点绘制(Paint)到屏幕上

回流和重绘(Reflow & )

回流也可称为重排

理解回流(Reflow):

什么情况下会引起回流?

理解重绘(Repaint):

什么情况下会引起重绘?

回流一定会引起重绘,所以回流是一件很消耗性能的事情

特殊解析: composite合成

在绘制的过程中,可以将布局后的元素绘制到多个合成图层中

标准流 => LayouTree => RenderLayer
\`position:fixed;\` => RenderLayer

默认情况,标准流中的内容都是被绘制在同一个图层(Layer)中的

而一些特殊的属性,浏览器会创建一个新的合成层(CompositingLayer),并且新的图层可以利用GPU来加速绘制

当元素具有哪些属性时,浏览器会为其创建新的合成层呢?

案例1:同一层渲染

.box1 {
  width: 100px;
  height: 100px;
  background-color: red;
}
.box2 {
  width: 100px;
  height: 100px;
  background-color: blue;
}
<body>
  <div class="box1"></div>
  <div class="box2"></div>
</body>

在开发者工具的图层工具中可以看到,两个元素.box1.box2都是在一个层(Document)下渲染的:

image-20221122103111654.png

案例2:分层渲染

当我们为.box2添加上position: fixed;属性,这时.box2将在由浏览器创建出来的合成层,分层单独渲染

.box2 {
  width: 100px;
  height: 100px;
  background-color: blue;
  position: fixed;
}
image-20221122103256116.png

案例3:transform 3D

为元素添加上transform属性时,浏览器也会为对应元素创建一个合成层,需要注意的是:只有3D的变化浏览器才会创建

如果是translateXtranslateY则不会

.box2 {
  width: 100px;
  height: 100px;
  background-color: blue;
  /* position: fixed; */
  transform: translateZ(10px);
}
image-20221122103715428.png

案例4:transition+transform

当我们为元素添加上动画时,动画的中间执行过程的渲染会在新的图层上进行,但是中间动画渲染完成后,结果会回到原始图层上

.box2 {
  width: 100px;
  height: 100px;
  background-color: blue;
  transition: transform 0.5s ease;
}
.box2:hover {
  transform: translateY(10px);
}

案例5:transition+opacity

transform类似,使用transition过渡的opacity动画,浏览器也会为其创建一个合成层

.box2 {
  width: 100px;
  height: 100px;
  background-color: blue;
  opacity: 1;
  transition: opacity 0.5s ease;
}
.box2:hover {
  opacity: 0.2;
}

总结

分层确实可以提高性能,但是它是以内存管理为代价的,因此不应当作为Web性能优化策略的一部分过度使用

浏览器对script元素的处理

之前我们说到,在解析到link标签时,浏览器会异步下载其中的css文件,并在DOM树构建完成后,将其与CSS Tree合成为RenderTree

但是当浏览器解析到script标签时,整个解析过程将被阻塞,当前script标签后面的DOM树将停止解析,直到当前script代码被下载、解析、执行完毕,才会继续解析HTML,构建DOM树

为什么要这样做呢?

这也会带来新的问题,比如在现代的页面开发中:

为了解决这个问题,浏览器的script标签为我们提供了两个属性(attribute):deferasync

defer属性

defer 即推迟,为script标签添加这个属性,相当于告诉浏览器:不要等待此脚本下载,而是继续解析HTML,构建DOM Tree

<script>
  console.log('script enter')
  window.addEventListener('DOMContentLoaded', () => {
    console.log('DOMContentLoaded enter')
  })
</script>
<script src="./defer.js" defer></script>
// defer.js
console.log('defer script enter')

上述代码在控制台的输出为:

script enter
defer script enter
DOMContentLoaded enter

async属性

async属性也可以做到:让脚本异步加载而不阻塞DOM树的构建,它与defer的区别:

要使用async属性标记的script操作DOM,必须在其中使用DOMContentLoaded监听器的回调函数,在该事件触发(DOM树构建完毕)后,执行相应的回调函数

`,87),i=[p];function r(o,c,t,d,b,u){return a(),n("div",null,i)}const C=s(l,[["render",r]]);export{h as __pageData,C as default};