vue3使用h函數(shù)封裝組件和$attrs和props的區(qū)別詳解
二次封裝組件需要考慮的3個(gè)重要的問題
1.props 如何進(jìn)行傳遞
2.插槽如何穿透
3.暴露實(shí)例以及實(shí)例中的方法
在vue3中的$attrs的變化
- vue3中$listeners已被刪除合并到$attrs中。
- vue3的$attrs現(xiàn)在包括class和style屬性。
- vue2中不包含class和style屬性。
也就是說:當(dāng)子組件寫上 v-bind="$attrs"
父組件就可以使用子組件的內(nèi)置事件和內(nèi)置屬性了。
下面我們會(huì)詳細(xì)說一下$attrs
props 如何進(jìn)行傳遞屬性和事件
我們可以在子組件中使用 v-bind="$attrs"
這樣可以把父組件中的屬性傳遞給子組件了
// 子組件
<template>
<div>
<!-- v-bind="$attrs" 可以接收到父組件中的屬性設(shè)置 -->
<el-input v-bind="$attrs"></el-input>
</div>
</template>
// 父組件
<template>
<div>
<MyInput class="set-width" placeholder="請輸入名稱" clearable v-model="name" @blur="clearHandler"></MyInput>
</div>
</template>
<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
let name = ref('')
const clearHandler = () => {
console.log('失去焦點(diǎn)啦')
name.value += 'copy'
}
</script>
<style lang="scss" scoped>
.set-width {
margin: 100px;
width: 300px;
}
</style>


如何解決寫組件時(shí)沒有屬性提示的問題
我們發(fā)現(xiàn)一個(gè)問題:在父組件中的組件寫相關(guān)屬性時(shí),沒有屬性提示。
// 子組件
<template>
<div>
<!-- v-bind="props" 現(xiàn)在我們的屬性肯定是 element-plus 的內(nèi)置屬性了 -->
<el-input v-bind="props"></el-input>
</div>
</template>
<script setup lang="ts">
// 引入 input 的所有屬性
import { type InputProps} from 'element-plus'
// 定義 props, Partial將必填屬性變成可選屬性
const props = defineProps<Partial<InputProps>>()
</script>
這樣父組件在使用的時(shí)候,就可以看到屬性提示了。
插槽如何封裝1: 通過 template 來封裝插槽
<template>
<div>
<el-input v-bind="props">
<!-- 插槽 -->
<template v-for="(_, slotName) in $slots" #[slotName]>
<slot :name="slotName"></slot>
</template>
</el-input>
</div>
</template>
<script setup lang="ts">
// 引入 input 的所有屬性
import { type InputProps} from 'element-plus'
// 定義 props, Partial將必填屬性變成可選屬性
const props = defineProps<Partial<InputProps>>()
插槽如何封裝2: 通過h函數(shù)來處理插槽
我們使用h函數(shù)來進(jìn)行封裝。
h函數(shù)如果第1個(gè)參數(shù)如果是組件,那么第三個(gè)參數(shù)就是插槽
<template>
<div>
<!-- 我們使用h函數(shù)來進(jìn)行封裝,h函數(shù)如果第1個(gè)參數(shù)如果是組件,那么第三個(gè)參數(shù)就是插槽 -->
<component :is="h(ElInput, {...$attrs,...props}, $slots)"></component>
</div>
</template>
<script setup lang="ts">
import { h } from 'vue'
// 引入 input 的所有屬性
import { type InputProps, ElInput} from 'element-plus'
// 定義 props, Partial將必填屬性變成可選屬性
const props = defineProps<Partial<InputProps>>()
</script>
// 父組件
<template>
<div>
<MyInput class="set-width" placeholder="請q輸入內(nèi)容">
<!-- 在組件中使用插槽 -->
<template #prepend>
<el-select v-model="select" placeholder="Select" style="width: 115px">
<el-option label="Restaurant" value="1" />
<el-option label="Order No." value="2" />
<el-option label="Tel" value="3" />
</el-select>
</template>
<template #append>.com</template>
</MyInput>
</div>
</template>
<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
const select = ref('1')
</script>
<style lang="scss" scoped>
.set-width {
margin: 100px;
width: 300px;
}
</style>

暴露實(shí)例以及實(shí)例中的方法
我們可以通過 defineExpose 來暴露實(shí)例以及方法【常用的】
也可以通過vm.exposed來進(jìn)行暴露實(shí)例以及方法
需要注意組件最初設(shè)置了v-if=false這種情況
// 子組件
<template>
<div>
<!-- 我們使用h函數(shù)來進(jìn)行封裝,h函數(shù)如果第1個(gè)參數(shù)如果是組件,那么第三個(gè)參數(shù)就是插槽 -->
<component :is="h(ElInput, {...$attrs,...props, ref: nodeRef}, $slots)"></component>
</div>
</template>
<script setup lang="ts">
import { h, getCurrentInstance } from 'vue'
// 引入 input 的所有屬性
import { type InputProps, ElInput} from 'element-plus'
// 定義 props, Partial將必填屬性變成可選屬性
const props = defineProps<Partial<InputProps>>()
// 獲取當(dāng)前組件實(shí)例
const vm = getCurrentInstance()
// ref可以是一個(gè)字符串,也可以是一個(gè)函數(shù)。這樣父組件就可以通過ref訪問這個(gè)組件的實(shí)例了
function nodeRef(inputInstance) {
// 現(xiàn)在我們把子組件實(shí)例給他,當(dāng)組件使用了v-if=false的時(shí)候,inputInstance為null
// 這里我們是把實(shí)例(實(shí)例中包含方法)暴露出去
vm.exposed= inputInstance || {}
// 代理對象也要做同步的更改
vm.exposeProxy = inputInstance || {}
}
</script>
// 父組件
<template>
<div>
<MyInput class="set-width" v-model="msg" ref="NodeInputRef" placeholder="請輸入內(nèi)容" @blur="clearHandler">
<!-- 在組件中使用插槽 -->
<template #prepend>
<el-select v-model="select" placeholder="Select" style="width: 115px">
<el-option label="Restaurant" value="1" />
<el-option label="Order No." value="2" />
<el-option label="Tel" value="3" />
</el-select>
</template>
<template #append>.com</template>
</MyInput>
<el-button @click="getHandler">清空值</el-button>
</div>
</template>
<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
const select = ref('1')
const msg = ref('放假快樂')
const NodeInputRef = ref(null)
// 獲取實(shí)例中的方法
const getHandler = () => {
NodeInputRef.value?.clear()
}
const clearHandler = () => {
console.log('失去焦點(diǎn)啦')
}
</script>


另外一種暴露方式
常見的暴露方式
defineProps({
name:xxx,
age:xxx,
})
等價(jià)與下面這一種
vm.exposed= {
name:xxx,
age:xxx,
}
vue3 中的 props
props 是組件的自定義屬性,用于從父組件向子組件傳遞數(shù)據(jù)。
props 不會(huì)包含繼承的屬性(如 class 和 style),除非顯式聲明。
vue3 中的 $attrs
vu3中$attrs: 包含了所有[傳遞]給[子組件]的非 props 屬性。如:繼承的屬性(如 class 和 style)以及未在 props 中聲明的屬性。
vue3中的$attrs: 包含 style和class。$attrs包含著數(shù)據(jù)和事件。
vue3 $listeners已被刪除合并到$attrs中。
在vue2中的$attrs
vu2中$attrs: 包含了所有[傳遞]給[子組件]的非 props 屬性和style和class之外的屬性。
vue2中的$attrs: 不包含 style和class
下面是詳細(xì)的講解:
- 在V ue2 中,attrs里面包含著上層組件傳遞的所有數(shù)據(jù)(除style和class)
- 當(dāng)一個(gè)組件聲明了prop時(shí)候,attrs里面包含除去prop里面的數(shù)據(jù)剩下的數(shù)據(jù)。
- 結(jié)合inheritAttrs:false,可以將傳遞下來的數(shù)據(jù)應(yīng)用于其他元素,而不是根元素。
h函數(shù)封裝上面的組件
有些的小伙伴說:我們是否可以使用h函數(shù)去封裝上面的組件呢?
<script lang="ts">
import { defineComponent, h, getCurrentInstance } from 'vue'
import { type InputProps, ElInput } from 'element-plus'
export default {
// 組件名稱
name: 'MyInput',
inheritAttrs: false,
setup(props, { attrs, slots }) {
console.log('attrs', attrs)
// attrs:除去props中聲明的屬性。包含屬性和事件
const vm = getCurrentInstance()
function nodeRef(inputInstance: any) {
vm.exposed = inputInstance || {}
vm.exposeProxy = inputInstance || {}
}
return () => h(ElInput, {
...attrs,
...props,
ref: nodeRef
}, slots)
}
}
<template>
<div>
<MyInput class="set-width" placeholder="請輸入名稱" clearable v-model="name" @blur="clearHandler"></MyInput>
</div>
</template>
<script setup lang="ts">
import MyInput from '@/components/MyInput.vue'
import { ref } from 'vue';
let name = ref('')
const clearHandler = () => {
console.log('失去焦點(diǎn)啦')
name.value += 'copy'
}
</script>
<style lang="scss" scoped>
.set-width {
margin: 100px;
width: 300px;
}
</style>

到此這篇關(guān)于vue3使用h函數(shù)封裝組件和$attrs和props的區(qū)別詳解的文章就介紹到這了,更多相關(guān)vue3封裝組件內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Element-ui Drawer抽屜按需引入基礎(chǔ)使用
這篇文章主要為大家介紹了Element-ui Drawer抽屜按需引入基礎(chǔ)使用,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07
詳解Vue數(shù)據(jù)驅(qū)動(dòng)原理
這篇文章主要介紹了詳解Vue數(shù)據(jù)驅(qū)動(dòng)原理的相關(guān)資料,幫助大家更好的理解和學(xué)習(xí)vue框架的相關(guān)知識(shí),感興趣的朋友可以了解下2020-11-11
vue.js父子組件傳參的原理與實(shí)現(xiàn)方法
這篇文章主要介紹了vue.js父子組件傳參的原理與實(shí)現(xiàn)方法,結(jié)合實(shí)例形式分析了vue.js父子組件傳參的基本原理、實(shí)現(xiàn)方法與相關(guān)操作注意事項(xiàng),需要的朋友可以參考下2023-04-04
使用Vue實(shí)現(xiàn)簡單的信號(hào)和電池電量組件
這篇文章主要為大家詳細(xì)介紹了如何使用Vue實(shí)現(xiàn)簡單的信號(hào)和電池電量組件效果,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2025-04-04
vue組件中傳值EventBus的使用及注意事項(xiàng)說明
這篇文章主要介紹了vue組件中傳值EventBus的使用及注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-11-11
vue使用vue-json-viewer展示JSON數(shù)據(jù)的詳細(xì)步驟
最近在開發(fā)一個(gè)公司的投放管理系統(tǒng)的操作日志模塊,要查看某條操作日志的請求參數(shù),要將請求的參數(shù)以JSON格式的形式展示出來,下面這篇文章主要給大家介紹了vue使用vue-json-viewer展示JSON數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下2022-09-09
vite項(xiàng)目的根目錄中的env.d.ts類型聲明文件里要寫什么
這篇文章主要介紹了vite項(xiàng)目的根目錄中的env.d.ts類型聲明文件里要寫什么,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-08-08

