Skip to content

团队标准工程搭建实践(一)

主要事项

【封装基础组件(基于 Element Plus)】:input、button、select 等。

一、目标

搭建平台框架(子应用级别?),负责与后端进行 CRUD 交互。

  • 1)搭建基于 VUE3/TS 的项目框架;
  • 2)确定引入的 Element UI 版本,并基于此版本二次封装组件库;
  • 3)如何协调组件库的使用升级(基于 Element UI 二次封装的组件库)?
  • 4)与后端进行 CRUD 页面级别的 API 接口联调。
  • 5)...

二、搭建基于 VUE3/TS 的项目框架

基于 Vue Admin Plus

三、封装组件库

确定引入的 Element UI 版本,并基于此版本二次封装组件库。

1)确定引入的 Element UI 版本

引入 Element Plus 2.15.14(Aug 24, 2023)。

2)基于此 Element UI 版本,二次封装基本组件库

封装后,比如,el-input 使用调整为 yb-input。

语法特性(一)

【如何实现组件属性透传】我们在一个组件内部没有声明任何 prop 时,调用该组件,传入相关的属性,会直接将属性传到根节点上。

【验证~语法特性(一)】以 el-button 为例,封装成 yb-button。

vue
<script lang="tsx">
// 1、二次封装(From:components/yb-button/index.vue)
import { defineComponent } from 'vue'
import { ElButton } from 'element-plus'
export default defineComponent({
  name: 'YbButton',
  components: { ElButton },
  props: {
    icon: {
      type: String,
      default: ''
    }
  },
  setup(props, { slots }) {
    return () => <el-button>{slots.default?.()}</el-button>
  }
})
</script>
vue
<template>
  <!-- 2、使用自定义button(From:views/basic/index.vue)-->
  <div>
    <yb-button type="primary" size="mini" plain>abc</yb-button>
  </div>
</template>
<script setup name="BasicIndex">
onMounted(() => {})
</script>
<style></style>
html
<!-- 3、页面呈现效果的代码片段如下 -->
<button
  aria-disabled="false"
  type="button"
  class="el-button el-button--primary el-button--mini is-plain"
>
  <!--v-if-->
  <span class="">abc</span>
</button>

如果把二次封装的基础组件外面再套一层 DIV,代码如下:

vue
<script lang="tsx">
// 1、二次封装(From:components/yb-button/index.vue)
import { defineComponent } from 'vue'
import { ElButton } from 'element-plus'
export default defineComponent({
  name: 'YbButton',
  components: { ElButton },
  props: {
    icon: {
      type: String,
      default: ''
    }
  },
  setup(props, { slots }) {
    return () => (
      <div>
        <el-button>{slots.default?.()}</el-button>
      </div>
    )
  }
})
</script>

那么页面最终的代码呈现效果如下(不符合预期):

html
<!-- 3、页面呈现效果的代码片段如下(透传的button属性都添加到最外层的DIV上面了,而不在具体的button上面) -->
<div type="primary" size="mini" plain="">
  <button aria-disabled="false" type="button" class="el-button">
    <!--v-if-->
    <span class="">abc</span>
  </button>
</div>

语法特性(二)

【如何实现组件属性透传】通过 vm.$attrs 进行透传。

官方对「vm.$attrs」的定义:包含了父作用域中不作为 prop 被识别 (且获取) 的 attribute 绑定 (class 和 style 除外)。当一个组件没有声明任何 prop 时,这里会包含所有父作用域的绑定 (class 和 style 除外),并且可以通过 v-bind="$attrs" 传入内部组件——在创建高级别的组件时非常有用。

vue
<script lang="tsx">
// 1、二次封装(From:components/yb-button/index.vue)
// === 使用 vm.$attrs
import { defineComponent } from 'vue'
import { ElButton } from 'element-plus'
export default defineComponent({
  name: 'YbButton',
  components: { ElButton },
  props: {
    icon: {
      type: String,
      default: ''
    }
  },
  setup(props, { slotsattrs }) {
    return () => (
      <div>
        <el-button {...attrs}>{slots.default?.()}</el-button>
      </div>
    )
  }
})
</script>

那么页面最终的代码呈现效果如下(基本符合预期):

html
<!-- 3、页面呈现效果的代码片段如下(使用了 attrs) -->
<div type="primary" size="mini" plain="">
  <button
    aria-disabled="false"
    type="button"
    class="el-button el-button--primary el-button--mini is-plain"
  >
    <!--v-if-->
    <span class="">abc</span>
  </button>
</div>

语法特性(三)

【如何实现组件属性透传】「inheritAttrs: false」的妙用

如果你不想要一个组件自动地继承 attribute,你可以在组件选项中设置 inheritAttrs: false。

vue
<script lang="tsx">
// 1、二次封装(From:components/yb-button/index.vue)
// === 使用 vm.$attrs
// === 使用 inheritAttrs: false,
import { defineComponent } from 'vue'
import { ElButton } from 'element-plus'
export default defineComponent({
  name: 'YbButton',
  components: { ElButton },
  inheritAttrs: false,
  props: {
    icon: {
      type: String,
      default: ''
    }
  },
  setup(props, { slotsattrs }) {
    return () => (
      <div>
        <el-button {...attrs}>{slots.default?.()}</el-button>
      </div>
    )
  }
})
</script>

那么页面最终的代码呈现效果如下(符合预期):

html
<!-- 3、页面呈现效果的代码片段如下(使用了 attrs 和 inheritAttrs: false) -->
<div>
  <button
    aria-disabled="false"
    type="button"
    class="el-button el-button--primary el-button--mini is-plain"
  >
    <!--v-if-->
    <span class="">abc</span>
  </button>
</div>

语法特性(四)

【如何实现组件事件透传】$listeners 合并到 $attrs。

vue
<script lang="tsx">
// 1、二次封装(From:components/yb-button/index.vue)
import { defineComponent } from 'vue'
import { ElButton } from 'element-plus'
export default defineComponent({
  name: 'YbButton',
  components: { ElButton },
  inheritAttrs: false,
  props: {
    icon: {
      type: String,
      default: ''
    }
  },
  setup(props, { slotsattrs }) {
    console.log('🚀 ~ setup ~ attrs:', attrs)
    return () => (
      <div>
        <el-button {...attrs} v-on="$attrs">
          {slots.default?.()}
        </el-button>
      </div>
    )
  }
})
</script>
vue
<!-- 2、使用自定义button -->
<template>
  <div>
    <yb-button type="primary" size="mini" plain @custom="custom('123')">
      abc
    </yb-button>
  </div>
</template>
<script setup name="BasicIndex">
// 注意:这里自定义了一个事件,封装组件能在 $attrs 里面监听到。
const custom = (num) => {
  console.log(num)
}
</script>
<style></style>

An image

语法特性(五)

【如何实现组件属性透传】渲染函数:TODO

3)封装业务组件

基于二次封装的组件库,封装特定的业务组件(比如列表页面、详情页面等)。

三、注意事项