vue3使用h函數(shù)封裝組件和$attrs和props的區(qū)別詳解
二次封裝組件需要考慮的3個重要的問題
1.props 如何進(jìn)行傳遞
2.插槽如何穿透
3.暴露實例以及實例中的方法
在vue3中的$attrs的變化
- vue3中$listeners已被刪除合并到$attrs中。
- vue3的$attrs現(xiàn)在包括class和style屬性。
- vue2中不包含class和style屬性。
也就是說:當(dāng)子組件寫上 v-bind="$attrs"
父組件就可以使用子組件的內(nèi)置事件和內(nèi)置屬性了。
下面我們會詳細(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('失去焦點啦')
name.value += 'copy'
}
</script>
<style lang="scss" scoped>
.set-width {
margin: 100px;
width: 300px;
}
</style>


如何解決寫組件時沒有屬性提示的問題
我們發(fā)現(xiàn)一個問題:在父組件中的組件寫相關(guān)屬性時,沒有屬性提示。
// 子組件
<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>
這樣父組件在使用的時候,就可以看到屬性提示了。
插槽如何封裝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個參數(shù)如果是組件,那么第三個參數(shù)就是插槽
<template>
<div>
<!-- 我們使用h函數(shù)來進(jìn)行封裝,h函數(shù)如果第1個參數(shù)如果是組件,那么第三個參數(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>

暴露實例以及實例中的方法
我們可以通過 defineExpose 來暴露實例以及方法【常用的】
也可以通過vm.exposed來進(jìn)行暴露實例以及方法
需要注意組件最初設(shè)置了v-if=false這種情況
// 子組件
<template>
<div>
<!-- 我們使用h函數(shù)來進(jìn)行封裝,h函數(shù)如果第1個參數(shù)如果是組件,那么第三個參數(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)前組件實例
const vm = getCurrentInstance()
// ref可以是一個字符串,也可以是一個函數(shù)。這樣父組件就可以通過ref訪問這個組件的實例了
function nodeRef(inputInstance) {
// 現(xiàn)在我們把子組件實例給他,當(dāng)組件使用了v-if=false的時候,inputInstance為null
// 這里我們是把實例(實例中包含方法)暴露出去
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)
// 獲取實例中的方法
const getHandler = () => {
NodeInputRef.value?.clear()
}
const clearHandler = () => {
console.log('失去焦點啦')
}
</script>


另外一種暴露方式
常見的暴露方式
defineProps({
name:xxx,
age:xxx,
})
等價與下面這一種
vm.exposed= {
name:xxx,
age:xxx,
}
vue3 中的 props
props 是組件的自定義屬性,用于從父組件向子組件傳遞數(shù)據(jù)。
props 不會包含繼承的屬性(如 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)一個組件聲明了prop時候,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('失去焦點啦')
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)文章
基于Vue和Firebase實現(xiàn)一個實時聊天應(yīng)用
在本文中,我們將學(xué)習(xí)如何使用Vue.js和Firebase來構(gòu)建一個實時聊天應(yīng)用,Vue.js是一種流行的JavaScript前端框架,而Firebase是Google提供的實時數(shù)據(jù)庫和后端服務(wù),結(jié)合這兩者,我們可以快速搭建一個功能強(qiáng)大的實時聊天應(yīng)用,需要的朋友可以參考下2023-08-08
基于Vue3和element-plus實現(xiàn)登錄功能(最終完整版)
這篇文章主要介紹了基于Vue3和element-plus實現(xiàn)一個完整的登錄功能,本文結(jié)合示例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03
Vue?+?Android?WebView實現(xiàn)大文件PDF預(yù)覽完整解決方案(附詳細(xì)代碼)
這篇文章主要介紹了Vue?+?Android?WebView實現(xiàn)大文件PDF預(yù)覽完整解決方案的相關(guān)資料,解決了在AndroidWebView環(huán)境下預(yù)覽大文件PDF的問題,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下2025-12-12
vue cli3.0 引入eslint 結(jié)合vscode使用
這篇文章主要介紹了vue cli3.0 引入eslint 結(jié)合vscode使用,本文分步驟給大家介紹的非常詳細(xì),具有一定的參考借鑒價值,需要的朋友可以參考下2019-05-05
vue--點擊當(dāng)前增加class,其他刪除class的方法
今天小編就為大家分享一篇vue--點擊當(dāng)前增加class,其他刪除class的方法,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-09-09
vue中v-if?和v-permission?共同使用的坑及解決方案
這篇文章主要介紹了vue中v-if?和v-permission?共同使用的坑及解決方案的相關(guān)資料,需要的朋友可以參考下2023-07-07

