前端框架的运行时与编译时
本文写于 2022 年 5 月 20 日。
运行时与编译时是前端工程师常常听到的两个词。
比如 Vue 运行时、Angular 运行时、React 运行时……
又比如 svelte 框架,总听到他的宣传说的是“0 运行时”,所以他的工作其实就是在“编译时”了。
这两个词到底是什么意思呢?
虚拟 DOM 的渲染
我们以虚拟 DOM 为例来讲解运行时与编译时。
虚拟 DOM 是 React/Vue 都采用了的一种设计手段,简单来讲它就是将 HTML 的 DOM,用一套 JS 对象来表示。
<div id="app">
<h1>Hello World</h1>
</div>
可以简单的这么表示:
const vDom = {
tag: "div",
id: "app",
children: [
{
tag: "h1",
children: ["Hello World"],
},
],
};
那么我们如何渲染这个对象呢?
可以提供一个 render 函数来做到。
function render(vDom, container) {
const el = document.createElement(vDom.tag);
el.id = vDom.id;
if (!vDom.children || vDom.children.length === 0) {
container.append(el);
return;
}
for (let i = 0; i < vDom.children.length; i++) render(vDom[i], el);
}
我们去遍历这个对象和他的 children
属性以渲染 DOM。(这个写法效率很低,只做示例用)
这就叫做运行时。
我们在代码跑起来之后,才进行的 DOM 渲染。
虚拟 DOM 写起来太麻烦怎么办
接下来问题来了,虚拟 DOM 实在是太难写了。我们这么去写 UI 会写到吐血的。
能不能让我们写普通的 HTML,然后把它变成虚拟 DOM 呢?
当然可以,我们的 Vue 和 React 都提供了这样的工具,所以我们才能愉快的书写 Vue template 和 JSX。
假设这是一个函数名叫 complier
。
const vDom = complier(htmlStr);
render(vDom, document.querySelector("#app"));
这样我们就能衔接上之前写的 render
方法。
直到此时,我们依旧是纯运行时框架。但 complier
函数并没有必要放到运行时呀!
我们可以写一个脚本把 complier
的结果直接硬编码到输出文件中。
// 源文件
<template>
<div>
<!-- ... -->
</div>
</template>
<script>
// do something here...
</script>
// 输出文件
const vDom = {
tag: "div",
children: [
// ...
],
};
render(vDom, app);
在浏览器中我们无须源文件,只需要输出文件,就可以运行我们的 render
函数了。
render 函数也能编译?
既然我们可以把 jsx/Vue template 编译成 vDom,那我们能不能直接把 render
函数也给干掉呢?
要知道 render
现在需要进行递归操作,效率是比较差的。
我们要时能把所有的递归操作都干掉,直接编译成一连串的 DOM 操作,岂不美哉?
没错,这样做是可以的。
如果我们把源代码直接编译成一连串的 DOM 操作,那么我们做的就叫做 “纯编译时”框架。因为在代码运行过程中,我们的框架没有做任何操作。
(完)