首先我们需要先理解一下什么事编译时和运行时
在语言层面,先来聊一下前端开发者最常遇见的两种语言JavaScript
和Java
Java
的代码就是被编译为.class
文件才能运行,这个编译过程就是编译时,运行.class
文件就是运行时- 我们在浏览器直接输入一些
JavaScript
代码,就可以执行,并没有经过编译成某个文件。所以我的理解是解释型语言是没有编译时,只有运行时。
JavaScript的编译时和运行时
JavaScript的编译时或运行时,一般指的是现代框架(react
、vue
)是采用那种思路来实现的。
运行时
一个纯运行时框架就是在我们将DOM的数据结构传入到渲染函数时,就可以直接渲染出真实的DOM结构元素。在我们的项目开发中,假设提供一个被叫做render
的API
,通过给该render函数传入源数据并调用,就可以得到真实的DOM结构,那么我们可以认为该render函数就是一个运行时。
现在有以下数据需要生成dom并挂载到body上
// node是一个虚拟DOM树
// tag为元素的标签
// children为数组时表示子元素,为字符串时表示文本
const node = {
tag: 'div',
children: [
tag: 'h1',
children: '运行时'
]
}
现在需要一个render函数来处理上面的数据
const render = (node, root) => {
const el = document.createElement(node.tag);
if (typeof node.children === 'string') {
// 如果子元素是文本节点,则创建文本节点。
const text = document.createTextNode(node.children)
el.appendChild(text)
} else if (Array.isArray(node.children)) {
node.children.forEach(child => {
render(child, el)
});
}
root.appendChild(el);
};
有了render函数,我们就可以调用并传参,来实现dom的渲染
const div = document.createElement('div')
render(node, div)
console.log(div)
// 最后打印的结果
/*
<div>
<div>
<h1>运行时</h1>
</div>
</div>
*/
观察上面的代码,其实在Vue
的框架中已经提供了类似的API供我们使用,可以使你利用JavaScript
的丰富表达力来完全编程式地声明组件最终的渲染输出。不过为什么我们在使用单文件组件的时候,大多都使用模版语法,这是因为较运行时的render
函数的数据,模板语法更直观、书写更方便、语义化更强。
编译时
在Vue
项目的单文件组件中,我们一般使用的是template
模版语法,例如
<div>
<h1>编译时</h1>
</div>
现在要想将以上模板代码编译成正在的dom
结构,就需要先解析上面的模版结构,这里就需要实现一个compiler
方法去解析代码,关于compiler
的实现稍微较为复杂,其原理可以参考编译器核心原理这篇文章
运行+编译时
现在有了运行时、编译时的简单示例,那么上面的代码可以这样使用:
const html =
`
<div>
<h1>编译时 + 运行时</h1>
</div>
`
// 编译,先将模板字符串编译成虚拟DOM树状结构
const compilerData = compiler(html);
// 在使用render函数生成真正的dom结构并挂载
render(compilerData, document.body)
上面就变成了一个简单的编译时+运行时的框架。它既支持运行时,用户可以直接提供数据对象从而无须编译;又支持编译时,用户可以提供 HTML
字符串,我们将其编译为数据对象后再交给运行时处理。
如果上面的代码稍微做一下修改,将render
的工作直接放到compiler
里面去实现,这样我们就省去了调用render
的步骤,这样就变成了一个纯粹的编译时框架,我们不需要提供任何数据,只需要提供模版代码就行。
总结
现在我们已经大概了解了什么是运行时
、编译时
、编译+运行时
、纯编译时等框架的区别,其实在我们使用的现代框架中Vue就是一个编译+运行时
的框架。由于它没有编译的过程,因此我们没办法分析用户提供的内容,但是如果加入编译步骤,可能就大不一样了,我们可以分析用户提供的内容,看看哪些内容未来可能会改变,哪些内容永远不会改变,这样我们就可以在编译的时候提取这些信息,然后将其传递给Render
函数,Render
函数得到这些信息之后,就可以做进一步的优化了。然而,假如我们设计的框架是纯编译时的,那么它也可以分析用户提供的内容。由于不需要任何运行时,而是直接编译成可执行的JavaScript
代码,因此性能可能会更好,但是这种做法有损灵活性,即用户提供的内容必须编译后才能用。
另外,Vue
的文件名中带有runtime
的Vue
构建版本未包含模板编译器,例如 vue.runtime.esm-bundler.js