Skip to Content

Vue设计与实现:运行时与编译时

当设计一个框架的时候,我们有三种选择:纯运行时的运行时 + 编译时的纯编译时的。这需要你根据目标框架的特征,以及对框架的期望,做出合适的决策。

纯运行时框架

我们先聊聊纯运行时的框架。假设我们设计了一个框架,它提供一个 Render 函数,用户可以为该函数提供一个树型结构的数据对象,然后 Render 函数会根据该对象递归地将数据渲染成 DOM 元素。

数据结构定义

我们规定树型结构的数据对象如下:

const obj = { tag: "div", children: [{ tag: "span", children: "text" }], };

接下来我们来实现 Render 函数:

const Render = (obj, root) => { const el = document.createElement(obj.tag); if (typeof el.children === "string") { const text = document.createTextNode(el.children); el.appendChild(text); } else if (el.children) { obj.children.forEach((child) => Render(child, el)); } root.appendChild(el); };

接下来我们结合一下:

const obj = { tag: "div", children: [{ tag: "span", children: "text" }], }; const Render = (obj, root) => { const el = document.createElement(obj.tag); if (typeof el.children === "string") { const text = document.createTextNode(el.children); el.appendChild(text); } else if (el.children) { obj.children.forEach((child) => Render(child, el)); } root.appendChild(el); }; Render(obj, document.body);

在浏览器中运行上面这段代码,就可以看到我们预期的内容。这就是纯运行时的框架

运行时 + 编译时框架

直到有一天,有一位小伙伴提出:“手写树型结构的数据对象太麻烦了,而且不直观,能不能支持用类似于 HTML 标签的方式描述树型结构的数据对象呢?”

这时候就会开始思考,我能不能引入编译的手段,把 HTML 自动编译成树形结构的对象。

Compiler 程序的引入

为此你编写了一个叫做 Compiler 的程序。它的作用就是编译 HTML 字符串。最简单的使用方式就是让用户分别去调用 CompilerRender

const html = `<div>text</div>`; const obj = Compiler(html); Render(obj, document.body);

上面这段代码能够很好地工作,这时我们的框架就变成了一个运行时 + 编译时的框架。它既支持运行 时,用户可以直接提供数据对象从而无须编译;又支持编译时,用户可以提供 HTML 字符串,我们将其 编译为数据对象后再交给运行时处理。准确地说,上面的代码其实是运行时编译,意思是代码运行的时候 才开始编译,而这会产生一定的性能开销,因此我们也可以在构建的时候就执行 Compiler 程序将用户 提供的内容编译好,等到运行时就无须编译了,这对性能是非常友好的。

纯编译时框架

不过,聪明的你一定意识到了另外一个问题:既然编译器可以把 HTML 字符串编译成数据对象,那么能不能直接编译成命令式代码呢?

直接编译为命令式代码

// HTML 模板 <div> <span>text</span> </div> // 编译成命令式代码 const div = document.createElement('div') const span = document.createElement('span') span.innerText = 'text' div.appendChild(span) document.body.appendChild(div)

这样我们只需要一个 Compiler 函数就可以了,连 Render 都不需要了。其实这就变成了一个纯编译时的框架,因为我们不支持任何运行时内容,用户的代码通过编译器编译后才能运行。

三种框架设计的对比

通过以上分析,我们可以总结出三种框架设计方案的特点和差异:

框架类型工作方式性能表现灵活性包体积典型代表
纯运行时运行时解析和渲染较低很高较大早期jQuery
运行时 + 编译时编译时优化 + 运行时处理中等中等Vue.js, React
纯编译时编译时完全处理很高较低很小Svelte
Last updated on