二次封装的方法

二次封装

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

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

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>

整体效果如图

相关推荐
啃火龙果的兔子1 小时前
判断手机屏幕上的横向滑动(左滑和右滑)
javascript·react.js·智能手机
yuanmenglxb20042 小时前
react基础技术栈
前端·javascript·react.js
coding随想3 小时前
从SPDY到HTTP/2:网络协议的革新与未来
javascript
Magnum Lehar3 小时前
vulkan游戏引擎vulkan部分的fence实现
java·前端·游戏引擎
一枚码农4044 小时前
使用pnpm、vite搭建Phaserjs的开发环境
javascript·游戏·vite·phaserjs
FreeBuf_4 小时前
恶意npm与VS Code包窃取数据及加密货币资产
前端·npm·node.js
hdsoft_huge4 小时前
VUE npm ERR! code ERESOLVE, npm ERR! ERESOLVE could not resolve, 错误有效解决
vue.js·rust·npm
agenIT4 小时前
vue3 getcurrentinstance 用法
javascript·vue.js·ecmascript
天天打码4 小时前
npm/yarn/pnpm安装时Sharp模块报错解决方法
前端·npm·node.js
代码老y4 小时前
基于springboot的校园商铺管理系统的设计与实现
java·vue.js·spring boot·后端·毕业设计·课程设计·个人开发