前言
從vue3
開始vue
引入了宏,比如defineProps
、defineEmits
等。我們每天寫vue
代碼時都會使用到這些宏,但是你有沒有思考過vue
中的宏到底是什么?為什么這些宏不需要手動從vue
中import
?為什么只能在setup
頂層中使用這些宏?
vue 文件如何渲染到瀏覽器上
要回答上面的問題,我們先來了解一下從一個vue
文件到渲染到瀏覽器這一過程經(jīng)歷了什么?
我們的vue
代碼一般都是寫在后綴名為vue的文件上,顯然瀏覽器是不認(rèn)識vue文件的,瀏覽器只認(rèn)識html、css、jss等文件。所以第一步就是通過webpack
或者vite
將一個vue文件編譯為一個包含render
函數(shù)的js
文件。然后執(zhí)行render
函數(shù)生成虛擬DOM,再調(diào)用瀏覽器的DOM API
根據(jù)虛擬DOM生成真實(shí)DOM掛載到瀏覽器上。
vue3的宏是什么?
我們先來看看vue
官方的解釋:
宏是一種特殊的代碼,由編譯器處理并轉(zhuǎn)換為其他東西。它們實(shí)際上是一種更巧妙的字符串替換形式。
宏是在哪個階段運(yùn)行?
通過前面我們知道了vue
文件渲染到瀏覽器上主要經(jīng)歷了兩個階段。
第一階段是編譯時,也就是從一個vue
文件經(jīng)過webpack
或者vite
編譯變成包含render函數(shù)的js文件。此時的運(yùn)行環(huán)境是nodejs
環(huán)境,所以這個階段可以調(diào)用nodejs
相關(guān)的api
,但是沒有在瀏覽器環(huán)境內(nèi)執(zhí)行,所以不能調(diào)用瀏覽器的API
。
第二階段是運(yùn)行時,此時瀏覽器會執(zhí)行js
文件中的render
函數(shù),然后依次生成虛擬DOM
和真實(shí)DOM
。此時的運(yùn)行環(huán)境是瀏覽器環(huán)境內(nèi),所以可以調(diào)用瀏覽器的API,但是在這一階段中是不能調(diào)用nodejs
相關(guān)的api
。
而宏就是作用于編譯時,也就是從vue文件編譯為js文件這一過程。
舉個defineProps
的例子:在編譯時defineProps
宏就會被轉(zhuǎn)換為定義props
相關(guān)的代碼,當(dāng)在瀏覽器運(yùn)行時自然也就沒有了defineProps
宏相關(guān)的代碼了。所以才說宏是在編譯時執(zhí)行的代碼,而不是運(yùn)行時執(zhí)行的代碼。
一個defineProps宏的例子
我們來看一個實(shí)際的例子,下面這個是我們的源代碼:
<template>
<div>content is {{ content }}</div>
<div>title is {{ title }}</div>
</template>
<script setup lang="ts">
import {ref} from "vue"
const props = defineProps({
content: String,
});
const title = ref("title")
</script>
在這個例子中我們使用defineProps
宏定義了一個類型為String
,屬性名為content
的props
,并且在template
中渲染content
的內(nèi)容。
我們接下來再看看編譯成js
文件后的代碼,代碼我已經(jīng)進(jìn)行過簡化:
import { defineComponent as _defineComponent } from "vue";
import { ref } from "vue";
const __sfc__ = _defineComponent({
props: {
content: String,
},
setup(__props) {
const props = __props;
const title = ref("title");
const __returned__ = { props, title };
return __returned__;
},
});
import {
toDisplayString as _toDisplayString,
createElementVNode as _createElementVNode,
Fragment as _Fragment,
openBlock as _openBlock,
createElementBlock as _createElementBlock,
} from "vue";
function render(_ctx, _cache, $props, $setup) {
return (
_openBlock(),
_createElementBlock(
_Fragment,
null,
[
_createElementVNode(
"div",
null,
"content is " + _toDisplayString($props.content),
1 /* TEXT */
),
_createElementVNode(
"div",
null,
"title is " + _toDisplayString($setup.title),
1 /* TEXT */
),
],
64 /* STABLE_FRAGMENT */
)
);
}
__sfc__.render = render;
export default __sfc__;
我們可以看到編譯后的js
文件主要由兩部分組成,第一部分為執(zhí)行defineComponent
函數(shù)生成一個 __sfc__
對象,第二部分為一個render
函數(shù)。render
函數(shù)不是我們這篇文章要講的,我們主要來看看這個__sfc__
對象。
看到defineComponent
是不是覺得很眼熟,沒錯這個就是vue
提供的API中的 definecomponent函數(shù)。這個函數(shù)在運(yùn)行時沒有任何操作,僅用于提供類型推導(dǎo)。這個函數(shù)接收的第一個參數(shù)就是組件選項(xiàng)對象,返回值就是該組件本身。所以這個__sfc__
對象就是我們的vue
文件中的script
代碼經(jīng)過編譯后生成的對象,后面再通過__sfc__.render = render
將render
函數(shù)賦值到組件對象的render
方法上面。
我們這里的組件選項(xiàng)對象經(jīng)過編譯后只有兩個了,分別是props
屬性和setup
方法。明顯可以發(fā)現(xiàn)我們原本在setup
里面使用的defineProps
宏相關(guān)的代碼不在了,并且多了一個props
屬性。沒錯這個props
屬性就是我們的defineProps
宏生成的。
我們再來看一個不在setup
頂層調(diào)用defineProps
的例子:
<script setup lang="ts">
import {ref} from "vue"
const title = ref("title")
if (title.value) {
const props = defineProps({
content: String,
});
}
</script>
運(yùn)行這個例子會報(bào)錯:defineProps is not defined
我們來看看編譯后的js代碼:
import { defineComponent as _defineComponent } from "vue";
import { ref } from "vue";
const __sfc__ = _defineComponent({
setup(__props) {
const title = ref("title");
if (title.value) {
const props = defineProps({
content: String,
});
}
const __returned__ = { title };
return __returned__;
},
});
明顯可以看到由于我們沒有在setup
的頂層調(diào)用defineProps
宏,在編譯時就不會將defineProps
宏替換為定義props
相關(guān)的代碼,而是原封不動的輸出回來。在運(yùn)行時執(zhí)行到這行代碼后,由于我們沒有任何地方定義了defineProps
函數(shù),所以就會報(bào)錯defineProps is not defined
。
總結(jié)
現(xiàn)在我們能夠回答前面提的三個問題了。
-
vue
中的宏到底是什么?vue3
的宏是一種特殊的代碼,在編譯時會將這些特殊的代碼轉(zhuǎn)換為瀏覽器能夠直接運(yùn)行的指定代碼,根據(jù)宏的功能不同,轉(zhuǎn)換后的代碼也不同。 -
為什么這些宏不需要手動從
vue
中import
?因?yàn)樵诰幾g時已經(jīng)將這些宏替換為指定的瀏覽器能夠直接運(yùn)行的代碼,在運(yùn)行時已經(jīng)不存在這些宏相關(guān)的代碼,自然不需要從
vue
中import
。 -
為什么只能在
setup
頂層中使用這些宏?因?yàn)樵诰幾g時只會去處理
setup
頂層的宏,其他地方的宏會原封不動的輸出回來。在運(yùn)行時由于我們沒有在任何地方定義這些宏,當(dāng)代碼執(zhí)行到宏的時候當(dāng)然就會報(bào)錯。
如果想要在vue
中使用更多的宏,可以使用 vue macros。這個庫是用于在vue中探索更多的宏和語法糖,作者是vue的團(tuán)隊(duì)成員 三咲智子 。文章來源:http://www.zghlxwxcb.cn/news/detail-827045.html
如果我的文章對你有點(diǎn)幫助,歡迎關(guān)注公眾號:【歐陽碼農(nóng)】,文章在公眾號首發(fā)。你的支持就是我創(chuàng)作的最大動力,感謝感謝!文章來源地址http://www.zghlxwxcb.cn/news/detail-827045.html
到了這里,關(guān)于vue3的宏到底是什么東西?的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!