眾所周知,當子組件使用setup后,父組件就不能像vue2那樣直接就可以訪問子組件內的屬性和方法。這個時候就需要在子組件內使用defineExpose宏函數來指定想要暴露出去的屬性和方法。這篇文章來講講defineExpose宏函數是如何暴露出去這些屬性和方法給父組件使用。注:本文中使用的vue版本為3.4.19。
父組件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方法也執行了。如下圖:
圖片
首先需要在瀏覽器中找到編譯后的使用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方法上面。點擊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屬性。
在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方法主要分為下面四步:
點擊下方卡片關注我,給自己一個進階vue的機會。
本文鏈接:http://www.tebozhan.com/showinfo-26-91161-0.html徹底搞清楚Vue3的DefineExpose宏是如何暴露方法給父組件使用
聲明:本網頁內容旨在傳播知識,若有侵權等問題請及時與本網聯系,我們將在第一時間刪除處理。郵件:2376512515@qq.com
上一篇: 從基礎概念到進階思考,完整的遞歸思維學習