二次封装的方法

二次封装

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

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

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>

整体效果如图

相关推荐
打不着的大喇叭39 分钟前
uniapp的光标跟随和打字机效果
前端·javascript·uni-app
无我Code1 小时前
2025----前端个人年中总结
前端·年终总结·创业
程序猿阿伟1 小时前
《前端路由重构:解锁多语言交互的底层逻辑》
前端·重构
Sun_light1 小时前
6个你必须掌握的「React Hooks」实用技巧✨
前端·javascript·react.js
爱学习的茄子1 小时前
深度解析JavaScript中的call方法实现:从原理到手写实现的完整指南
前端·javascript·面试
莫空00001 小时前
Vue组件通信方式详解
前端·面试
呆呆的心1 小时前
揭秘 CSS 伪元素:不用加标签也能玩转出花的界面技巧 ✨
前端·css·html
百锦再1 小时前
重新学习Vue中的按键监听和鼠标监听
javascript·vue.js·vue·计算机外设·click·up·down
快起来别睡了1 小时前
Vue 3 中的组件通信与组件思想详解
vue.js
susnm1 小时前
Dioxus 与数据库协作
前端·rust