浏览器实现原理详解

135 阅读9分钟

渲染系列

资源加载

  • DNS 解析:当用户在浏览器地址栏输入一个 URL,浏览器首先会查看本地的 DNS 缓存(包括操作系统和浏览器自身的缓存),如果缓存中存在对应的 IP 地址,就直接使用该地址;若缓存中没有,则向本地 DNS 服务器发起查询请求。本地 DNS 服务器如果没有记录,会向根域名服务器、顶级域名服务器等逐级查询,最终获取到目标域名对应的 IP 地址。例如,访问 www.example.com,浏览器会经过一系列查询找到该域名对应的 IP 地址,如 192.0.2.1
  • TCP 连接建立:基于 DNS 解析得到的 IP 地址和端口号(默认 HTTP 端口是 80,HTTPS 端口是 443),浏览器使用 TCP 协议与服务器建立连接。这个过程遵循 TCP 的三次握手机制:客户端向服务器发送 SYN 包,请求建立连接;服务器收到 SYN 包后,向客户端发送 SYN + ACK 包,表示同意建立连接;客户端收到 SYN + ACK 包后,再向服务器发送 ACK 包,完成连接建立。
  • HTTP 请求与响应:连接建立后,浏览器会根据用户请求的资源类型和操作,构造 HTTP 请求并发送给服务器。例如,一个简单的 GET 请求可能如下:
GET /index.html HTTP/1.1
Host: www.example.com
User - Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q = 0.9,image/avif,image/webp,image/apng,*/*;q = 0.8,application/signed - exchange;v = b3;q = 0.9

服务器接收到请求后,处理请求并返回 HTTP 响应,响应示例如下:

HTTP/1.1 200 OK
Content - Type: text/html; charset=UTF - 8
Content - Length: 1234
Date: Mon, 21 Jun 2021 12:00:00 GMT

<!DOCTYPE html>
<html>
<head>
    <title>Example Page</title>
</head>
<body>
    <h1>Welcome to the example page</h1>
</body>
</html>

页面解析与渲染

  • HTML 解析与 DOM 构建:浏览器接收到 HTML 响应后,开始解析 HTML 代码。解析器从代码的起始位置开始,逐个读取字符,识别出 HTML 标签和文本内容,将其转换为一个个的标记(token),如 <html><body><h1> 等。然后根据这些标记构建 DOM 树,DOM 树的每个节点代表一个 HTML 元素或文本节点,节点之间的父子关系反映了 HTML 元素的嵌套结构。例如,对于上述 HTML 响应,会构建出如下的 DOM 树:
html
├── head
│   └── title
│       └── "Example Page"
└── body
    └── h1
        └── "Welcome to the example page"
  • CSS 解析与 CSSOM 构建:如果 HTML 文档中引用了 CSS 文件,浏览器会并行请求这些 CSS 文件。接收到 CSS 文件后,CSS 解析器会将 CSS 代码解析为样式规则,这些规则定义了 HTML 元素的外观和布局。例如,对于 CSS 代码:
h1 {
    color: red;
    font - size: 24px;
}

解析器会将其转换为样式规则对象,然后根据这些规则构建 CSSOM 树。CSSOM 树的节点代表 CSS 选择器和对应的样式属性,与 DOM 树结合起来确定每个元素的最终样式。

  • 渲染树构建:将 DOM 树和 CSSOM 树合并,构建渲染树。渲染树只包含需要显示在页面上的元素及其样式信息,会过滤掉那些不可见的元素(如 display: none 的元素)。例如,如果在上述 HTML 中某个元素设置了 display: none,则该元素不会出现在渲染树中。
  • 布局计算:根据渲染树中节点的样式和布局信息,计算每个元素在页面上的具体位置和大小。布局计算是一个递归的过程,从根节点开始,依次计算每个子节点的位置和大小。例如,对于一个包含多个 <div> 元素的页面,布局过程会确定每个 <div> 元素的宽度、高度、左边距、上边距等信息。
  • 绘制:根据布局结果,将渲染树中的元素绘制到屏幕上。绘制过程是将元素的样式和内容转换为像素的过程,涉及到颜色填充、文本绘制、图像绘制等操作。绘制通常是按照从后往前的顺序进行,以确保元素的层次关系正确。例如,背景元素会先绘制,然后再绘制前景元素。
  • 合成:现代浏览器采用合成技术来提高页面的渲染性能。合成是将页面的不同层分别绘制,然后合并到一起显示在屏幕上。例如,页面的背景、滚动条、浮动元素等可以分别作为不同的层进行绘制和处理,这样在页面发生滚动或元素发生变化时,只需要重新绘制和合成相关的层,而不需要重新绘制整个页面。

JavaScript 执行系列

解析与编译

  • 词法分析:JavaScript 引擎首先对 JavaScript 代码进行词法分析,将代码分解成一个个的词法单元(token)。例如,对于代码 var num = 10;,词法分析器会将其分解为 varnum=10; 等词法单元。
  • 语法分析:根据词法单元,语法分析器会根据 JavaScript 的语法规则构建抽象语法树(AST)。AST 是一种树形结构,它以一种更结构化的方式表示代码的语法结构。例如,上述代码的 AST 会包含变量声明、赋值操作等节点。
  • 编译:将抽象语法树转换为字节码或机器码。现代 JavaScript 引擎通常采用即时编译(JIT)技术,在运行时将频繁执行的代码编译成机器码,提高执行效率。例如,Chrome 的 V8 引擎会先将代码编译为字节码,然后在运行过程中,对于热点代码(即执行频率高的代码)会进一步编译为机器码。

执行与事件循环

  • 执行栈:JavaScript 引擎在执行代码时,会维护一个执行栈。当调用一个函数时,会创建一个新的执行上下文并压入执行栈中;当函数执行完毕后,会将该执行上下文从执行栈中弹出。例如:
function outer() {
    function inner() {
        console.log('Inner function');
    }
    inner();
    console.log('Outer function');
}
outer();

在执行过程中,会先将 outer 函数的执行上下文压入执行栈,然后在 outer 函数内部调用 inner 函数时,将 inner 函数的执行上下文压入执行栈。inner 函数执行完毕后,其执行上下文从执行栈中弹出,接着 outer 函数继续执行,执行完毕后,其执行上下文也从执行栈中弹出。

  • 事件循环:JavaScript 是单线程的,但可以通过事件循环处理异步操作。异步操作(如定时器、网络请求等)会被放入任务队列中。事件循环不断地从任务队列中取出任务并放入执行栈中执行。例如:
console.log('Start');
setTimeout(() => {
    console.log('Timeout');
}, 1000);
console.log('End');

代码执行时,先打印 Start,然后遇到 setTimeout,将其回调函数放入任务队列中等待 1 秒后执行。接着打印 End,此时执行栈为空,事件循环会在 1 秒后将 setTimeout 的回调函数从任务队列中取出并放入执行栈中执行,打印 Timeout

存储与安全系列

数据存储

  • 缓存:浏览器会将访问过的资源(如 HTML 文件、图片、脚本等)缓存到本地,以减少网络请求,提高访问速度。缓存分为强缓存和协商缓存。强缓存通过响应头中的 Cache - ControlExpires 字段控制,例如 Cache - Control: max - age = 3600 表示资源在 3600 秒内可以直接使用本地缓存。协商缓存通过 ETagLast - Modified 字段控制,当强缓存失效时,浏览器会发送请求到服务器,通过比较 ETagLast - Modified 的值来判断资源是否有更新。
  • Cookie:Cookie 是服务器发送到用户浏览器并保存在本地的一小块数据,用于在客户端和服务器之间传递信息。例如,用于保存用户的登录状态、用户偏好等。浏览器在每次请求时会将 Cookie 发送到服务器,服务器可以根据 Cookie 的信息来识别用户。Cookie 有不同的属性,如 namevaluedomainpathexpires 等,通过这些属性可以控制 Cookie 的作用范围和有效期。
  • 本地存储:HTML5 提供了 localStoragesessionStorage 两种本地存储方式。localStorage 用于长期保存数据,除非手动删除,否则数据不会过期;sessionStorage 用于临时保存数据,数据在会话结束(如关闭浏览器窗口)后会被清除。例如:
// 设置 localStorage
localStorage.setItem('username', 'John');
// 获取 localStorage
const username = localStorage.getItem('username');

安全机制

  • 同源策略:同源策略是浏览器的核心安全机制之一,它限制了不同源(协议、域名、端口都相同)的页面之间的交互。例如,一个页面无法直接访问另一个不同源页面的 DOM 元素、发送 AJAX 请求等。这可以防止恶意网站通过跨源访问获取用户的敏感信息。
  • CORS(跨域资源共享):为了支持合法的跨域请求,W3C 提出了 CORS 机制。服务器可以通过设置响应头(如 Access - Control - Allow - Origin)来允许特定源的页面访问其资源。例如,服务器可以设置 Access - Control - Allow - Origin: https://example.com,表示只允许 https://example.com 这个源的页面访问其资源。
  • XSS(跨站脚本攻击)防护:XSS 攻击是指攻击者通过在网页中注入恶意脚本,当用户访问该页面时,恶意脚本会在用户的浏览器中执行,从而获取用户的敏感信息。浏览器通过对输入和输出进行编码、过滤等方式来防止 XSS 攻击。例如,对用户输入的 HTML 标签进行转义处理,避免其被解析为 HTML 代码。
  • CSRF(跨站请求伪造)防护:CSRF 攻击是指攻击者通过诱导用户在已登录的网站上执行恶意请求。为了防止 CSRF 攻击,服务器可以使用 CSRF 令牌,在用户登录时生成一个唯一的令牌,并将其包含在表单或请求头中。当用户发起请求时,服务器会验证该令牌的有效性。