vue3使用h函數(shù)如何封裝組件和$attrs和props的區(qū)別
二次封裝組件需要考慮的3個重要的問題
1,props 如何進行傳遞
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)置屬性了。
下面我們會詳細說一下$attrs
props 如何進行傳遞屬性和事件
我們可以在子組件中使用 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ù)來進行封裝。
h函數(shù)如果第1個參數(shù)如果是組件,那么第三個參數(shù)就是插槽
<template>
<div>
<!-- 我們使用h函數(shù)來進行封裝,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來進行暴露實例以及方法
需要注意組件最初設(shè)置了v-if=false這種情況
// 子組件
<template>
<div>
<!-- 我們使用h函數(shù)來進行封裝,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
下面是詳細的講解:
在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>

遇見問題,這是你成長的機會,如果你能夠解決,這就是收獲。
出處:http://www.rzrgm.cn/IwishIcould/
想問問題,打賞了卑微的博主,求求你備注一下的扣扣或者微信;這樣我好聯(lián)系你;(っ??ω??)っ???!
如果覺得這篇文章對你有小小的幫助的話,記得在右下角點個“推薦”哦,或者關(guān)注博主,在此感謝!
萬水千山總是情,打賞5毛買辣條行不行,所以如果你心情還比較高興,也是可以掃碼打賞博主(っ??ω??)っ???!
想問問題,打賞了卑微的博主,求求你備注一下的扣扣或者微信;這樣我好聯(lián)系你;(っ??ω??)っ???!
如果文中有什么錯誤,歡迎指出。以免更多的人被誤導(dǎo)。

浙公網(wǎng)安備 33010602011771號