简单的低代码——组件封装

lxf2023-05-16 01:26:27

前言

对于我这种低级的前端来说,现在接触的工作内容基本上就是写业务。特别是针对表格之类的,那已经是常见的不能再常见的业务了。而对于我之前的一个项目,某个单页面涉及到的表格有三种,每个涉及到的业务功能都不同。我当时虽然有想封装一个组件去写,奈何当时太菜了没有好的思路去封装,最后无奈硬写了三个表格。

现在想起来挺蠢的,现在我就来弥补当时的遗憾,对这种表格进行一个简单的封装。

技术栈

这里没有用什么高大上的技术,简单常见的就是最好的

技术栈vue3 + element-plus

组件的入口

我这里想做的是类似element-plus一样的可以像插件一样引入的类型,不是vue直接引入。

其实也挺简单的,无非就是想办法将我们写的组件抛出去,能够在vue实例里面注册使用

我们先来看看目录结构

简单的低代码——组件封装 这里的结构也不复杂,packages是我们整个插件所在的文件,index.js为入口文件。剩下的就是每个组件——一个入口文件,一个组件的vue文件

那我们看看element-plus的源码是怎么写组件的入口文件的

button组件的入口文件

简单的低代码——组件封装 我们简单的解读一下,这个文件里引入了组件的vue文件,然后通过他写的注册方法进行注册

注册方法:

简单的低代码——组件封装

这个函数接收到组件后直接对这个组件调用了.install方法,我一开始也没太懂,于是我就去看vue的官方文档,我们来看看官方文档的解释:插件 | Vue.js (vuejs.org)。

简单的低代码——组件封装

简单来说我们的插件可以调用install方法接收app实例对象,然后进行app.component()注册或者传递给app.use()来使用插件

接下来我们试试

// QueryForm入口文件
import QueryForm from '../QueryForm/QueryForm.vue'

QueryForm.install = (app) => {
  app.component('QueryForm', QueryForm)
}
export default QueryForm

我们直接在main.js引入然后app.use就能使用,但是我们的组件不止一个,我们就要把所有的组件在统一的入口文件里引入然后导出,于是:

// package/index.js
import QueryForm from './QueryForm'
import BaseTable from './BaseTable'

export default {
  install(app) {
    app.use(QueryForm)
    app.use(BaseTable)
  }
}

我们在项目的main.js引入使用

...
import Rocket from './../packages'

...
app.use(Rocket)

看看效果

简单的低代码——组件封装 这两块就是我进行替换的两个组件,看起来没问题很成功。

小总结

这里我们做的也就是把组件从简单的单项目里面编写导入变成了可以在其他项目中引入使用的通用的组件,我们做低代码就需要这样做通用性强的组件框架

QueryForm组件的编写

现在开始就到了我们的组件编写阶段了,我们也是基于element-plus进行封装的。我们需要注意的是组件之间的传值和事件触发。

这里其实也就是组件的通信

我们知道,在vue中组件的传递无非就是父组件通过:参数名="参数值"以及@方法名="方法"向子组件传递参数和事件,而子组件通过props和emit来接收参数和事件。

通过v-model传递的参数因为没有给参数名我们在接受的时候会有一个默认的名称modelValue

而每个参数都会默认自带一个事件:数据更新——update:参数名,如果我们只想单纯的更新数据,那么我们就不需要自己单独写事件,直接使用这个就好。

接下来我们就来写组件

<!--  -->
<template>
  <el-form ref="queryForm" :inline="true" :model="queryModel">
    <template v-for="(item, index) in form" :key="index">
      <FormItem :item="item" v-bind="item" v-model="queryModel[item.model]" />
    </template>
    <el-form-item>
      <el-button type="primary" @click="handleQuery">查询</el-button>
      <el-button @click="handleReset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup>
/**
 * from = [
 *  {
 *    type: 'input',
 *    model: 'userId',
 *    label: '用户Id',
 *    placeholder: "请输入用户ID"
 *  }
 * ]
 */
import { getCurrentInstance, reactive } from "vue";
import FormItem from "./FormItem.vue";
const props = defineProps(["modelValue", "form"]);
const emit = defineEmits(["update:modelValue", "handleQuery"]);

// ctx可以与vue2的$起到相同的作用
const ctx = getCurrentInstance();
const form = props.form;
const queryModel = reactive({
  ...props.modelValue
});

const handleReset = () => {
  ctx.refs.queryForm.resetFields();
};

const handleQuery = () => {
  // 都是用来更新数据的方法,一个是使用默认事件,一个是我们自己手写的事件
  emit("update:modelValue", { ...queryModel });
  emit("handleQuery", { ...queryModel });
};
</script>
<style lang="scss" scoped></style>

这里接受的两个参数

  1. modelValue: 用来进行输入框之类的进行数据绑定的
  2. form: 每个formItem的结构数据

这里有个写法,item里面有很多需要绑定的属性,一个个来绑定太麻烦了,v-bind可以直接绑定item里面所有的属性。

这里因为每个formItem都是不定的,所以我们又写了一个formItem组件来进行渲染

<!--  -->
<template>
  <el-form-item :prop="item.model">
    <el-input v-if="item.type == 'input'" v-bind="$attrs" />
    <el-select v-else-if="item.type == 'select'" v-bind="$attrs">
      <el-option
        v-for="option in item.options"
        :key="option.value"
        v-bind="option"
      />
    </el-select>
  </el-form-item>
</template>

<script setup>
import {} from "vue";
const props = defineProps(["item"]);
const emit = defineEmits([]);
</script>
<style lang="scss" scoped></style>

这里有个$attrs,我们直接看一看官方解释官方解释

简单的低代码——组件封装 因为这里我们又封装了一个组件,就又需要进行数据和事件的绑定再写一遍很麻烦,我们直接使用$attrs来进行透传把所有的属性、数据、事件都绑定上。

小总结

这样我们就实现了一个简单的组件就可以根据json数据直接生成横向的搜索栏表单。

BaseTable组件的编写

这里的编写跟上面的差不多,就不多赘述了。

<template>
  <div class="base-table">
    <div class="action">
      <slot name="action"></slot>
    </div>
    <el-table v-bind="$attrs">
      <template v-for="item in columns" :key="item.prop">
        <el-table-column
          type="selection"
          width="55"
          v-if="item.type == 'selection'"
        />
        <el-table-column v-else-if="!item.type" v-bind="item" />
        <el-table-column v-if="item.type == 'action'" v-bind="item">
          <template #default="scope">
            <template v-for="(btn, index) in item.list" :key="index">
              <el-button
                :type="btn.type || 'text'"
                size="small"
                @click="handleAction(index, scope.row)"
                v-if="btn.visible"
              >
                {{ btn.text }}
              </el-button>
            </template>
          </template>
        </el-table-column>
      </template>
    </el-table>
    <el-pagination
      class="pagination"
      background
      layout="prev, pager, next"
      :total="pager.total"
      :page-size="pager.pageSize"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script setup>
import {} from "vue";

const props = defineProps(["columns", "pager"]);
const emit = defineEmits(["handleAction", "handleCurrentChange"]);
/**
 * 列按钮点击
 * @param {number} index 索引
 * @param {object} row 行数据
 */
const handleAction = (index, row) => {
  emit("handleAction", { index, row: { ...row } });
};

/**
 * 页码变化
 * @param  {number} pageNum 页码
 */
const handleCurrentChange = (pageNum) => {
  emit("handleCurrentChange", pageNum)
}
</script>
<style lang="scss" scoped></style>

总结

这样的低代码封装可以用来简化我们的开发流程,提高开发效率。我写的这个也只是个较为简单的表格组件,其实表格能实现的还有很多。我写这个组件也是自己进步的一个过程,从简单的业务到相对复杂的组件封装,期待我以后能封装更大更复杂的组件甚至框架。

我现在还是比较菜的,文章里面写的不对的或者我理解有误的希望大家能对我进行指正和点名,也是我发现自己不足的好办法。

本网站是一个以CSS、JavaScript、Vue、HTML为核心的前端开发技术网站。我们致力于为广大前端开发者提供专业、全面、实用的前端开发知识和技术支持。 在本网站中,您可以学习到最新的前端开发技术,了解前端开发的最新趋势和最佳实践。我们提供丰富的教程和案例,让您可以快速掌握前端开发的核心技术和流程。 本网站还提供一系列实用的工具和插件,帮助您更加高效地进行前端开发工作。我们提供的工具和插件都经过精心设计和优化,可以帮助您节省时间和精力,提升开发效率。 除此之外,本网站还拥有一个活跃的社区,您可以在社区中与其他前端开发者交流技术、分享经验、解决问题。我们相信,社区的力量可以帮助您更好地成长和进步。 在本网站中,您可以找到您需要的一切前端开发资源,让您成为一名更加优秀的前端开发者。欢迎您加入我们的大家庭,一起探索前端开发的无限可能!