无星的前端之旅(二十二)—— 自定义CompositionAPI/hook

背景

我们做的大量都是后台管理系统,对于这种系统,我们会发现,里面的业务其实相当单一。

大量都是类似这种, 搜索条件,表格,分页

1.png

每个页面都要处理上下翻页,搜索,mounted加载首页table数据等等逻辑

虽然不复杂,但很累。

因此,我们需要封装一些DSL来降低我们的重复工作量

啥是CompositionAPI/hook

看看文档

我们的请求经过封装,大概是这种格式

// /api/index.js
import API from '@szyx/axios';
// 请求拦截器
API.interceptors.request.use((config) => {
  // xxxx
  return config;
});

// 结果拦截器
API.interceptors.response.use(
  (response) => {
    // 判断状态
    if (response.status === '0') {
      return response.data;
    }
    // 否则都是错了
    throw new Error(JSON.stringify(response));
  },
  (error) => {
    throw error;
  },
);
export default {
  getExample: (params) => API.POSTJSON('/xxx/xxx/xxx', params),
};

提取CompositionAPI

tableCompositions.js

import { ref, onMounted } from 'vue';
import API from '@/api/index';

export default function tableCompositon(params, urlName, pageDefault = 1, pageSizeDefault = 10) {
  // 表格数据
  const tableData = ref([]);
  // 总量
  const total = ref(0);
  // 当前页面,默认1
  const page = ref(pageDefault);
  // 页面数量,默认10
  const pageSize = ref(pageSizeDefault);

  const getTableData = () => {
    API[urlName]({
      ...params,
      pageNum: page.value,
      pageSize: pageSize.value,
    }).then((data) => {
      tableData.value = data.list;
      page.value = data.pageNum;
      pageSize.value = data.pageSize;
      total.value = data.total;
    });
  };
  // 页面加载请求
  onMounted(() => { getTableData(); });

  return {
    tableData,
    total,
    page,
    pageSize,
    getTableData,
  };
}

参数解析

  • params入参是指当前页面除了页码分页数量以外还需要的业务参数
  • urlName是指请求的方法名,上面可以看到我们的请求封装完以后的导出都是一个个的方法,例如调用getExample方法,则传递getExample这个字符串即可。

return解析

  • tableData 页面的表格数据,表格肯定是数组类型的数据
  • total 一共多少条数据
  • page 当前页码
  • pageSize 当前分页数
  • getTableData 获取表格数据的方法

内容解析

应该很简单,都能看懂,定义了一些数据,一个请求方法,还有onMounted中发起请求,然后return了这些东西出去

使用

比如像这样的一个页面

2.png

省略掉表单弹窗相关的代码

<template>
  <div class="flex row align-items-center header">
    <div class="flex row align-items-center flex1">
      <p class="title">用户名:</p>
      <el-input
        v-model="nameState.username"
        placeholder="用户名称"
        class="name-input"
        @keyup.enter="getTableData"
      />
    </div>

    <div class="flex row ">
      <el-button type="primary" @click="page.value = 1;
                                        getTableData();">
        搜索
      </el-button>
      <el-button plain @click="nameState.username = '';getTableData();">清空</el-button>
    </div>
  </div>
  <div class="table">
    <el-table :data="tableData" border stripe
              :header-cell-style="tableHeaderStyle" :cell-style="tableCellStyle"
    >
      <el-table-column prop="name" label="用户名" align="center" />
      <el-table-column prop="roleName" label="角色名称" align="center" />
      <el-table-column label="操作" align="center">
        <template #default="scope">
          <el-button type="primary" v-permission="'100001002'" @click="editUser(scope.row)">
            修改
          </el-button>
          <el-button type="danger" v-permission="'100001003'" @click="deleteUser(scope.row)">
            删除
          </el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
  <Pagination
    class="pagination"
    v-model:total="total"
    v-model:page="page"
    v-model:limit="pageSize"
    @pagination="getTableData"
  />
</template>

<script>
import {
  defineComponent, reactive,
} from 'vue';
import Pagination from '@/components/Pagination/index.vue';
import tableCompositions from '@/compositions/tableCompositions';

export default defineComponent({
  components: { Pagination },
  setup() {
    // 用户名称
    const nameState = reactive({
      username: '',
    });
    // 获取table数据
    const {
      tableData, getTableData, total, pageSize, page,
    } = tableCompositions(
      nameState,
      'getUserList',
    );

    return {
      total,
      pageSize,
      page,
      nameState,
      tableData,
      getTableData,
    };
  },
});
</script>

大概不到100行的代码,就结束了。

js基本没什么逻辑。只需要定义除pagepageSize以外还需要的业务参数

比如我这个页面还需要一个用户名

  const nameState = reactive({
      username: '',
  });

然后将nameState作为参数传递给tableCompositions,获取所有的东西

  const {
      tableData, getTableData, total, pageSize, page,
    } = tableCompositions(
      nameState,
      'getUserList',
    );

然后tableData直接丢给el-table就行了

pagination

至于pagination,其实直接用el-pagination就可以了

我们也只是把一些常用的放进去,包了一层而已

Pagination/index.vue

<template>
  <el-pagination
    :background="background"
    v-model:current-page="currentPage"
    v-model:page-size="pageSize"
    :layout="layout"
    :page-sizes="pageSizes"
    :total="total"
    v-bind="$attrs"
    @size-change="handleSizeChange"
    @current-change="handleCurrentChange"
  />
</template>

<script>
export default {
  name: 'Pagination',
  props: {
    total: {
      required: true,
      type: Number,
    },
    page: {
      type: Number,
      default: 1,
    },
    limit: {
      type: Number,
      default: 20,
    },
    pageSizes: {
      type: Array,
      default() {
        return [5, 10, 20, 30, 50];
      },
    },
    layout: {
      type: String,
      default: 'total, sizes, prev, pager, next, jumper',
    },
    background: {
      type: Boolean,
      default: true,
    },

    hidden: {
      type: Boolean,
      default: false,
    },
  },
  computed: {
    currentPage: {
      get() {
        return this.page;
      },
      set(val) {
        this.$emit('update:page', val);
      },
    },
    pageSize: {
      get() {
        return this.limit;
      },
      set(val) {
        this.$emit('update:limit', val);
      },
    },
  },
  methods: {
    handleSizeChange(val) {
      console.log('handleSizeChange', val);
      this.$emit('pagination', { page: this.currentPage, limit: val });
    },
    handleCurrentChange(val) {
      console.log('handleCurrentChange', val);
      this.$emit('pagination', { page: val, limit: this.pageSize });
    },
  },
};
</script>

所以上下页的逻辑,往上一绑,就结束了

  <Pagination
    class="pagination"
    v-model:total="total"
    v-model:page="page"
    v-model:limit="pageSize"
    @pagination="getTableData"
  />

react也一样,我这里就不放react代码了,可以自己封装一下

我们的宗旨只有一个:懒

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,214评论 6 481
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,307评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 152,543评论 0 341
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,221评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,224评论 5 371
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,007评论 1 284
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,313评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,956评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,441评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,925评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,018评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,685评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,234评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,240评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,464评论 1 261
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,467评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,762评论 2 345

推荐阅读更多精彩内容