二次封装的方法

二次封装

我们开发中经常需要封装一些第三方组件,那么父组件应该怎么传值,怎么调用封装好的组件原有的属性、插槽、方法,一个个调用虽然可行,但十分麻烦,我们一起来看更简便的方法。

二次封装组件,属性怎么传

attrs 主要接收不在 props 里定义 ,但父组件又传过来的属性,通过 v-bind="$attrs",可以将属性全部传给封装起来的组件(如下面例子中的 el-input ),而不需要一个一个传

xml 复制代码
// 父组件
<template>
  <div>
    <MyInput v-model="text" placeholder="请输入地址"></MyInput>
  </div>
</template>

<script setup>
import MyInput from './components/MyInput.vue';
import { ref } from 'vue';
const text = ref('123');
</script>
xml 复制代码
// MyInput 组件
<template>
  <div class="item">
    <el-input v-bind="$attrs"></el-input>
  </div>
</template>
<script setup>
import { onMounted, useAttrs } from 'vue';
const props = defineProps({
  // placeholder:{
  //   type:String
  // }
  // attrs包含的是不在props中的属性
  // 如果这里有placeholder,下面输出attrs就不会有这个placeholder
});
const attrs = useAttrs(); // 需要引入 vue 中的 useAttrs ,调用 useAttrs 获取 attrs
onMounted(() => {
  console.log(attrs); // 输出如下图
})
</script>

监听事件怎么实现

$listeners :包含了父作用域中(不含 .native 修饰器的)v-on 事件监听器 ,他可以通过 v-on="listeners" 传入内部组件,监听内部组件的所有事件.

xml 复制代码
<template>
  <div>
    <MyInput
      v-model="text"
      placeholder="请输入地址"
      ref="focusRef"
      @input="inputNum"
    >
      <template #prepend="{}">
        <el-select v-model="select" placeholder="Select" style="width: 115px">
          <el-option label="A" value="1" />
          <el-option label="B" value="2" />
          <el-option label="C" value="3" />
        </el-select>
      </template>
      <template #append>
        <el-button :icon="Search" />
      </template>
    </MyInput>
  </div>
</template>
<script setup>
import MyInput from "./components/MyInput.vue";
import { onMounted, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
const text = ref("123");
const select = ref("");
const focusRef = ref();
onMounted(() => {
  focusRef.value.focus();
});
const inputNum = (val) => { // 被监听事件触发时调用的方法
  console.log("输出:" + val);
};
</script>

// 子组件
<template>
  <div class="item">
    <el-input v-bind="$attrs" v-on="$listeners" ref="inp">
      <template v-for="(value, name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}"></slot>
      </template>
    </el-input>
  </div>
</template>
<script setup>
import { onMounted, useSlots, useAttrs, nextTick, ref } from "vue";

const attrs = useAttrs();
const slots = useSlots();
const inp = ref();
onMounted(() => {
  console.log(attrs);
  console.log(slots);

  console.log(inp.value);
});
defineExpose(
  new Proxy(
    {},
    {
      // 使用 Proxy 代理暴露出去
      get(_target, prop) {
        return inp.value?.[prop];
      },
      has(_target, prop) {
        return prop in inp.value;
      },
    }
  )
);
</script>

插槽怎么使用

二次封装组件时经常需要往原组件的插槽中传递内容,这时要让原始组件知道我们使用了哪些插槽 ,可以使用 $slots。

slots** 是一个表示父组件所传入\[插槽\]的对象,我们可以在子组件中通过 slots 获取到父组件传过来所有插槽名** ,接下来子组件遍历 $slots 动态渲染插槽即可

xml 复制代码
// 父组件
<template>
  <div>
    <MyInput v-model="text" placeholder="请输入地址">
      <template #prepend="{}">
        <el-select v-model="select" placeholder="Select" style="width: 115px">
          <el-option label="A" value="1" />
          <el-option label="B" value="2" />
          <el-option label="C" value="3" />
        </el-select>
      </template>
      <template #append>
        <el-button :icon="Search" />
      </template>
    </MyInput>
  </div>
</template>
<script setup>
import MyInput from './components/MyInput.vue';
import { ref } from 'vue';
import { Search } from '@element-plus/icons-vue'
const text = ref('123');
const select = ref('')
</script>
xml 复制代码
// 也可以往插槽传值 slotData
<template>
  <div class="item">
    <el-input v-bind="$attrs">
      <template v-for="(value,name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}"></slot>
      </template>
    </el-input>
  </div>
</template>
<script setup>
import { onMounted, useSlots, useAttrs } from 'vue';

const attrs = useAttrs();
const slots = useSlots() // 引入 useSlots
onMounted(() => {
  console.log(attrs);
  console.log(slots); // 输出如下图
})
</script>

父组件的 ref 怎么调用目标组件内部方法

我们要想通过父组件的 ref 调用到子组件内部方法 (如 el-input 的 focus 方法)可以怎么做?其实可以通过 ref 链式调用,比如 focusRef.value.inp.value.focus(),但代码的可读性差

更好的方法是将所有方法 暴露出去供父组件调用,可以利用 Proxy 对象来创建一个代理,并通过 defineExpose 将这个代理暴露 给父组件。这个代理的目的是拦截对特定属性的访问,并将这些访问重定向到 inp.value(即 el-input 的引用)上,这样,父组件就可以通过组件的 ref 访问到 el-input 实例的属性

xml 复制代码
// 父组件
<template>
  <div>
    <MyInput v-model="text" placeholder="请输入地址" ref="focusRef">
      <template #prepend="{}">
        <el-select v-model="select" placeholder="Select" style="width: 115px">
          <el-option label="A" value="1" />
          <el-option label="B" value="2" />
          <el-option label="C" value="3" />
        </el-select>
      </template>
      <template #append>
        <el-button :icon="Search" />
      </template>
    </MyInput>
  </div>
</template>
<script setup>
import MyInput from "./components/MyInput.vue";
import { onMounted, ref } from "vue";
import { Search } from "@element-plus/icons-vue";
const text = ref("123");
const select = ref("");
const focusRef = ref();
onMounted(() => {
  focusRef.value.focus();
});
</script>
xml 复制代码
// 子组件
<template>
  <div class="item">
    <el-input v-bind="$attrs" ref="inp">
      <template v-for="(value, name) in $slots" #[name]="slotData">
        <slot :name="name" v-bind="slotData || {}"></slot>
      </template>
    </el-input>
  </div>
</template>
<script setup>
import { onMounted, useSlots, useAttrs, nextTick, ref } from "vue";

const attrs = useAttrs();
const slots = useSlots();
const inp = ref();
onMounted(() => {
  console.log(attrs);
  console.log(slots);

  console.log(inp.value); // 这里输出如下图
});
defineExpose( // 使用 Proxy 代理暴露出去
  new Proxy(
    {},
    {
      get(_target, prop) {
        return inp.value?.[prop];
      },
      has(_target, prop) {
        return prop in inp.value;
      },
    }
  )
);
</script>

整体效果如图

相关推荐
百锦再3 分钟前
国产数据库的平替亮点——关系型数据库架构适配
android·java·前端·数据库·sql·算法·数据库架构
旺仔Sec3 分钟前
2025年海南省职业院校技能大赛“应用软件系统开发“赛项竞赛样题
前端·应用软件系统开发
码界奇点6 分钟前
基于SpringBoot和Vue的Fuint门店会员营销系统设计与实现
vue.js·spring boot·后端·毕业设计·springboot·源代码管理
FakeOccupational26 分钟前
【树莓派 002】 RP2040 实现示波器 PIO来驱动 ADC10080 并抓取数据方案+ 内置12-bitADC&DMA&网页前端可视化方案
前端
至善迎风30 分钟前
Bun:下一代 JavaScript 运行时与工具链
开发语言·javascript·ecmascript·bun
DJ斯特拉32 分钟前
Vue工程化
前端·javascript·vue.js
秋深枫叶红33 分钟前
嵌入式第三十五篇——linux系统编程——exec族函数
linux·前端·学习
LinDon_39 分钟前
【vue2form表单中的动态表单校验】
前端·javascript·vue.js
一水鉴天1 小时前
整体设计 之28 整体设计 架构表表述总表的 完整程序(之27 的Q268 )(codebuddy)
java·前端·javascript