组件的二次封装,主要的问题是解决v-model的实现,其次是属性的传递
attrs透传属性
升级到vue3后$attrs这个实例属性可以做到之前vue2中$attrs与$listener两者的活,即将父级所有的属性接收(除了组件自身声明过的) 既然是所有的属性,自然就包括属性与事件,也包括v-model,代码如下: 注意: 此写法中,绑定的组件必须是根节点,因为透传的属性默认是传到根节点上,如果想作用于别的地方,需要调用 defineOptions({ inheritAttrs: false })
如果想访问绑定的值做一些操作,可以通过useAttrs()方法获取
defineModel
vue3.3版本加入的实验性api,目前控制台会有警告但是可用,此api简化了v-model所需的代码,不需要定义事件触发,只需像定义ref一样定义一个绑定值,代码如下
<template>
<el-input v-model="formVal" v-bind="$attrs" />
</template>
<script lang="ts" setup>
const value = defineModel()
</script>
利用计算属性
巧妙的利用计算属性
<template>
<el-input v-model="formVal" v-bind="$attrs" />
</template>
<script lang="ts" setup>
import { computed } from "vue"
const props = defineProps<{
params: string
}>()
const formVal = computed({
get() {
return props.params
},
set(value) {
emit("update:params", value)
}
})
</script>
使用watch
这种方式比较笨重,方式:
- 组件自身维护一个绑定的值state,初始值为props的值
- 监听state,值改变时触发update更新父级绑定的值,这样即可以做到父子组件的v-model 不过很明显这种方式是最麻烦的,但也有用武之地
<template>
<el-input v-model="formVal" v-bind="$attrs" />
</template>
<script lang="ts" setup>
import { ref, watch } from "vue"
const props = defineProps<{
params: string
}>()
const emit = defineEmits(["update:params"])
const formVal = ref(props.params)
watch(() => formVal.value, (val) => {
emit('update:params', val)
})
</script>
还有个注意的点,这种方式下父级值改变并不会起作用,所以这种情况可以改为监听props,动态给子组件自身赋值,子组件change事件触发后,再$emit更新父组件值即可