跳到主要内容

浏览器的组成

浏览器的核心是两部分:渲染引擎和 JavaScript 解释器(又称 JavaScript 引擎)。

渲染引擎

不同的浏览器有不同的渲染引擎。

Firefox:Gecko 引擎,Safari:WebKit 引擎,Chrome:Blink 引擎,IE: Trident 引擎,Edge: EdgeHTML 引擎

渲染过程四个阶段。

  1. 解析代码:HTML 代码解析为 DOM,CSS 代码解析为 CSSOM(CSS Object Model)。
  2. 对象合成:将 DOM 和 CSSOM 合成一棵渲染树(render tree)。
  3. 布局:计算出渲染树的布局(layout)。
  4. 绘制:将渲染树绘制到屏幕。

以上四步并非严格按顺序执行,往往第一步还没完成,第二步和第三步就已经开始了。所以,会看到这种情况:网页的 HTML 代码还没下载完,但浏览器已经显示出内容了。

重绘和回流

渲染树转换为布局,称为“布局流”(flow);布局显示到页面的这个过程,称为“绘制”(paint)。它们都具有阻塞效应,并且会耗费很多时间和计算资源。

页面生成以后,脚本操作和样式表操作,都会触发“重流”(reflow)和“重绘”(repaint)。再比如,重绘 table 布局和 flex 布局,开销都会比较大。

优化:

  • 读取 DOM 或者写入 DOM,尽量写在一起,不要混杂。不要读取一个 DOM 节点,然后立刻写入,接着再读取一个 DOM 节点。
  • 缓存 DOM 信息。
  • 一次性改变样式。
  • 使用 documentFragment 操作 DOM
  • 动画使用 absolute 定位或 fixed 定位,这样可以减少对其他元素的影响。
  • 只在必要时才显示隐藏元素。
  • 使用 window.requestAnimationFrame(),因为它可以把代码推迟到下一次重绘之前执行,而不是立即要求页面重绘。
  • 使用虚拟 DOM(virtual DOM)库。

JavaScript 引擎

早期,JavaScript 处理过程如下:

  • 读取代码,进行词法分析(Lexical analysis),将代码分解成词元(token)。
  • 对词元进行语法分析(parsing),将代码整理成“语法树”(syntax tree)。
  • 使用“翻译器”(translator),将代码转为字节码(bytecode)。
  • 使用“字节码解释器”(bytecode interpreter),将字节码转为机器码。

现代浏览器改为采用“即时编译”(Just In Time compiler,缩写 JIT),即字节码只在运行时编译,用到哪一行就编译哪一行,并且把编译结果缓存(inline cache)。

字节码不能直接运行,而是运行在一个虚拟机(Virtual Machine)之上,一般也把虚拟机称为 JavaScript 引擎。

网页中嵌入 JavaScript 代码,主要有四种方法。

  • <script> 元素直接嵌入代码。
  • <script> 标签加载外部脚本
  • 事件属性
  • URL 协议

工作原理

浏览器加载 JavaScript 脚本,主要通过<script>元素完成。正常的网页加载流程是这样的。

  1. 浏览器一边下载 HTML 网页,一边开始解析。也就是说,不等到下载完,就开始解析。
  2. 解析过程中,浏览器发现<script>元素,就暂停解析,把网页渲染的控制权转交给 JavaScript 引擎。
  3. 如果<script>元素引用了外部脚本,就下载该脚本再执行,否则就直接执行代码。
  4. JavaScript 引擎执行完毕,控制权交还渲染引擎,恢复往下解析 HTML 网页。

优化

  1. defer 属性

    继续完成解析 HTML 后,执行下载好的 js。

<script src="a.js" defer></script>
<script src="b.js" defer></script>
  1. async 属性

    继续往下解析 HTML,js 下载完成,立即执行,继续解析 html。

::: tip 提示 同时使用 async 和 defer 属性,浏览器行为由 async 属性决定。 :::

参考链接

https://wangdoc.com/javascript/bom/engine#%E5%8F%82%E8%80%83%E9%93%BE%E6%8E%A5