1.4 运行时和编译时
框架可以只有运行时、只有编译时,或者两者结合;编译器能分析用户内容并做优化,运行时则保证灵活性,Vue.js 3 选择了二者兼顾。
一、三种架构形态
| 形态 | 能否分析用户内容 | 优势 | 代价 |
|---|---|---|---|
| 纯运行时 | 不能(没有编译过程) | 结构简单、灵活 | 无法在编译期做优化 |
| 运行时 + 编译时 | 能(借助编译器) | 可在编译期提取信息、做进一步优化,同时保留运行时灵活性 | 需要额外构建/编译步骤 |
| 纯编译时 | 能 | 性能可能更好(可做更多静态优化) | 有损灵活性,用户提供的内容必须编译后才能用 |
- 加入编译步骤带来的收益:可以分析用户提供的内容、可以在编译时提取信息、可以做进一步的优化。
- 纯编译时虽然也能分析内容,但用户内容不经编译就无法使用,牺牲了灵活性。
二、Schema 式示例:Render / Compiler
假设有 Render(obj, root),接收一个树型结构的数据对象并渲染:
function Render(obj, root) {
const el = document.createElement(obj.tag)
if (typeof obj.children === 'string') {
const text = document.createTextNode(obj.children)
el.appendChild(text)
} else if (obj.children) {
// 数组:递归调用 Render,使用 el 作为 root 参数
obj.children.forEach((child) => Render(child, el))
}
root.appendChild(el)
}
纯运行时:用户直接提供数据对象,不涉及任何额外步骤。
const obj = {
tag: 'div',
children: [
{ tag: 'span', children: 'hello world' }
]
}
Render(obj, document.body)
运行时 + 编译时:用户提供 HTML 字符串,先经 Compiler 编译成数据对象,再交给 Render。
const html = `
<div>
<span>hello world</span>
</div>
`
const obj1 = Compiler(html) // 编译得到树型结构的数据对象
Render(obj1, document.body) // 再调用 Render 进行渲染
上例属于运行时编译——代码运行的时候才开始编译。更常见的做法是在构建时执行
Compiler,用户内容提前编译好,运行时无须再编译。
纯编译时:只要一个 Compiler,连 Render 都不需要,用户的代码必须经编译器编译后才能运行。
Compiler(html)
三、取舍结论
- 纯运行时:灵活但无法优化,且用户要手写冗长的数据对象。
- 纯编译时:理论上性能更好,但用户内容必须编译才能用,失去运行时灵活性。
- 运行时 + 编译时:兼顾灵活性与优化空间,是 Vue.js 3 的选择;其性能甚至不输纯编译时的框架。
- Svelte 是纯编译时框架的典型代表,但它的真实性能可能达不到理论高度。
记住这条权衡:「能不能分析用户内容」决定了优化上限,「要不要编译才能用」决定了灵活性下限。
相关
- 1.3 虚拟 DOM 的性能到底如何:编译器带来的优化最终作用在虚拟 DOM 的更新上。