Vue3+ts+element-plus 组件的二次封装-- 页脚分页el-pagination的二次封装

Vue 笔记

本人是一个web前端开发工程师,主要是vue框架,整理了一些Vue常用的技术,一方面是分享,一方面是做总结,今后也会一直更新,有好建议的同学欢迎评论区分享 ;-)

序号文章
0组件库展示
1通过JSON配置–头部搜索条件的封装组件
2通过JSON配置–表单Table的封装组件
3页脚的封装组件
4通过JSON配置–Form表单的封装组件
5生成npm组件库
6发布到npm
7vitest为组件库添加单元测试
8vuepress为组件库生成文档
9通过github或者gitee pages将组件库文档发布到线上,免费!
10源码地址

在这里插入图片描述


组件库开发流程

Vue组件库专栏会按顺序执行一下流程,不断完善组件库开发流程

  1. Vue3+element-plus+vite 组件的二次封装,封装了头部的搜索条件栏,tabel栏,分页栏,form表单,都设置成了通过json可配置项,方便复用;
  2. 封装好了就开始打包,并且进行本地测试;
  3. 组件库发布到npm;
  4. 添加vitest单元测试框架;
  5. 添加vuepress文档。


前言

环境状态
vue版本:vue3
是否使用 ts:是

后台管理系统的网站,一个页面无非就是4个常用业务块

  1. 头部的搜索栏
  2. table表格
  3. 页脚
  4. 新增编辑弹框

那咋们是不是可以将其进行封装成组件呢?
只需要传入一个配置文件就可以了~

项目解构如下:
在这里插入图片描述

封装后的展示图如下:
在这里插入图片描述

效果图如下:

在这里插入图片描述

如果能做成这样,是不是页面就会整洁很多?


需求分析

一个管理系统的分页基本上样式以及布局都是一样的,不然也没必要封装一层,这边主要需要3个属性,以及1个事件。

  1. total 传递数据总数量,这个是单向数据流,是后台返回的数据量
  2. pageNum传递当前页码,这个是需要双向绑定的,因为这个是父组件初始化,后期根据用户操作 elementUI 返回新的页码,这个主要用于后台请求数据的一个参数。
  3. pageSize传递当前一页的大小,这个是需要双向绑定的,因为这个是父组件初始化,后期根据用户操作 elementUI 返回新的页码,这个主要用于后台请求数据的一个参数。
  4. 监听pageNum,pageSize的change事件,虽然也可以在父组件通过watch监听变量,不过那样代码就比较多了,可以直接监听change事件,触发就直接调用自定义的请求数据接口getList…

分页el-pagination的二次封装分析

1. 效果图

在这里插入图片描述


2. 查看elementUI组件的配置

element-plus官网

在这里插入图片描述

从中可以看出:

  1. 单向数据流total,是用来展示总共有多少条数据的,主要从父组件=>封装子组件=>elementUI组件,主要是由父组件控制。
  2. 双向数据绑定current-page,以及page-size,分别代表当前页码以及每一页的数据量。
  3. current-page,以及page-size,父组件只是负责初始化赋值,后期操作主要是elementUI,触发了update事件给了封装好的组件,组件再把update事件传递到了父组件,父组件修改该值。

在这里插入图片描述


3. 父组件

3.1 父组件 template 中的调用

<!-- 分页 -->
<PenkFooter
  :total="paginationData.total"
  v-model:pageNum="paginationData.pageNum"
  v-model:pageSize="paginationData.pageSize"
  @change="getList"
></PenkFooter>

可以看到,就配置了上述的4个属性即可。


3.2 父组件 script 中的调用

这边用一个对象 paginationData,存放分页组件的数据,pageNum以及pageSize是双向数据绑定,所以不需要怎么操作,直接调用即可;
{
pageNum: paginationData.pageNum,
pageSize: paginationData.pageSize,
}

total的话也只是需要每次根据条件查询,后端返回的总数据量,更新上去即可~~~
paginationData.total = res.count;

代码如下:

// 表格数据
let tableData = reactive([]);
// 分页数据
const paginationData = reactive({
  total: 100,
  pageNum: 1,
  pageSize: 10,
});
// 查找数据
async function getList() {
  // 清空数据
  tableData.length = 0;
  console.log("tableData1:", tableData);

  // 判断是否有对象,没有的话就自动弄
  let res = await http.getList({
    ...refPenkSearch.value.queryObj,
    pageNum: paginationData.pageNum,
    pageSize: paginationData.pageSize,
  });
  // @ts-ignore
  tableData.push(...res.rows);
  paginationData.total = res.count;

  console.log("tableData:", tableData);
}

tips:这边要注意reactive 创建的tabelData,不能使用直接赋值,因为是个proxy对象~


4. 组件的封装

双向数据绑定,这里使用了 v-model 语法糖,拆分开就是 v-bind绑定数据,负责传值;@update:xxx的监听事件,负责通知调用者更新值。

如果组件都是自己写的,完全可以用provide,inject来处理,但是,用了第三方的elementUI组件,所以比较繁琐。

  1. v-bind:父组件=>封装组件=>elementUI组件 已经打通
  2. @update:elementUI组件=>封装组件,封装组件=>父组件 打通。
  3. 封装组件 => 父组件还没打通! 需要监听封装组件中中间件,接收到了elementUI组件的update事件,再将事件传递到父组件。
  4. 并且父组件传递过去的props 是不能作为 封装组件的v-model 属性传递给elementUI组件的,所以这边还需要一个临时变量用于存储值。

vue官网
在这里插入图片描述


4.1 封装组件template

// 子组件的template
<el-pagination
  v-model:current-page="paginationData.pageNum"
  v-model:page-size="paginationData.pageSize"
  :page-sizes="[10, 30, 50, 100]"
  background
  layout="total, sizes, prev, pager, next, jumper"
  :total="total"
  @size-change="handleSizeChange"
  @current-change="handleCurrentChange"
/>

4.2 封装组件script

4.2.1 props属性
// 子组件的script
// 暴露props
interface props {
  // 总条目数
  total: number;
  // 当前页
  pageNum: number;
  // 页数
  pageSize: number;
}
const props = defineProps<props>();

let paginationData = reactive({
  pageNum: props.pageNum,
  pageSize: props.pageSize,
});
4.2.2 监听elementUI事件,并且触发update事件给父组件
// 子组件的script
import { ref, reactive, watch } from "vue";
// 生成事件对象,数组中就是对象名
const emit = defineEmits(["change", "update:pageSize", "update:pageNum"]);
// 事件,如果用v-model 其实不是很需要这数据...
function handleSizeChange(e: number) {
  emit("update:pageSize", e);
  emit("change", paginationData);
}
function handleCurrentChange(e: number) {
  emit("update:pageNum", e);
  emit("change", paginationData);
}

总结

封装很简单,只要将elementUI组件新的值,传递给父组件即可。


组件源码

<!--
 * @Author: Penk
 * @LastEditors: Penk
 * @LastEditTime: 2022-11-29 16:41:36
 * @FilePath: \front-master\src\components\public\PenkFooter.vue
 * @email: 492934056@qq.com
-->
<template>
  <div class="penk-footer">
    <el-pagination
      :total="total"
      v-model:current-page="paginationData.pageNum"
      v-model:page-size="paginationData.pageSize"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :page-sizes="[10, 30, 50, 100]"
      background
      layout="total, sizes, prev, pager, next, jumper"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, reactive, watch } from "vue";

// 暴露props
interface props {
  // 总条目数
  total: number;
  // 当前页
  pageNum: number;
  // 页数
  pageSize: number;
}
const props = defineProps<props>();

let paginationData = reactive({
  pageNum: props.pageNum,
  pageSize: props.pageSize,
});

// 生成事件对象,数组中就是对象名
const emit = defineEmits(["change", "update:pageSize", "update:pageNum"]);
// 事件,如果用v-model 其实不是很需要这数据...
function handleSizeChange(e: number) {
  emit("update:pageSize", e);
  emit("change", paginationData);
}
function handleCurrentChange(e: number) {
  emit("update:pageNum", e);
  emit("change", paginationData);
}
</script>

<style lang="less" scoped>
.penk-footer {
  margin-top: 20px;
  float: right;
}
</style>

  • 3
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
好的,我可以为你提供一个基本的ElDialog封装,供你参考: ```vue <template> <el-dialog :title="title" :visible.sync="dialogVisible" :before-close="handleClose" :close-on-click-modal="false" :custom-class="customClass" :width="width" :lock-scroll="lockScroll" :modal-append-to-body="modalAppendToBody" :destroy-on-close="destroyOnClose" :center="center" @opened="handleOpen" @closed="handleClosed" v-bind="$attrs" v-on="$listeners" > <slot></slot> </el-dialog> </template> <script lang="ts"> import { defineComponent } from 'vue'; import { ElDialog } from 'element-plus'; export default defineComponent({ name: 'MyDialog', props: { title: { type: String, default: '', }, dialogVisible: { type: Boolean, default: false, }, customClass: { type: String, default: '', }, width: { type: String, default: '50%', }, lockScroll: { type: Boolean, default: true, }, modalAppendToBody: { type: Boolean, default: true, }, destroyOnClose: { type: Boolean, default: false, }, center: { type: Boolean, default: true, }, }, emits: ['update:dialogVisible', 'opened', 'closed'], methods: { handleClose(done: () => void) { // 自定义关闭操作 done(); }, handleOpen() { this.$emit('opened'); }, handleClosed() { this.$emit('closed'); }, }, components: { ElDialog, }, }); </script> ``` 这里我们使用了Vue3的Composition API,使用`defineComponent`定义了一个组件,并引入了Element Plus的ElDialog组件。 我们将ElDialog组件的属性和事件通过props和emits暴露出来,并在组件内部进行了一些自定义操作,如自定义关闭操作和自定义事件触发。 你可以根据自己的需求对组件进行进一步封装和定制化。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Penk是个码农

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值