Vue3实现组件二次封装的小技巧分享

 更新时间:2024年09月12日 09:01:21   作者:唐诗  
组件的二次封装:保留组件已有的功能,需要重写组件方法,当组件已有大量功能时候,则需要重写很多重复代码,且组件功能进行修改的时候,封装的组件也需要对应修改,从而造成许多开发和维护成本,本文给大家分享了Vue3实现组件二次封装的小技巧,需要的朋友可以参考下

双向数据绑定

我们以 input 组件作为例子

双向数据绑定的原理及实现想必大家已经烂熟于心了直接看官网吧!

子组件接受一个 modelValue 的 prop, 通过 emit 触发 update:modelValue 事件完成数据的更新

父组件直接 v-model="xxxx"

嫌麻烦官方还提供了 defineModel() 用于简化上边的步骤

向子组件传递插槽

我们以 input 组件作为例子,创建一个 WrapInput.vue 组件

未学习之前

WrapInput.vue 常规的做法,遍历 $slots 来实现

<script setup lang="ts">
const model = defineModel()

</script>

<template>
  <el-input v-model="model" placeholder="Please input" >
    <template v-for="(_, slot) in $slots" :key="solt" v-slot:[slot]="slotProps">
      <slot :name="slot" v-bind="slotProps"></slot>
    </template>
  </el-input>
</template>

<style lang='scss' scoped></style>

在 app.vue 中引入并传递 prepend、append 插槽

<script setup lang="ts">
import { ref } from "vue";
import WrapInput from "./components/WrapInput.vue";

const inputText = ref('')
</script>

<template>
  <WrapInput v-model="inputText">
    <template #prepend>Http://</template>
    <template #append>.com</template>
  </WrapInput>

  <div>
    {{inputText}}
  </div>
</template>

<style scoped>
</style>

正确渲染了插槽

学习之后

让我们来修改下 WrapInput.vue

<script setup lang="ts">
import { h } from "vue";
import { ElInput } from "element-plus";
const model = defineModel()
</script>

<template>
<component :is="h(ElInput, $attrs, $slots)" v-model="model"></component>
</template>

<style lang='scss' scoped></style>

app.vue 的代码不做任何修改

插槽正常传递、数据更新正常,看到这种写法的时候有点震惊的

component 组件为什么可以传入 h 函数

看下 h 函数的文档, h(ElInput, $attrs, $slots) 是创建了一个虚拟 dom 节点

而 component 组件的 is 属性则可以接收

  • 被注册的组件名
  • 导入的组件对象
  • 一个返回上述值之一的函数

component 组件的 is 属性接收到一个函数时,Vue 会调用这个函数并使用其返回值作为要渲染的组件。

在这种情况下,h(ElInput, $attrs, $slots) 会立即执行并返回一个 VNode,这个 VNode 描述了如何渲染 ElInput 组件。

获取子组件的 ref

未学习之前

之前的自己的写法有点蠢的具体的做法是在子组件创建一个 getRef 的函数把 ref 暴露出去,父组件调用 getRef 方法后在执行子组件方法的调用,大概是下边这样

WrapInput1.vue

<script setup lang="ts">
import { h, ref} from "vue";
import { ElInput } from "element-plus";
const model = defineModel()

const inputRef = ref()

function getRef () {
  return inputRef.value
}

defineExpose({
  getRef
})
</script>

<template>
  <component ref="inputRef" :is="h(ElInput, $attrs, $slots)" v-model="model"></component>
</template>

<style lang='scss' scoped></style>

学习之后

WrapInput.vue

<script setup lang="ts">
import { h, ref } from "vue";
import { ElInput } from "element-plus";
const model = defineModel()

const inputRef = ref()

defineExpose(new Proxy({}, {
  get(_target, prop)  {
    return inputRef.value?.[prop]
  },
  has (_target, prop) {
    return prop in inputRef.value
  }
}))

</script>

<template>
  <component :is="h(ElInput, $attrs, $slots)" v-model="model" ref="inputRef"></component>
</template>

<style lang='scss' scoped></style>

使用 Proxy 代理暴露出去的方法,是有点震惊的,还能这么写

App.vue

<script setup lang="ts">
import { ref } from "vue";
import WrapInput from "./components/WrapInput.vue";

const inputText = ref('')

const prependSlotText =  ref('Http://')
const appendSlotText =  ref('.com')

function updateSlotInfo (){
  prependSlotText.value = 'https://'
  appendSlotText.value = `${new Date().getTime()}`
}

const wrapInputRef = ref()
function setWrapInputFocus () {
  wrapInputRef.value?.focus()
}
</script>

<template>
  <WrapInput v-model="inputText" ref="wrapInputRef">
    <template #prepend>{{ prependSlotText }}</template>
    <template #append>{{ appendSlotText }}</template>
  </WrapInput>

  <div style="margin: 20px 0;">
    {{inputText}}
  </div>

  <el-button type="primary" @click="updateSlotInfo">更新插槽内容</el-button>
  <el-button type="primary" @click="setWrapInputFocus">set input focus</el-button>
  
</template>

<style scoped>
</style>

调用组件的 focus 方法让 WrapInput.vue 组件获取焦点

总结

本文实践了在 vue3 中在二次封装组件时如何实现 v-model、插槽传递、子组件 ref 获取

插槽传递通过向 component 组件的 is 属性传递 h 函数创建虚拟 dom 来实现

获取子组件的 ref 则是使用 new Proxy 的方式来实现

以上就是Vue3实现组件二次封装的小技巧分享的详细内容,更多关于Vue3组件二次封装的资料请关注脚本之家其它相关文章!

相关文章

  • 前端面试之vue2和vue3的区别有哪些

    前端面试之vue2和vue3的区别有哪些

    这篇文章主要为大家介绍了前端面试之vue2和vue3的区别有哪些,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-05-05
  • vue element Cascader级联选择器解决最后一级显示空白问题

    vue element Cascader级联选择器解决最后一级显示空白问题

    这篇文章主要介绍了vue element Cascader级联选择器解决最后一级显示空白问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-10-10
  • vue-cli webpack2项目打包优化分享

    vue-cli webpack2项目打包优化分享

    下面小编就为大家分享一篇vue-cli webpack2项目打包优化,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2018-02-02
  • Vue数组响应式操作及高阶函数使用代码详解

    Vue数组响应式操作及高阶函数使用代码详解

    这篇文章主要介绍了Vue数组响应式操作及高阶函数使用代码详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • vue-router传参的4种方式超详细讲解

    vue-router传参的4种方式超详细讲解

    我们在组件切换时经常会有传递一些数据的需求,这样就涉及到了路由传参的问题,下面这篇文章主要给大家介绍了关于vue-router传参的4种超详细方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-07-07
  • 用Vue-cli搭建的项目中引入css报错的原因分析

    用Vue-cli搭建的项目中引入css报错的原因分析

    本篇文章主要介绍了用Vue-cli搭建的项目中引入css报错的原因分析,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Vue-router优化import引入过多导致index文件臃肿问题

    Vue-router优化import引入过多导致index文件臃肿问题

    这篇文章主要为大家介绍了Vue-router优化import引入过多导致index文件臃肿问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-08-08
  • vuex项目中登录状态管理的实践过程

    vuex项目中登录状态管理的实践过程

    由于状态零散地分布在许多组件和组件之间的交互中,大型应用复杂度也经常逐渐增长,为了解决这个问题,Vue 提供 vuex,这篇文章主要给大家介绍了关于vuex项目中登录状态管理的相关资料,需要的朋友可以参考下
    2021-09-09
  • vue2升级vue3问题bug解决分析整理

    vue2升级vue3问题bug解决分析整理

    这篇文章主要介绍了vue2升级vue3遇到的问题bug解决分析整理,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-10-10
  • 详解element-ui动态限定的日期范围选择器代码片段

    详解element-ui动态限定的日期范围选择器代码片段

    这篇文章主要介绍了element-ui动态限定的日期范围选择器代码片段,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-07-07

最新评论