本文是建立在下文的基础之上完成的:
浏览器的渲染机制
渲染数构建之渲染树与DOM树的关系

一. 简述,这儿有几个重要的概念:

  1. 当浏览器通过地址获取到html以后会将它保存到内存中,浏览器会从内存中读取数据,所以html的解析都是从内存中的字节开始的,生成dom完整的流程是:字节数据 => 字符串 => Token => Node => DOM(对应Bytes → characters → tokens → nodes → object model);同理,CSSOM的构建过程是:字节数据 => 字符串 => Token => Node => CSSOM

    SRE实战 互联网时代守护先锋,助力企业售后服务体系运筹帷幄!一键直达领取阿里云限量特价优惠。
  2. 在DOM树构建的同时,浏览器会构建渲染树。然后浏览器根据渲染树进行布局,操作系统进行绘制;注意这儿如果DOM树在构建的时候遇到script,那么将阻碍DOM数的进一步构建,但是此时并不会阻碍操作系统将布局好的部分进行绘制;

  3. 当html中有link与script引入外部资源的时候,是并行发起请求;不管它的位置在什么地方;也就是说header中script与html底部的script,它们的src都是并行发起的(应该是 Bytes → characters 后)

对浏览器渲染机制的理解 随笔 第1张

二. 接下来我们会从几个简单的例子分析一下这个过程,然后得出一些结论

1.下面是本地创建的html文件,里面省略了一些非核心代码(服务器是node构建的服务器)

<!DOCTYPE html>
<html lang="en">
<head>
  ...
  <style>
    .box {
      color: red;
    }
  </style>
</head>
<body>
  <div class="box">
    这儿是box
  </div>
  <script src="http://127.0.0.1:8080/test.js"></script>
  <div class="box2">
    这儿是box2
  </div>
</body>
</html>

2.node核心代码,我们对js和css的返回都进行了延时处理
(1)app.js

const http = require('http');
const url = require('url');
const fs = require('fs');
const path = require('path');
const mime = require('mime');

// 创建服务器
const server = http.createServer((req, res) => {
  let requestPath = url.parse(req.url).pathname;
  // 获取请求文件的Content-type
  let type = mime.getType(requestPath);
  type = type?type:'text/plain';

  // 设置返回头
  res.setHeader('Content-type', `${type}; charset=utf-8`);
  
  let file = '';
  let time = 3000; // 设置默认时间

  // 如果检测到请求的是css
  if (/\.css$/.test(requestPath)) {
    file = path.resolve(__dirname, 'test/test.css');
    time = 8000;
  } else {
    file = path.resolve(__dirname, 'test/test.js')
  }
  let data = fs.readFileSync(file)
  setTimeout(() => {
    res.end(data);
  }, time)
});

server.listen('8080', '127.0.0.1', () => {
  console.log(`server run ar 127.0.0.1:8080`);
});

(2)test.css

.box {
  color: blue;
}

(3)test.js

var box = document.createElement('div');
box.innerText = '12345';
document.body.appendChild(box);

3.我们按照整个流程分析一下这个过程:
(1)浏览器从本地读取文件,在( Bytes → characters 后)就会向服务器发起请求,然后开始构建DOM树;
(2)当构建到style的时候,浏览器会构建CSSOM树,(注意,这儿CSSOM的构建会阻碍dom的继续构建,后面我们会通过一个简单的例子证明);
(3)当CSSDOM树构建完毕以后,DOM树继续构建,同时这时候会生成render渲染树;
(4)浏览器会根据渲染树进行布局,同时操作系统开始绘制;
(5)当构建到body中的时,DOM数的构建被堵塞,直到test.js被返回并且执行完毕以后,然后才会构建剩下的DOM(但是堵塞的时候,类为box的盒子是已经出现在浏览器上面了)
(6)直到DOM数构建完毕以后,剩下来改变页面显示的就是重绘和重排了;
(7)下面是被js文件阻塞,以及阻塞完毕以后显示的页面;
对浏览器渲染机制的理解 随笔 第2张
对浏览器渲染机制的理解 随笔 第3张

三. 接下里我们看一下,CSSOM树的构建是否会阻碍DOM树的继续构建?

1.修改html的代码如下,由于我们在服务器设置了延时处理,所以如果CSSOM在构建的时候阻塞了DOM的构建,那么页面上将出现一段比较明显的空白页;

<head>
  ......
  <link rel="stylesheet" href="http://127.0.0.1:8080/test.css"/>
</head>
<body>
  <div class="box">
    这儿是box
  </div>
  <div class="box2">
    这儿是box2
  </div>
</body>
</html>

2.刷新html,观察现象如下:
对浏览器渲染机制的理解 随笔 第4张
3.当css返回以后,dom树的构建继续:
对浏览器渲染机制的理解 随笔 第5张

四. 注意,并不是js返回以后就会立即执行,而是构建dom树的时候,遇到script标签时才会执行

1.修改代码如下

<head>
  ...
  <link rel="stylesheet" href="http://127.0.0.1:8080/test.css"/>
</head>
<body>
  <div class="box">
    这儿是box
  </div>
  <!-- 这儿是添加的代码 -->
  <script src="http://127.0.0.1:8080/test.js"></script>
  <div class="box2">
    这儿是box2
  </div>
</body>
</html>

2.前面我们设置的node中,如果是请求的是test.js,那么3s后将返回数据,如果是test.css将8s后返回数据
3.刷新html,可以发现,控制打印的数据,总是等待css返回以后才会打印,所以js文件并不是返回后就立即执行。
对浏览器渲染机制的理解 随笔 第6张

扫码关注我们
微信号:SRE实战
拒绝背锅 运筹帷幄