likes
comments
collection
share

如何用VUE3打造通用组件

作者站长头像
站长
· 阅读数 11

前言

众所周知,使用组件库进行开发无疑能帮我们节省开发精力,提高开发效率。目前开源的组件库也有很多,那你知道它们怎么被打造出来的吗?接下来我们试着打造一下ElementUI的一些简单组件吧!

准备

  1. 搭建环境: yarn create vite myelementui --template vue-ts

  2. 初始化:yarn 下载依赖

  3. 创建公共样式:在styles文件夹下用scss写入公共的样式

如何用VUE3打造通用组件

如何用VUE3打造通用组件

一、打造Container 布局容器

container容器

思路:

  • 创建一个总体容器,里面可以放其他标签(用slot插槽)
  • 给默认类名,也就给了该容器默认样式
  • 设置该容器内的内容默认排列方向,也能接收给此组件传的排列方向的值
  1. 在component目录下创建container文件夹,并在此文件夹下创建对应组件文件

如何用VUE3打造通用组件

如何用VUE3打造通用组件

  1. 编写Container.vue文件
<template> //section 标签h5自带
  <section class="el-container" :class="{'is-vertical':isVertical}">//动态绑定类名 设置排列方向ops>(),{ vue自带的有默认值
    <slot></slot> //放插槽 就可以在里面写文字了
  </section>
</template>


<script lang="ts">
export default { //定义组件的名字 抛出给别人用的
  name:'ElContainer'
}
</script>

<script setup lang="ts">
import {computed,useSlots,VNode,Component} from 'vue'

const slots=useSlots() //可以获取插槽slot内用到的标签

//用泛型限定传入的值类型
interface Props{
  direction?:string //此属性可存在可不存在
}

const props=defineProps<Props>() //接收父组件传的值

//根据isVertical来设置布局方向
const isVertical=computed(()=>{//父组件传入的属性
  if(slots&&slots.default){
    return slots.default().some((vn:VNode)=>{
      const tag=(vn.type as Component).name //断言成组件类型
      return tag==='ElHeader'||tag==='ElFooter' //设置只有里面的组件名为ElHeader 和ElFooter 的布局方向是纵轴方向
    })
  }else if(props.direction==='vertical'){ //如果在用到组件时直接在组件上写明方向为vertical
    return true
  }else{
    return false
  }
  
})
</script>

<style lang="scss" scoped>
@import '../../styles/mixin.scss';
@include b(container) {
  display: flex;
  flex-direction: row; //默认弹性主轴方向为横轴
  flex:1;
  flex-basis: auto;
  box-sizing: content-box;
  @include when(vertical) { //有is-vertical的类名 则设置弹性主轴方向为纵轴
    flex-direction: column;
  }
}
</style>
  1. 编写index.ts文件(将打造的组件注册为全局组件)

这里为了方便一次性写完需要注册所有组件

import {App} from 'vue'

import ElContainer from './Container.vue'
import ElHeader from './Header.vue'
import ElMain from './Main.vue'
import ElAside from './Aside.vue'
import ElFooter from './Footer.vue'
export default {
  install(app:App){
    app.component(ElContainer.name,ElContainer) //注册全局组件  
    app.component(ElHeader.name,ElHeader)
    app.component(ElMain.name,ElMain)
    app.component(ElAside.name,ElAside)
    app.component(ElFooter.name,ElFooter)
  }
}
  1. container下的文件生效,在main.ts下引入该文件(让组件生效,能被使用)
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElContainer from './components/container

const app=createApp(App)
app.use(ElContainer)
app.mount('#app')

  1. 这样container组件打造好了,可以直接在App.vue里用了

这里一次性写完用打造出的组件写了两个不同的布局

<template>
  <div>
    <el-container>
      <el-header height="100px">header</el-header>
      <el-container>
        <el-aside>aside</el-aside>
        <el-main>main</el-main>
      </el-container>
      <el-footer height="100px">footer</el-footer>
    </el-container>
    <br> 
    
    //这是第2个布局,只是位置换了一下
    <el-container>
      <el-header height="100px">header</el-header>
      <el-container>
        <el-aside>aside</el-aside>
        <el-container>
          <el-main>main</el-main>
          <el-footer height="100px">footer</el-footer>
      </el-container>
     </el-container> 
    </el-container>
  </div>
</template>

<style lang="scss">
body{
  width: 1000px;
  margin: 10px auto;
}
.el-header,.el-footer{
  background: #b3c0d1;
  text-align: center;
  line-height: 100px;
}
.el-aside{
  background: #d3dce6;
}
.el-main{
  background: #e9eef3;
  text-align: center;
  line-height: 160px;
}

body > .el-container{
  margin-bottom: 40px;
}
.el-container:nth-child(5) .el-aside,
.el-container:nth-child(6) .el-aside {
  line-height: 260px;
}
</style>

Header 头部

思路:

  • 利用html自带标签header创建头部容器,同时给类名
  • 该容器可以设置高度
  • 标签内可以放其他内容(slot)
  1. 编写Header.vue文件
<template>
  <header class="el-header" :style="{ height }"> //为了可以设置头部高度
    <slot></slot> //为了可以在里面放内容
  </header>
</template>

<script lang="ts">
export default {
  name:'ElHeader' //定义组件名字
}
</script>
<script setup lang="ts">

interface Props{ //泛型 设置父组件传的高度的类型
  height?:string
}

withDefaults(defineProps<Props>(),{ vue自带的有默认值的withDefaults:不仅可以接收父组件传的值,还能设置默认值
  height:'60px'
})
</script>

<style lang="scss" scoped>
@import '../../styles/mixin.scss';
@include b(header){
  padding: $--header-padding;
  box-sizing: border-box;
  flex-shrink: 0;
}
</style>
  1. index.ts下添加 声明为全局组件(已写在了开头)
  2. 就可以直接去App.vue中使用了(已写在了开头)

Main 主体部分

思路:

  • 利用html自带的main标签,同时给类名
  • 在标签内放插槽(slot)
  1. 编写Main.vue的内容
<template>
  <main class="el-main">
    <slot />
  </main>
</template>


<script lang="ts">
export default {
  name:'ElMain' //组件名
}
</script>

<style lang="scss" scoped>
@import '../../styles/mixin.scss';
@include b(main){  //组件样式
  display: block;
  flex:1;
  flex-basis: auto;
  overflow: auto;
  box-sizing: border-box;
  padding: $--main-padding;
}
</style>
  1. index.ts下添加 声明为全局组件(已写在了开头)
  2. 就可以直接去App.vue中使用了(已写在了开头)

Aside 侧边栏组件

思路:

  • 利用html自带的aside标签,同时给类名
  • 可以设置该aside容器的宽度
  • 在该标签内可以放内容(slot)
  1. 编写Aside.vue文件
<template>
  <aside class="el-aside" :style="{width}">
    <slot></slot>
  </aside>
</template>

<script lang="ts">
export default {
  name:'ElAside'
}
</script>
<script setup lang="ts">
interface Props{
  width:string
}
withDefaults(defineProps<Props>(),{
  width:'200px'
})

</script>

<style lang="scss" scoped>
@import '../../styles/mixin.scss';
@include b(aside){
  overflow: auto;
  box-sizing: border-box;
  flex-shrink: 0;
}
</style>
  1. index.ts下添加 声明为全局组件(已写在了开头)
  2. 就可以直接去App.vue中使用了(已写在了开头)

Footer 尾部

思路:

  • 利用html自带的footer标签创建容器,同时给类名
  • 可以设置该footer容器的高度
  • 在该标签内可以放内容(slot)
  1. 编写Footer.vue文件
<template>
  <footer class="el-footer" :style="{ height} ">
    <slot />
  </footer>
</template>

<script lang="ts">
export default {
  name:'ElFooter'
}
</script>
<script setup lang="ts">
interface Props{
  height?:string
}
withDefaults(defineProps<Props>(),{
  height:"60px"
})
</script>

<style lang="scss" scoped>
@import '../../styles/mixin.scss';
@include b(footer){
  padding: $--footer-padding;
  box-sizing: border-box;
  flex-shrink: 0;
}
</style>
  1. index.ts下添加 声明为全局组件(已写在了开头)
  2. 就可以直接去App.vue中使用了(已写在了开头)

最终效果:

如何用VUE3打造通用组件

二、打造Button按钮组件

思路:

  • 利用html自带的button标签打造,同时给默认类名
  • button上可以设置大小,样式(在别的地方用到此组件时传值给这个button)
  • 标签内可以放文字(slot插槽)
  1. 准备好公用样式(同样在mixin.css下写,由于样式有点多,这里就放一部分用到的,可以自行添加)

如何用VUE3打造通用组件

//color
$--color-white: #ffffff !default;
$--color-text-regular: #606266 !default;
$--color-text-placeholder: #c0c4cc !default;
$--border-color-base: #dcdfe6 !default;
$--button-default-border-color: $--border-color-base !default;
/* Color
-------------------------- */
/// color|1|Brand Color|0
$--color-primary: #409eff !default;
/// color|1|Background Color|4
$--color-white: #ffffff !default;
/// color|1|Background Color|4
$--color-black: #000000 !default;
/// color|1|Functional Color|1
$--color-success: #67c23a !default;
/// color|1|Functional Color|1
$--color-warning: #e6a23c !default;
/// color|1|Functional Color|1
$--color-danger: #f56c6c !default;
/// color|1|Functional Color|1
$--color-info: #909399 !default;

//button
$--button-font-size: $--font-size-base !default;
$--button-font-weight: $--font-weight-primary !default;
$--button-primary-font-color: $--color-white !default;

$--button-default-background-color: $--color-white !default;
$--button-default-font-color: $--color-text-regular !default;
$--button-padding-vertical: 12px !default;
$--button-padding-horizontal: 20px !default;
$--button-border-radius: $--border-radius-base !default;

$--button-medium-font-size: $--font-size-base !default;
/// borderRadius||Border|2
$--button-medium-border-radius: $--border-radius-base !default;
/// padding||Spacing|3
$--button-medium-padding-vertical: 10px !default;
/// padding||Spacing|3
$--button-medium-padding-horizontal: 20px !default;

/// fontSize||Font|1
$--button-small-font-size: 12px !default;
$--button-small-border-radius: #{$--border-radius-base - 1} !default;
/// padding||Spacing|3
$--button-small-padding-vertical: 9px !default;
/// padding||Spacing|3
$--button-small-padding-horizontal: 15px !default;

@mixin button-size(
  $padding-vertical,
  $padding-horizontal,
  $font-size,
  $border-radius
) {
  padding: $padding-vertical $padding-horizontal;
  font-size: $font-size;
  border-radius: $border-radius;
  &.is-round {
    padding: $padding-vertical $padding-horizontal;
  }
}


@mixin button-variant($color, $background-color, $border-color) {
  color: $color;
  background-color: $background-color;
  border-color: $border-color;

  &:hover,
  &:focus {
    background: mix(
      $--color-white,
      $background-color,
      20%
    );
    border-color: mix(
      $--color-white,
      $border-color,
      20%
    );
    color: $color;
  }

  &:active {
    background: mix(
      $--color-black,
      $background-color,
      $--button-active-shade-percent
    );
    border-color: mix(
      $--color-black,
      $border-color,
      $--button-active-shade-percent
    );
    color: $color;
    outline: none;
  }

  &.is-active {
    background: mix(
      $--color-black,
      $background-color,
      $--button-active-shade-percent
    );
    border-color: mix(
      $--color-black,
      $border-color,
      $--button-active-shade-percent
    );
    color: $color;
  }
}

  1. 编写Button.vue文件
<template>
  <button class="el-button" :class="[size?`el-button--${size}`:'',type?`el-button--${type}`:''] ">
    <slot></slot>
  </button>
</template>

<script lang="ts">
export default {
  name:'ElButton' //按钮名
}
</script>
<script setup lang="ts">
interface Props{
  size?:''|'small'| 'medium' |'large' //只能传这几种值的一种
  type?:''|'primary'|'success'|'danger'
}
withDefaults(defineProps<Props>(),{ //接收父组件传的值
  size:'',
  type:''
})

</script>

<style lang="scss" scoped>
@import "../../styles/mixin.scss";
@include b(button){
  display: inline-block;
  line-height: 1;
  white-space: nowrap;
  cursor: pointer;
  background: $--button-default-background-color;
  color: $--button-default-font-color;
  -webkit-appearance: none;
  text-align: center;
  border: $--border-base;
  border-color: $--button-default-border-color;
  box-sizing: border-box;
  outline: none;
  margin: 0;
  font-weight: $--button-font-weight;
  & + & {
    margin-left: 10px;
  }
  @include button-size(
    $--button-padding-vertical,
    $--button-padding-horizontal,
    $--button-font-size,
    $--button-border-radius
  );
  &:hover,
  &:focus {
    color: $--color-primary;
    border-color: mix($--color-white,$--color-primary,70%);
    background-color: mix($--color-white,$--color-primary,90%);
  }
  @include m(medium) {
    @include button-size(
      $--button-medium-padding-vertical,
      $--button-medium-padding-horizontal,
      $--button-medium-font-size,
      $--button-medium-border-radius
    );
  }
  @include m(small) {
    @include button-size(
      $--button-small-padding-vertical,
      $--button-small-padding-horizontal,
      $--button-small-font-size,
      $--button-small-border-radius
    );

  }
  @include m(large) {
    @include button-size(
      $--button-large-padding-vertical,
      $--button-large-padding-horizontal,
      $--button-large-font-size,
      $--button-large-border-radius
    );
  }
@include m(primary) {
    @include button-variant(
      $--button-primary-font-color,
      $--button-primary-background-color,
      $--button-primary-border-color
    );
  }
  @include m(success) {
    @include button-variant(
      $--button-success-font-color,
      $--button-success-background-color,
      $--button-success-border-color
    );
  }
  @include m(danger) {
    @include button-variant(
      $--button-danger-font-color,
      $--button-danger-background-color,
      $--button-danger-border-color
    );
  }

} 
</style>
  1. index.ts下添加 声明为全局组件
import ElButton from './Button.vue'
import {App} from 'vue'

export default {
  install(app:App){
    app.component(ElButton.name,ElButton)
  }
}
  1. main.ts文件下使该组件生效
import { createApp } from 'vue'
import './style.css'
import App from './App.vue'
import ElContainer from './components/container'
import ElButton from './components/button'

const app=createApp(App)
app.use(ElContainer)
app.use(ElButton)
app.mount('#app')
  1. 就可以直接去App.vue中使用了
<template>
<div>
    <el-button size="small" type="primary">按钮</el-button>
    <el-button >提交</el-button>
  </div>
</template>

最终效果:

如何用VUE3打造通用组件

到此为止,打造的两个简单的组件就完成啦~

结束语

本篇文章就到此为止啦,由于本人经验水平有限,难免会有纰漏,对此欢迎指正。如觉得本文对你有帮助的话,欢迎点赞收藏❤❤❤。要是您觉得有更好的方法,欢迎评论,提出建议!

转载自:https://juejin.cn/post/7271649090407251987
评论
请登录