运行时与编译时
在设计一个框架的时候,我们有三个选择:
纯运行时
、运行时+编译时
、纯编译时
。所以我们需要很准确的了解到这三个不同选择的概念以及作用。
1.运行时
我们首先了解一下传纯运行时的框架。
假设我们创建了一个开个你家,它提供了一个 Render 函数,用户只需要给这个函数传递一个树形结构的对象,然后 Render 函数会根据这个对象递归地将数据渲染成 DOM 元素
树形结构数据如下:
const obj = {
tag: "div",
children: [{ tag: "span", children: "hello coder_MX" }],
};
Render 函数如下
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
root.appendChild(el);
}
运行时执行
const obj = {
tag: "div",
children: [{ tag: "span", children: "hello coder_MX" }],
};
// 渲染到 body 下
Render(obj, document.body);
在浏览器控制台执行上述代码,就能直接看到我们预期的内容了
结论(纯运行时)
到现在,你应该已经大致明白了何为纯运行时
了,就是用户在使用渲染页面的时候,直接给 Render 函数传的就是树形结构的 DOM 元素对象,其他无需要做任何事情。
那么,用户手写树形结构对象真的是一件简单的事情吗?于是我们引出了编译时+运行时
的概念
2.编译时+运行时
接着上面运行时的问题讨论,一个框架的存在,不仅仅是为了降低渲染的消耗,更重要的是要降低开发者的
心智负担
,这样用才是符合用户的框架,接下来我们详细讨论一下编译时
为了满足用户的需求,我们要想到能不能引入编译的手段,可以直接把 HTML 标签编译成树型结构的数据对象,这样用户便编写的 HTML 也可以使用 Render 函数进行渲染啦!
用户编写的 HTML 代码
<div>
<span>hello coder_M</span>
</div>
经过编译处理,我们得到如下的树形结构对象
const obj = {
tag: "div",
children: [{ tag: "span", children: "hello coder_MX" }],
};
再接下来,Render 函数就可以直接享用上述的结构数据了
所以为了完成上述的逻辑,我们需要开发一个compiler
的程序,它的作用就是将 HTML 代码编译成树形结构的对象。如下:
const html = `
<div>
<span>hello coder_MX</span>
</div>
`;
// 调用 Compiler 编译得到树型结构的数据对象
const obj = Compiler(html);
// 再调用 Render 进行渲染
Render(obj, document.body);
结论(运行时+编译时)
上述代码的顺利执行,这时候,我们的框架就变成了一个 运行时+编译时的框架。它既支持运行时(用户直接使用树形结构的对象进行渲染),又支持编译时(将用户编写的 HTML 代码编译成树形结构数据,传递给 Render 函数)。
但是更准确的讲,上述代码属于运行时编译
,意思时,代码运行的时候才进行了编译处理,而这样一定会产生一些性能的开销,因此我们可以在构建的时候就执行Compiler
,当用户需要渲染 DOM 的时候,就无需编译了,这对性能来说时非常友好的。
3.纯编译时
屏幕面前的你一定会想到,既然Compiler
程序可以将 HTML 代码编译成声明式框架
需要的树形结构 DOM,那为什么不直接编译成命令式
的代码呢?当然可以,如下
<div>
<span>hello coder_MX</span>
</div>
经过
Compiler
程序编译,得到如下代码:
const div = document.creatElement('div')
const span = document.creatElement('span')
span.innerText = 'hello coder_MX'
div.appendChild(sapn)
document.body.appednChild(div)
结论(纯编译时)
这样,我们仅仅需要一个 Compiler
函数就可以了,根本无需Render
函数,我们就得到了一个纯编译时的框架了,因为我们不支持任何运行时内容,只有用户进行了编译,我们就能立马渲染。
全文总结
纯运行时框架
:由于它没有编译的过程,因此我们没办法分析用户提供的内容。
运行时+编译时
:可以分析用户提供的内容,看看哪些内容未来可能会改变,哪些内容永远不会改变,这样我们就可以在编译的时候提取这些信息,然后将其传递给 Render 函数,Render 函数得到这些信息之后,就可以做进一步的优化
纯编译时
:也可以分析用户提供的内容。由于不需要任何运行时,而是直接编译成可执行的 JavaScript 代码,因此性能可能会更好,但是这种做法有损灵活性,即用户提供的内容必须编译后才能用
##拓展
运行时+编译时的框架:
Vue、Angular
纯编译时
Svelte(真实性能可能达不到理论高度)
感谢阅读~