前言 眾所周知,當子組件使用setup後,父組件就不能像vue2那樣直接就可以訪問子組件內的屬性和方法。這個時候就需要在子組件內使用defineExpose巨集函數來指定想要暴露出去的屬性和方法。這篇文章來講講defineExpose巨集函數是如何暴露出去這些屬性和方法給父組件使用。註:本文中使用的vu ...
前言
眾所周知,當子組件使用setup後,父組件就不能像vue2那樣直接就可以訪問子組件內的屬性和方法。這個時候就需要在子組件內使用defineExpose
巨集函數來指定想要暴露出去的屬性和方法。這篇文章來講講defineExpose
巨集函數是如何暴露出去這些屬性和方法給父組件使用。註:本文中使用的vue版本為3.4.19
。
關註公眾號:【前端歐陽】,給自己一個進階vue的機會
加我微信heavenyjj0012回覆「666」,免費領取歐陽研究vue源碼過程中收集的源碼資料,歐陽寫文章有時也會參考這些資料。同時讓你的朋友圈多一位對vue有深入理解的人。
看個demo
父組件index.vue
的代碼如下:
<template>
<ChildDemo ref="child" />
<button @click="handleClick">調用子組件的validate方法</button>
</template>
<script setup lang="ts">
import ChildDemo from "./child.vue";
import { ref } from "vue";
const child = ref();
function handleClick() {
console.log(child.value.validate);
child.value.validate?.();
}
</script>
上面的代碼很簡單,通過ref
拿到子組件的實例賦值給child
變數。然後在按鈕的click事件中列印出子組件的validate
方法和執行validate
方法。
再來看看子組件child.vue
不使用defineExpose
巨集的例子,代碼如下:
<template></template>
<script setup>
function validate() {
console.log("執行子組件validate方法");
}
</script>
在瀏覽器中點擊父組件的button按鈕,可以看到控制臺中列印的是undefined
,並且子組件內的validate
方法也沒有執行。因為子組件使用了setup,預設是不會暴露setup中定義的屬性和方法。如下圖:
我們再來看看子組件child.vue
使用defineExpose
巨集的例子,代碼如下:
<template></template>
<script setup>
function validate() {
console.log("執行子組件validate方法");
}
defineExpose({
validate,
});
</script>
在瀏覽器中點擊父組件的button按鈕,可以看到控制臺中列印的不再是undefined
,子組件內的validate
方法也執行了。如下圖:
關註公眾號:【前端歐陽】,給自己一個進階vue的機會
加我微信heavenyjj0012回覆「666」,免費領取歐陽研究vue源碼過程中收集的源碼資料,歐陽寫文章有時也會參考這些資料。同時讓你的朋友圈多一位對vue有深入理解的人。
編譯後的代碼
首先需要在瀏覽器中找到編譯後的使用defineExpose
巨集的child.vue
文件,在network面板中找到child.vue
,然後右鍵點擊Open in Sources panel就可以在source面板中找到編譯後的child.vue
。如下圖:
為了要在瀏覽器中debug,我們還需要在設置中關閉瀏覽器的javascript source map,如下圖:
現在我們來看看編譯後的child.vue
文件,代碼如下:
const _sfc_main = {
__name: "child",
setup(__props, { expose: __expose }) {
function validate() {
console.log("執行子組件validate方法");
}
__expose({
validate,
});
const __returned__ = { validate };
return __returned__;
},
};
function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) {
return null;
}
_sfc_main.render = _sfc_render;
export default _sfc_main;
從上面可以看到_sfc_main
對象中的setup
對應的就是我們源代碼<script setup>
中的內容,並且defineExpose
巨集函數也不在了,變成了一個__expose
方法(defineExpose
巨集函數如何編譯成__expose
方法我們會在下一篇文章講)。如下圖:
expose
方法
給__expose
方法打個斷點,刷新頁面此時斷點停留在__expose
方法上面。點擊step into
進入到__expose
方法內部,如下圖:
進入到__expose
方法內部,我們發現__expose
方法是在一個createSetupContext
函數中定義的。在我們這個場景中createSetupContext
函數簡化後的代碼如下:
function createSetupContext(instance) {
const expose = (exposed) => {
instance.exposed = exposed || {};
};
return Object.freeze({
// ...省略
expose,
});
}
我們先來看看函數中的instance
變數,我想你通過名字應該已經猜到了他就是當前vue實例對象。如下圖:
在vue實例對象中有我們熟悉的data方法、directives和componens屬性等。
在expose
函數內部做的事情也很簡單,將子組件需要暴露的屬性或者方法組成的對象賦值給vue實例上的exposed
屬性。
父組件訪問子組件的validate
方法
在vue3中想要訪問子組件需要使用特殊的 ref
attribute,在我們這個例子中就是使用<ChildDemo ref="child" />
。這樣使用後就可以使用child
變數訪問子組件,其實在這裡child
變數的值就是一個名為getExposeProxy
函數的返回值(後面的文章中會去詳細講解ref
attribute是如何訪問子組件)。
getExposeProxy
函數的代碼如下:
function getExposeProxy(instance) {
if (instance.exposed) {
return (
instance.exposeProxy ||
(instance.exposeProxy = new Proxy(proxyRefs(markRaw(instance.exposed)), {
get(target, key) {
if (key in target) {
return target[key];
} else if (key in publicPropertiesMap) {
return publicPropertiesMap[key](instance);
}
},
has(target, key) {
// ...省略
},
}))
);
}
}
前面我們講過了defineExpose
巨集函數中定義了想要暴露出來的屬性和方法,經過編譯後defineExpose
巨集函數變成了__expose
方法。執行__expose
方法後會將子組件想要暴露的屬性或者方法組成的對象賦值給vue實例上的exposed
屬性,也就是instance.exposed
。
在上面的getExposeProxy
函數中就是返回了instance.exposed
的Proxy
對象,當我們使用child.value.validate
訪問子組件的validate
方法,其實就是訪問的是instance.exposed
對象中的validate
方法,而instance.exposed
中的validate
方法就是defineExpose
巨集函數暴露的validate
方法。如下圖:
總結
父組件想要訪問子組件暴露的validate
方法主要分為下麵四步:
-
子組件使用
defineExpose
巨集函數聲明想要暴露validate
方法。 -
defineExpose
巨集函數經過編譯後變成__expose
方法。 -
執行
__expose
方法將子組件需要暴露的屬性或者方法組成的對象賦值給子組件vue實例上的exposed
屬性,也就是instance.exposed
。 -
父組件使用ref訪問子組件的
validate
方法,也就是訪問child.value.validate
。其實訪問的就是上一步的instance.exposed.validate
方法,最終訪問的就是defineExpose
巨集函數中暴露的validate
方法。
關註公眾號:【前端歐陽】,給自己一個進階vue的機會
加我微信heavenyjj0012回覆「666」,免費領取歐陽研究vue源碼過程中收集的源碼資料,歐陽寫文章有時也會參考這些資料。同時讓你的朋友圈多一位對vue有深入理解的人。