二次封装的方法

二次封装

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

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

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>

整体效果如图

相关推荐
quo-te3 分钟前
AJAX简介
前端·ajax·okhttp
bingbingyihao14 分钟前
通过代码获取接口文档工具
开发语言·前端·javascript
月伤5914 分钟前
JS中Map对象与数组的相互转换
前端·javascript·html
SEO_juper2 小时前
解密 URL 参数:如何利用它们提升网站性能和用户体验
前端·javascript·ux·seo·url·数字营销·谷歌seo
nuIl2 小时前
让 Cursor 帮你把想法落地
前端·ai编程
HyaCinth2 小时前
Taro 数字滚动组件
javascript·react.js·taro
去伪存真2 小时前
看我如何破解api接口文档定义空白, 还不想手动写接口TS类型定义的困局
前端·typescript
skyWang4162 小时前
Vite模块联邦(vite-plugin-federation)实现去中心化微前端后台管理系统架构
前端
喝拿铁写前端2 小时前
你以为你是中级前端,其实你还停留在执行阶段-完整前端成长之路
前端
前端卧龙人2 小时前
uniapp开发技巧:开启代理与gzip优化实践
前端