[前端] Vue3CompositionAPI优雅封装第三方组件实例

2016 0
Honkers 2022-10-21 16:02:24 | 显示全部楼层 |阅读模式
目录

    前言一、对于第三方组件的属性props、事件events二、对于第三方组件的插槽slots三、对于第三方组件的方法methods


前言

对于第三方组件,如何在保持第三方组件原有功能(属性props、事件events、插槽slots、方法methods)的基础上,优雅地进行功能的扩展了?
以Element Plus的el-input为例:
很有可能你以前是这样玩的,封装一个MyInput组件,把要使用的属性props、事件events和插槽slots、方法methods根据自己的需要再写一遍:
  1. // MyInput.vue
  2. <template>
  3.   <div class="my-input">
  4.     <el-input v-model="inputVal" :clearable="clearable" @clear="clear">
  5.     <template #prefix>
  6.       <slot name="prefix"></slot>
  7.     </template>
  8.       <template #suffi>
  9.       <slot name="suffix"></slot>
  10.     </template>
  11.     </el-input>
  12.   </div>
  13. </template>
  14. <script setup>
  15. import { computed } from 'vue'
  16. const props = defineProps({
  17.   modelValue: {
  18.     type: String,
  19.     default: ''
  20.   },
  21.   clearable: {
  22.     type: Boolean,
  23.     default: false
  24.   }
  25. })
  26. const emits = defineEmits(['update:modelValue', 'clear'])
  27. const inputVal = computed({
  28.   get: () => props.modelValue,
  29.   set: (val) => {
  30.     emits('update:modelValue', val)
  31.   }
  32. })
  33. const clear = () => {
  34.   emits('clear')
  35. }
  36. </script>
复制代码
可过一段时间后,需求变更,又要在MyInput组件上添加el-input组件的其它功能,可el-input组件总共有20个多属性,5个事件,4个插槽,那该怎么办呢,难道一个个传进去,这样不仅繁琐而且可读性差。
在Vue2中,我们可以这样处理,点击此处查看 封装Vue第三方组件
此文诣在帮助大家做一个知识的迁移,探究如何使用Vue3 CompositionAPI优雅地封装第三方组件~

一、对于第三方组件的属性props、事件events

在Vue2中
    $attrs: 包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件$listeners:包含了父作用域中的 (不含 .native 修饰器的) v-on 事件监听器。它可以通过 v-on="$listeners" 传入内部组件
而在Vue3中
    $attrs:包含了父作用域中不作为组件 props 或自定义事件的 attribute 绑定和事件(包括 class 和 style和自定义事件),同时可以通过 v-bind="$attrs" 传入内部组件。$listeners 对象在 Vue 3 中已被移除。事件监听器现在是 $attrs 的一部分。在 <script setup>中辅助函数useAttrs可以获取到$attrs。
  1. //MyInput.vue
  2. <template>
  3.   <div class="my-input">
  4.     <el-input v-bind="attrs"></el-input>
  5.   </div>
  6. </template>
  7. <script setup>
  8. import { useAttrs } from 'vue'
  9. const attrs = useAttrs()
  10. </script>
复制代码
当然,这样还不够。光这样写,我们绑定的属性(包括 class 和 style)同时会在根元素(上面的例子是class="my-input"的Dom节点)上起作用。要阻止这个默认行为,我们需要设置inheritAttrs为false。
下面我们来看看Vue3文档对inheritAttrs的解释
默认情况下父作用域的不被认作 props 的 attribute 绑定 (attribute bindings) 将会“回退”且作为普通的 HTML attribute 应用在子组件的根元素上。当撰写包裹一个目标元素或另一个组件的组件时,这可能不会总是符合预期行为。通过设置 inheritAttrs 到 false,这些默认行为将会被去掉。而通过实例 property $attrs 可以让这些 attribute 生效,且可以通过 v-bind 显性的绑定到非根元素上。
于是,我们对于第三方组件的属性props、事件events处理,可以写成如下代码:
  1. // MyInput.vue
  2. <template>
  3.   <div class="my-input">
  4.     <el-input v-bind="attrs"></el-input>
  5.   </div>
  6. </template>
  7. <script>
  8. export default {
  9.   name: 'MyInput',
  10.   inheritAttrs: false
  11. }
  12. </script>
  13. <script setup>
  14. import { useAttrs } from 'vue'
  15. const attrs = useAttrs()
  16. </script>
复制代码
二、对于第三方组件的插槽slots

Vue3中
    $slots:我们可以通过其拿到父组件传入的插槽Vue3中移除了$scopedSlots,所有插槽都通过 $slots 作为函数暴露在 <script setup>中辅助函数useSlots可以获取到$slots。
基于以上几点,如果我们对于第三方组件的封装没有增加额外的插槽,且第三方组件的插槽处于同一个dom节点之中,我们也有一种取巧的封装方式, 通过遍历$slots拿到插槽的name,动态添加子组件的插槽:
  1. //MyInput.vue
  2. <template>
  3.   <div class="my-input">
  4.     <el-input v-bind="attrs">
  5.       <template v-for="k in Object.keys(slots)" #[k] :key="k">
  6.         <slot :name="k"></slot>
  7.       </template>
  8.     </el-input>
  9.   </div>
  10. </template>
  11. <script>
  12. export default {
  13.   name: 'MyInput',
  14.   inheritAttrs: false
  15. }
  16. </script>
  17. <script setup>
  18. import { useAttrs, useSlots } from 'vue'
  19. const attrs = useAttrs()
  20. const slots = useSlots()
  21. </script>
复制代码
如果不满足以上条件的话,咱还得老老实实在子组件中手动添加需要的第三方组件的插槽~

三、对于第三方组件的方法methods

对于第三方组件的方法,我们通过ref来实现。首先在MyInput组件中的el-input组件上添加一个ref="elInputRef"属性,然后通过defineExpose把elInputRef暴露出去给父组件调用。
子组件:MyInput.vue
  1. // MyInput.vue
  2. <template>
  3.   <div class="my-input">
  4.     <el-input v-bind="attrs" ref="elInputRef">
  5.       <template v-for="k in Object.keys(slots)" #[k] :key="k">
  6.         <slot :name="k"></slot>
  7.       </template>
  8.     </el-input>
  9.   </div>
  10. </template>
  11. <script>
  12. export default {
  13.   name: 'MyInput',
  14.   inheritAttrs: false
  15. }
  16. </script>
  17. <script setup>
  18. import { useAttrs, useSlots } from 'vue'
  19. const attrs = useAttrs()
  20. const slots = useSlots()
  21. const elInputRef = ref(null)
  22. defineExpose({
  23.   elInputRef  // <script setup>的组件里的属性默认是关闭的,需通过defineExpose暴露出去才能被调用
  24. })
  25. </script>
复制代码
父页面:Index.vue的调用代码如下:
  1. // Index.vue
  2. <template>
  3.   <my-input v-model='input' ref="myInput">
  4.     <template #prefix>姓名</template>
  5.   </my-input>
  6. </template>
  7. <script setup>
  8. import MyInput from './components/MyInput.vue'
  9. import { ref, onMounted } from 'vue'
  10. const input = ref('')
  11. const myInput = ref(null) // 组件实例
  12. onMounted(()=> {
  13.   myInput.value.elInputRef.focus() // 初始化时调用elInputRef实例的focus方法
  14. })
  15. </script>
复制代码
以上就是Vue3 Composition API优雅封装第三方组件实例的详细内容,更多关于Vue3 Composition API封装第三方组件的资料请关注中国红客联盟其它相关文章!
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

Honkers

荣誉红客

关注
  • 4008
    主题
  • 36
    粉丝
  • 0
    关注
这家伙很懒,什么都没留下!

中国红客联盟公众号

联系站长QQ:5520533

admin@chnhonker.com
Copyright © 2001-2025 Discuz Team. Powered by Discuz! X3.5 ( 粤ICP备13060014号 )|天天打卡 本站已运行