vue.js

关注公众号 jb51net

关闭
首页 > 网络编程 > JavaScript > javascript类库 > vue.js > Vue3组件二次封装

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

作者:唐诗

组件的二次封装:保留组件已有的功能,需要重写组件方法,当组件已有大量功能时候,则需要重写很多重复代码,且组件功能进行修改的时候,封装的组件也需要对应修改,从而造成许多开发和维护成本,本文给大家分享了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组件二次封装的资料请关注脚本之家其它相关文章!

您可能感兴趣的文章:
阅读全文