- 什么是运行时?
- 什么是编译时?
- 什么是运行时编译?
看了很多的文章,大家说的很全面,但是又感觉不是很清楚,终于把我给干懵了,不知道有没有同样被干懵的同学,今天就让我用能看懂的方式,也就是大白话,给大家解释下这几个概念。同时也解答几个疑惑点。
运行时
就是能直接在浏览器运行的代码,或者说浏览器能直接认识的代码。
比如说纯原生代码:
<body>
<button>+</button>
</body>
</html>
<script>
let num = 0;
document.querySelector('button').onclick = function(){
num += 1
console.log(num)
}
</script>
再比如说通过虚拟dom转换成dom对象:
const obj = {
tag: "div",
children: [
{
tag: "span",
children: "hello world!",
},
],
};
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) {
obj.children.forEach((child) => Render(child, el));
}
root.appendChild(el);
}
Render(obj, document.body);
注意,这不叫编译,这叫渲染,其实不管是React,还是Vue,底层都是这么渲染的。
编译时
就是代码本身无法运行,浏览器不认识,需要经过一次编译,才能运行。
比如svelte代码,.svelte文件是浏览器完全不认识的。如下代码,{count}、on:click浏览器并不认识,必须经过编译才能在浏览器运行。
<script>
let count = 0;
function handleClick() {
count += 1;
}
</script>
<button on:click={handleClick}>
Clicked {count} {count === 1 ? 'time' : 'times'}
</button>
再比如.vue文件,浏览器也不认识,所以也需要编译。
<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>
<template>
<button @click="count++">Count is: {{ count }}</button>
</template>
<style scoped>
button {
font-weight: bold;
}
</style>
既然.vue需要编译,那vue就是编译时啊,我也是在这里有点懵,但是其实并不是。
运行时编译
我们来看vue的完整版,通过CDN使用vue。
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
<div id="app">{{ message }}</div>
<script>
const { createApp, ref } = Vue
createApp({
setup() {
const message = ref('Hello vue!')
return {
message
}
}
}).mount('#app')
</script>
其实div#app并不是真的dom对象,因为{{message}}这个变量浏览器是不认识的,在运行时,是通过vue底层的compiler函数,进行了编译,编译为虚拟dom,再进行渲染的。所以vue是运行时编译。
疑惑点
问题一:那react是运行时编译吗?
不是,我们来看react的完整版,通过CDN使用react。
<!-- react CDN引入 -->
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<!-- babel引入,用于处理jsx -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<div id="root"></div>
<script type="text/babel">
const root = ReactDOM.createRoot(document.querySelector('#root'))
// 1.定义元素内的文本
let message = "Hello World"
// 3.封装渲染函数
function RootRender() {
root.render((<h2>{message}</h2>))
}
RootRender()
</script>
我们可以看到,react是直接渲染的,message这个变量浏览器不认识,react在渲染时仅仅完成了变量替换而已,都是浏览器认识的代码。所以react不是运行时编译,而是运行时。
问题二:既然是运行时编译,打包为什么每次都要编译.vue文件。
没错,我们无论用的是webpack也好,vite也好,都会编译.vue文件,这是为什么呢?因为框架版vue其实是运行时,我们随便找一个vue项目看看打包后的产物。
我们看到会有一个runtime.js文件,当然根据业务,展现形式有所差异.runtime.js文件是没有编译能力的,因为.vue文件的代码在打包时已经编译完成了,这里只需要渲染。
这是vue为了性能,去掉了编译相关的代码,如果想加回来,有个runtimeCompiler属性,设置一下就好了。
问题三:是不是运行时编译就是最好的方案?
不是,各个方案各有优点。
- 运行时:浏览器可以运行;运行速度较快;编写时比较复杂。
- 编译时:浏览器无法运行,需要通过编译;运行速度较快;编写比较方便。
- 运行时编译:浏览器可以运行;但是运行时需要进行编译,耗费一定的时间,运行速度较慢;但是比较灵活,而且编写也比较方便。
所以各个框架有自己的特性和取舍,所以不要以此差异论长短。