解析HTML
准备工作: 获取资源
输入URL按下回车后, 浏览器会从网络线程池取出一个合适的线程并发起网络请求.
首先我们需要往哪里发.
如果是通过ip来访问的, 在操作系统发起网络请求前, 会先判断是不是在同个广播域.
把自己的IP和目标IP都计算一遍, 如果一样, 那么在同一个广播域; 如果不一样, 那么就需要查路由表确认下一跳, 将数据包的目标IP设置目标IP, 发给下一跳, 让下一跳帮我们发送数据.
现在知道了目标IP, 下一步就是获取MAC地址了.
如果操作系统最近获取过目标的MAC地址, 那么可以直接使用缓存; 如果没有, 就需要使用ARP来获取MAC地址.
操作系统会广播一个目标IP设置成目标IP, 目标MAC地址设为全为1(FF:FF:FF:FF:FF:FF). 广播域中的所有系统都会收到这个包, 但是只有目标IP跟自己匹配的系统才会回应这个包, 告诉我们正确的MAC地址.
拿到MAC地址后, 就可以发包了.
如果是通过域名访问的话, 那么需要先进行DNS解析得到IP, 然后再走一次上面的流程.
btw, DNS解析也是网络通信, 也是上面的流程. 不再重复.
浏览器的网络线程拿到响应头, 发现Content-Type: text/html, 之后拿到一点body就会把下载好的数据块包装成任务, 放到对应的任务队列里了.
解析HTML
浏览器逐个取出任务并开始解析, 尝试得到一棵DOM树和CSSOM树. 同时有一个预加载扫描器, 会扫描HTML文档, 提前下载需要的资源(js, css, 媒体资源等).
- 当主线程遇到js文件时, 会在这里阻塞. 等待js文件下载, 然后执行js.
- 当主线程遇到css文件时, 主线程不会阻塞, 继续构建DOM树. 当css下载好后, 主线程才拿过来解析css文件. (虽然不会阻塞解析DOM树, 但是会阻塞js的执行)
- 当主线程遇到媒体资源时, 主线程不会阻塞, 继续构建DOM树.
注: 这里依旧是在做解析HTML这一个宏任务. 网络进程/线程会通过IPC的方式, 通知主线程js/css文件的到来.
根据之前的事件循环, 每完成一个数据块的解析, 接下来会清空微任务队列, 然后如果需要渲染, 那么进行渲染.
这也是浏览器明明没有下载完HTML, 但我们却可以看到部分东西的原因.
残缺没有太多关系, 浏览器会自己补齐缺少的另一个标签
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>raw</title>
</head>
<body>
<div id="app">123这玩意是可以正常显示123的.
如果之后插入标签
插入普通标签
无事发生, 插了就插了. 浏览器会标记脏, 下次渲染的时候渲染.
插入需要网络资源的标签
img, video, script, style等
插到DOM树后, 主线程通知网络线程进行资源下载, 最后DOM任务源生成任务到对应的队列里.
主线程之后取出任务. 如果是js, 那么运行代码; 如果是css, 解析得到CSSOM; 如果图片等, 解码. 成功后执行onload回调, 失败了执行onerror回调.