前言
我们每天写vue3
项目的时候都会使用setup
语法糖,但是你有没有思考过下面几个问题。setup
语法糖经过编译后是什么样子的?为什么在setup
顶层定义的变量可以在template
中可以直接使用?为什么import
一个组件后就可以直接使用,无需使用components
选项来显式注册组件?
vue 文件如何渲染到浏览器上
要回答上面的问题,我们先来了解一下从一个vue
文件到渲染到浏览器这一过程经历了什么?
我们的vue
代码一般都是写在后缀名为vue的文件上,显然浏览器是不认识vue文件的,浏览器只认识html、css、jss等文件。所以第一步就是通过webpack
或者vite
将一个vue文件编译为一个包含render
函数的js
文件。然后执行render
函数生成虚拟DOM,再调用浏览器的DOM API
根据虚拟DOM生成真实DOM挂载到浏览器上。
setup
编译后的样子
在javascript
标准中script
标签是不支持setup
属性的,浏览器根本就不认识setup
属性。所以很明显setup
是作用于编译时阶段,也就是从vue文件编译为js文件这一过程。
我们来看一个简单的demo,这个是index.vue
源代码:
<template>
<h1>{
{ title }}</h1>
<h1>{
{ msg }}</h1>
<Child />
</template>
<script lang="ts" setup>
import { ref } from "vue";
import Child from "./child.vue";
const msg = ref("Hello World!");
const title = "title";
if (msg.value) {
const content = "content";
console.log(content);
}
</script>
这里我们定义了一个名为msg
的ref
响应式变量和非响应式的title
变量,还有import
了child.vue
组件。
这个是child.vue
的源代码
<template>
<div>i am child</div>
</template>
我们接下来看index.vue
编译后的样子,代码我已经做过了简化:
import { ref } from "vue";
import Child from "./Child.vue";
const title = "title";
const __sfc__ = {
__name: "index",
setup() {
const msg = ref("Hello World!");
if (msg.value) {
const content = "content";
console.log(content);
}
const __returned__ = { title, msg, Child };
return __returned__;
},
};
import {
toDisplayString as _toDisplayString,
createElementVNode as _createElementVNode,
createVNode as _createVNode,
Fragment as _Fragment,
openBlock as _openBlock,
createElementBlock as _createElementBlock,
} from "vue";
function render(_ctx, _cache, $props, $setup, $data, $options) {
return (
_openBlock(),
_createElementBlock(
_Fragment,
null,
[
_createElementVN