为什么Vue是运行时编译?

494 阅读3分钟
  • 什么是运行时?
  • 什么是编译时?
  • 什么是运行时编译?

看了很多的文章,大家说的很全面,但是又感觉不是很清楚,终于把我给干懵了,不知道有没有同样被干懵的同学,今天就让我用能看懂的方式,也就是大白话,给大家解释下这几个概念。同时也解答几个疑惑点。

运行时

就是能直接在浏览器运行的代码,或者说浏览器能直接认识的代码。

比如说纯原生代码:

<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项目看看打包后的产物。

image.png

我们看到会有一个runtime.js文件,当然根据业务,展现形式有所差异.runtime.js文件是没有编译能力的,因为.vue文件的代码在打包时已经编译完成了,这里只需要渲染。

这是vue为了性能,去掉了编译相关的代码,如果想加回来,有个runtimeCompiler属性,设置一下就好了。

问题三:是不是运行时编译就是最好的方案?

不是,各个方案各有优点。

  • 运行时:浏览器可以运行;运行速度较快;编写时比较复杂。
  • 编译时:浏览器无法运行,需要通过编译;运行速度较快;编写比较方便。
  • 运行时编译:浏览器可以运行;但是运行时需要进行编译,耗费一定的时间,运行速度较慢;但是比较灵活,而且编写也比较方便。

所以各个框架有自己的特性和取舍,所以不要以此差异论长短。