Vue3 组件封装的一些技巧和心得

点击上方 前端Q,关注公众号

回复加群,加入前端Q技术交流群

在日常开发的过程中,使用Vue的组件进行业务拆分,代码解耦是一个很好的选择;

今天就来分享一下我在使用Vue3进行组件封装的一些技巧和心得,希望能够帮助到大家;

1. 组件特性

Vue中组件是一个独立的实例,每个组件都有共通点,就是:属性插槽事件方法

在日常我们使用第三方组件库的时候,组件库的文档都会说明上面四个特性,而组件封装就是围绕这四个特性进行的;

2. 组件封装

2.1 组件继承

很多情况下,我们会在一个组件的基础上进行扩展,这个时候就需要用到组件继承;

Vue2的时候,我们可以使用extends关键字进行组件继承,但是在Vue3中,extends关键字已经被废弃了;

Vue3中,如果想要实现组件继承其实很简单,要明白一个组件其实就是一个js对象,我们可以直接将一个组件对象合并,然后注册成一个新的组件;

import { createApp } from "vue";
import App from "./App.vue";
import ElementPlus, { ElInput } from "element-plus";
import "element-plus/dist/index.css";
import { merge } from "lodash";

const app = createApp(App);
app.use(ElementPlus);

// 组件继承,将ElInput组件的placeholder属性默认值改为"请输入"
app.component(
  "ElInput",
  merge(ElInput, {
    props: {
      placeholder: {
        default: "请输入"
      }
    }
  })
);

app.mount("#app");

这里直接使用了lodashmerge方法,将ElInput组件的props属性进行了合并,然后覆盖注册成了一个新的组件;

因为有很多小伙伴遇到一个问题就是需要固定ElTable组件的一些属性,比如borderstripesize等,这个时候用这种方法就非常方便;

2.2 组件插槽

上面的组件继承只是简单的改变了组件的默认属性,但是如果我们想要改变组件的结构,就需要用到组件插槽;

通常情况下我们要拆分组件的业务,然后封装成业务组件,这个时候可能会使用到多个组件;

这个时候组件里面有很多组件,需要替换组件里面的组件里面的插槽,这个时候就需要透传插槽;

<!--  透传插槽  -->
<template>
  <div>
    区域A这里有一个组件,这个组件需要替换插槽
    <el-tree :data="treeData">
      <template v-if="$slots.tree" #default="{ node, data }">
        <slot name="tree" :node="node" :data="data" />
      </template>
    </el-tree>
  </div>

  <div>
    区域B这里有一个组件,这个组件需要替换插槽
    <el-table :data="tableData">
      <template v-if="$slots.default">
        <slot />
      </template>
    </el-table>
  </div>
</template>

<script>
export default {
  data() {
    return {
      treeData: new Array(10)
          .fill(0)
          .map((_, index) => ({ label: "label" + index })),
      tableData: [],
    };
  },
};
</script>

通过使用$slots可以获取到组件的插槽,然后通过v-if判断是否有插槽,如果有插槽就进行透传;

除了这种方式之外,还可以使用jsx语法,这种方式更加灵活;

<script lang="jsx">
export default {
  render() {
    const areaA = (
        <div>
          区域A这里有一个组件,这个组件需要替换插槽
          <el-tree data={treeData}>
            {{
              default: this.$slots.tree
            }}
          </el-tree>
        </div>
    );

    const areaB = (
        <div>
          区域B这里有一个组件,这个组件需要替换插槽
          <el-table data={tableData}>
            {{
              default: this.$slots.default
            }}
          </el-table>
        </div>
    );
    
    return (
        <div>
          {areaA}
          {areaB}
        </div>
    );
  }
}
</script>

setup语法中是没有this的,这个使用需要获取$slots的时候需要使用useSlots方法;

2.3 组件事件和透传 attrs

Vue2中,我们可以使用$listeners来获取组件的事件,然后进行透传;

而在Vue3中,$listeners已经被废弃了,$listeners$attrs都被合并到了$attrs中;

<!-- 组件 -->
<template>
  <div v-bind="$attrs"></div>
</template>

<!-- 父组件 -->
<template>
  <div>
    <MyComponent 
        class="my-class"
        @click="handleClick"
    />
  </div>
</template>

Vue3中,我们可以直接使用$attrs来获取组件的事件,然后进行透传;

例如上面的例子,我们可以直接在组件中使用$attrs来获取到class@click事件,等同于下面的写法;

<!-- 组件 -->
<template>
  <div class="my-class" @click="handleClick"></div>
</template>

但是这里其实有一个小技巧,就是Vue3默认属性是可以透传的,例如上面的例子其实可以简化成下面的写法;

<!-- 组件 -->
<template>
  <div></div>
</template>

<!-- 父组件 -->
<template>
  <div>
    <MyComponent 
        class="my-class"
        @click="handleClick"
    />
  </div>
</template>

就是组件里面什么都不写,最后在父组件中使用这个组件的时候,属性会透传到组件中的根元素上;

参考:透传 Attributes[1]

了解这个特性就可以这样封装组件:

<!-- 组件 -->
<template>
  <el-dialog>
  </el-dialog>
</template>

<!-- 父组件 -->
<template>
  <div>
    <MyComponent 
        v-model="visible"
        width="500px"
    />
  </div>
</template>

通常我们会封装一个Dialog组件来解耦业务,这个时候直接将Dialog作为根元素,然后可以将v-modelwidth属性透传到Dialog组件上;

这样不需要写Dialog组件开启关闭的双向绑定的代码,前提是不需要在组件内部操作Dialog的开启关闭;

2.4 组件方法

Vue2中,我们可以通过this.$refs.xxx来获取到组件的实例,然后调用组件的方法;

Vue3中,我们可以通过ref来获取到组件的实例,然后调用组件的方法;

但是不管是Vue2还是Vue3,在组件内部想要使用组件的子组件的方法都不是一件容易的事情;

通常都是手动将组件的实例获取到,然后再重新定义在组件的methods中;

<!-- 组件 -->
<template>
  <div>
    <el-input ref="input" />
  </div>
</template>

<script>
export default {
  methods: {
    focus() {
      this.$refs.input.focus();
    },
  },
};
</script>

组件的方法通常没有啥特别好的方式,除了我上面的这种方式之外,还有小伙伴是直接将ref返回出去:

<template>
  <div>
    <el-input ref="input" />
  </div>
</template>

<script>
export default {
  methods: {
    inputRef() {
      return this.$refs.input
    },
  },
};
</script>

当然还有一种偷懒的方式:

<template>
  <div>
    <el-input ref="input" />
  </div>
</template>

<script>
export default {
  mounted() {
    Object.values(this.$refs.input).forEach((value) => {
      if (typeof value === 'function') {
        this[value.name] = (...args) => value(...args);
      }
    });
  },
  methods: {
    inputRef() {
      return this.$refs.input
    },
  },
};
</script>

不过这种偷懒的方式只能在options api中使用,因为在composition api中是没有this的;

对于setup语法,如果需要使用组件的方法,可以使用getCurrentInstance来获取到组件的实例,然后将方法挂载到exposed上;

<template>
  <div>
    <el-input ref="input" />
  </div>
</template>

<script setup>
import { getCurrentInstance, onMounted, ref } from "vue";

const instance = getCurrentInstance();
const input = ref(null);
onMounted(() => {
  Object.values(input.value).forEach((value) => {
    if (typeof value === "function") {
      instance.exposed[value.name] = (...args) => value(...args);
    }
  });
});
</script>

这种方式不太稳定,因为exposedVue3的一个私有属性,不建议使用;

setup语法中如果需要暴露组件的内部方法,需要使用defineExpose来暴露;

<script setup>
// ... 省略其他代码

defineExpose({
  focus: () => {
    input.value.focus();
  },
});
</script>

总结

这次带来的是Vue3的组件封装的一些技巧,主要是setup语法的一些特性,以及Vue3中的一些奇淫技巧;

Vue3的组件封装相比Vue2来说更加的灵活,但是也更加的复杂,需要我们在使用的时候多加注意;

这次分享的只是一些技术上的点,组件封装是一门非常大的学问,需要我们在实际的项目中多加实践;

作者:田八

链接:https://juejin.cn/post/7247050634191454266

4df41fa33ec7e80ad8e39194c3f49d94.png

往期推荐

axios中的那些天才代码!看完我实力大涨!

4e3c4cbcab3979933fe9a98f05e40d8c.png

领域驱动设计DDD在B端营销系统的实践

831ebd0e5b7da3a306e3bd6383342ace.png

Vue3 又一新选择:VueHooks Plus 强势来袭!

04f4c8b092292ffd435eb28a9e9a2436.png


最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

04f20eb037c9a03ba73afcd0c60249f6.jpeg

5b52e941e96ba31d6ac0af11dd94c0f8.png

点个在看支持我吧

6e0d342570039d1d982a32c2929be731.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Vue 2和Vue 3在组件封装方面有一些区别。下面是一些主要的区别: 1. 语法:Vue 2使用的是Options API,而Vue 3引入了Composition API。Options API基于选项对象,在一个对象中定义组件的选项,但随着组件变得复杂,选项对象可能会变得冗长。Composition API允许开发者通过函数的形式来组织组件的逻辑,使代码更易维护和重用。 2. 组件注册:在Vue 2中,我们使用Vue.component()全局注册组件或者通过components选项在局部注册组件。而在Vue 3中,全局注册的方法变成了app.component(),而且不再需要通过Vue实例来进行注册。 3. Props 的类型声明:在Vue 2中,我们需要使用props选项来声明组件的Props,并可以指定各个prop的类型、默认值等信息。而在Vue 3中,可以使用`<script setup>`语法来声明Props并给它指定类型。 4. 生命周期钩子:在Vue 2中,我们使用各种生命周期钩子函数来处理组件的生命周期事件。而在Vue 3中,Options API中的生命周期钩子函数仍然可用,但推荐使用Composition API中的函数式API来处理。 5. Transition/动画:Vue 2中有内置的transition和动画系统,可以通过<transition>和<transition-group>标签来实现。而在Vue 3中,这些功能被迁移到了单独的@vue/transition和@vue/animation库中,需要额外安装和引入。 需要注意的是,Vue 3在组件封装方面引入了更多的改进,并且提供了更强大和灵活的开发体验。但由于Vue 3相对于Vue 2来说还比较新,一些库和插件可能还没有完全适配Vue 3,因此在升级之前需要考虑项目的具体情况和可行性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值