跳到主要内容

📄 网页浏览流程

1. Please explain how the browser obtains HTML from the server and how the browser renders the HTML on the screen​

请说明浏览器如何从 server 端取得 HTML,并如何在浏览器上渲染 HTML

1. 发起请求​

  • 网址输入:用户在浏览器上输入网址,或是点击一个链接,浏览器会开始解析这串 URL,确认要向哪个服务器发起请求。
  • DNS 查找:浏览器开始执行查找 DNS,以及对应的服务器 IP 地址。
  • 建立连接:浏览器通过互联网使用 HTTP 或 HTTPS 协议,向服务器的 IP 地址发出请求,同时如果是 HTTPS 协议,还需要进行 SSL/TLS 连接。

2. Server 端响应​

  • 处理请求:服务器接收到请求后,会根据请求的路径与参数,从数据库中读取对应的数据。
  • 发送 Response:接着会将 HTML 文件作为 HTTP Response 的一部分发送回浏览器,Response 本身还会包含诸如状态码或其他参数(cors, content-type)等。

3. 解析 HTML​

  • 构建 DOM Tree:浏览器开始读取 HTML 文件,并根据 HTML 文件的标签与属性,转换成 DOM 并开始在内存中构建 DOM Tree。
  • requesting subresources(请求子资源):解析 HTML 文件时,如果遇到外部资源,例如 CSS、JavaScript、图片等,浏览器会进一步向服务器发起请求,获取这些资料。

4. Render Page(渲染页面)​

  • 构建 CSSOM Tree:浏览器开始解析 CSS 文件,构建 CSSOM Tree。
  • Render Tree:浏览器将 DOM Tree 和 CSSOM Tree 合并成一个 Render Tree,包含所有要渲染的节点与对应样式。
  • Layout(布局):浏览器开始进行版面设定(Layout 或 Reflow),计算每个节点的位置与大小。
  • Paint(绘制):最后浏览器经过绘制(painting)阶段,将每个节点的内容画到页面上。

5. JavaScript 交互​

  • 执行 JavaScript:如果 HTML 中包含有 JavaScript,则浏览器会解析并执行,这个动作可能会改变 DOM 和修改样式。

整个流程上是一种渐进的状态,理论上,用户会先看到部分网页内容,最后才看到完整的网页,这个过程中,可能会触发多次回流与重绘,尤其是网页本身包含复杂的样式或是交互效果尤为明显。这时除了浏览器本身执行的优化外,开发者通常也会尽可能采用一些手段,让使用体验更为平滑。

2. Please describe Reflow and Repaint​

Reflow(回流/重排)​

泛指网页中的 DOM 产生变化,导致浏览器需要重新计算元素的位置,将其摆放到正确位置,比较白话来说,就是 Layout 要重新产生排列元素。

触发 Reflow​

回流存在两种情境,一种是全局整个页面都出现变化,另一,则是部分 component 区块产生变化。

  • 初始进入页面时,是影响最大的一次回流
  • 添加或删除 DOM 元素。
  • 针对元素改变它的尺寸大小,譬如内文增加,或是文字大小变化等等。
  • 元素的排版方式调整,譬如通过 margin 或 padding 来调整移动。
  • 浏览器本身的视窗大小出现变化。
  • 触发伪类,例如 hover 效果。

Repaint(重绘)​

没有改变 Layout,单纯更新或改变元素,因为元素本身是内含在 Layout 中,所以如果触发回流必然会导致重绘,反之,仅触发重绘则不一定会回流。

触发 Repaint​

  • 改变元素的颜色或背景,譬如添加 color 或是调整 background 的属性等等。
  • 改变元素的阴影或是 border 也属于重绘。

如何优化 Reflow 或 Repaint​

  • 不要使用 table 排版,table 的属性容易因为改动属性,而导致排版会重新排列,若不得已需要使用的话,建议添加以下属性,使其每次仅渲染一行,避免影响整个表单范围,例如 table-layout: auto; or table-layout: fixed;。
  • 不应该操作 DOM 去逐一调整样式,而是应该将需要改变的样式通过 class 定义好之后,再通过 JS 进行切换。
    • 以 Vue 框架为例,可以用绑定 class 的方式来切换样式,而不是用 function 来直接修改样式。
  • 如果是一个需要频繁切换的场景,譬如 tab 切换,应该优先考虑使用 v-show 而非 v-if,前者仅使用 css 的 display: none; 属性来做隐藏,而后者却会触发生命周期,重新创建或销毁元素,自然会有更大的性能消耗。
  • 如果真的不得已需要触发回流,可以通过 requestAnimationFrame 来进行优化(主要是因为这个 api 有针对动画来设计,可以和浏览器绘制的帧数同步),这样可以将多次回流合并成一次,减少重绘的次数。
    • 譬如某个动画,需要在页面上向目标移动,这边就可以通过 requestAnimationFrame 来计算每一次移动。
    • 同样的,CSS3 的部分属性,可以触发 client 端的硬件加速,可以提升动画的效能,例如 transform opacity filters Will-change。
  • 如果条件允许,尽可能在较低层级的 DOM 节点上来改动样式,避免因为触发父元素样式变动,导致其下所有子元素全部被影响。
  • 如果需要执行动画,可以在绝对定位的元素 absolute , fixed 上使用,这样对其他元素影响不大,仅会触发重绘,可以避免回流。

Example​

// bad
const element = document.querySelector('.wrapper');
element.style.margin = '4px';
element.style.padding = '6px';
element.style.borderRadius = '10px';
// good
.update {
margin: 4px;
padding: 6px;
border-radius: 10px;
}

const element = document.querySelector('.wrapper');
element.classList.add('update');

Reference​

3. Please describe when will the browser send the options to the server​

请说明浏览器何时会向 server 端发送 options

多数情况下,是应用在 CORS 场景,在实际送出请求之前,会先有一个 preflight(预检) 的动作,浏览器会先发送一个 OPTIONS 请求,询问 server 是否允许这个跨域请求,如果 server 回应允许,浏览器才会发送真正的请求,反之,如果不允许,则浏览器跳出 error。

另外,假如请求的 method 不是 GET, HEAD, POST 也会触发 OPTIONS 请求。