编译器性能优化
SFC 编译总体速度提高 44%
基于使用Elk 存储库中的所有 SFC 文件完成的基准测试,这应该在很大程度上代表了现实世界的应用程序:
yaml
benching with:
- 225 files
- isProd: true
- sourceMap: true
- 3 warmup runs
- 10 bench runs
old compiler: 1513ms
new compiler: 845ms
new compiler is 44.15% faster.
---
benching with:
- 225 files
- isProd: true
- sourceMap: true
- 5 warmup runs
- 20 bench runs
old compiler: 2872ms
new compiler: 1618ms
new compiler is 43.66% faster.
注意:此基准测试仅测量解析 SFC 文件并将其转换为 JavaScript 和源映射所花费的时间@vue/compiler-sfc
。它不包括 CSS 处理、JavaScript 捆绑和缩小。因此,对整个项目的构建时间的影响不会那么显着,但仍然应该很明显。
解析速度提高 100%
解析器从头开始重写,速度提高了 100% - 即与旧模板相比,它解析相同模板的时间缩短了一半。
- 旧的解析器是递归下降解析器,它使用大量正则表达式和低效的前向搜索。
- 新的解析器使用从htmlparser2分叉的有限状态机分词器。它以线性方式迭代输入,具有最少的前瞻和回溯,并在很大程度上消除了对正则表达式的依赖。
代码生成速度加快 40%
-
优化行/列计算
以前,每次
CodegenContext.push
调用都涉及迭代推送的字符串以检查换行符,以便记录源映射生成的正确行和列位置。分析表明,在 中完成的这次迭代advancePositionWithMutation()
会产生不小的开销。在a95d76e
中,对此进行了优化,以便如果提前知道换行符的存在或位置,则可以跳过字符串迭代。 -
优化源地图生成
我们注意到,
SourceMapGenerator.addMapping
仅仅标准化和验证输入参数就花费了大量时间。鉴于我们始终知道所提供的确切参数,我们可以通过直接添加映射来避免开销。这是在8928473
中完成的。
消除 SFC 模板双重解析和源映射开销
<template>
与普通 Vue 模板相比,SFC 解析有一些不同的要求:由于需要支持自定义块,因此除应将其视为纯文本外的所有根级标签的内容。仍然需要完整的标签结构解析,因为内部<template>
可能存在嵌套<template>
标签,但是,由于旧解析器选项的设计方式,生成的 AST 不可重复用于模板编译。
这意味着对于每个 SFC,我们必须执行两次解析调用:一次针对 SFC 块,另一次针对实际模板内容。另外,由于第二次模板解析是对已经提取的内容进行的,因此我们需要重新映射其源映射位置以相对于整个SFC。这实际上是非常昂贵的,应该避免。
新的解析器通过将 SFC 解析逻辑视为首要关注点来解决这个问题。因此,我们可以直接重用该<template>
块的 AST 进行后续的转换和代码生成,并且还避免了昂贵的源映射重新映射。
API变更
重构对 AST 格式和@vue/compiler-core
解析器选项进行了一些细微的更改。这些属性和选项主要在内部或自定义编译器中使用(这确实是高级用例),因此它们不应影响大多数最终用户。
AST 格式更改
-
删除的属性:
SourceLocation.source
sqlinterface SourceLocation { start: Position end: Position --source: string }
为了方便起见,在 AST Node 的对象上公开了此属性
loc
,但使用很少。start
此外,可以使用和end
位置以及完整源字符串按需生成此信息。它的实用性并不能证明在解析期间生成这些子字符串的成本以及所需的额外内存是合理的,因此它被删除了。这可能会影响用户区模板转换插件,但应该很容易迁移。
-
删除的属性:
ElementNode.selfClosingTag
除了旧解析器在解析阶段之外,此属性从未在任何其他地方使用过,因此为了 AST 简洁性将其删除。
解析器选项更改
-
新选项:
parseMode
- 类型:
'base' | 'html' | 'sfc'
- 默认:
'base'
为了最大限度地提高性能,一些用于处理 HTML 特定行为的逻辑(例如,内部内容的特殊处理
<script>
)直接在标记生成器中处理。这种行为在默认模式下被禁用'base'
。在
'sfc'
mode 中,除 之外的所有根级标签中的内容都<template>
被视为纯文本,而 的内容<template>
在 mode 中进行解析'html'
。 - 类型:
-
新选项:
ns
这个新选项可用于在解析模板时指定根命名空间。
-
删除的选项:
getTextMode
此选项的等效逻辑已被硬编码到分词器中,以获得更好的性能。理论上,这确实在定义应被视为纯文本容器的替代标签列表方面消除了一些灵活性,但在实践中不存在这种用例。
尺寸增加
重构会导致运行时编译器大小稍大一些。全局构建的大小变化,包括编译器和运行时 (min+brotli):44.5kb => 46.4kb (+1.9kb)。考虑到性能改进以及大小增加不会影响使用构建步骤的项目这一事实,这是可以接受的。