Rumah  >  Artikel  >  hujung hadapan web  >  Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!

Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!

青灯夜游
青灯夜游asal
2022-12-23 19:23:592087semak imbas

Dalam proses membangunkan bahagian belakang pengurusan, anda pasti akan menghadapi banyak penambahan, pemadaman, pengubahsuaian dan halaman carian, dan kebanyakan logik halaman ini adalah sama. Ini akan membawa kepada peningkatan tahap gandingan kod . Di bawah, kami akan Data boleh guna semula diekstrak ke dalam cangkuk, yang bukan sahaja menyelesaikan masalah gandingan tetapi juga meningkatkan kecekapan kerja.

Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!

Dalam proses membangunkan bahagian belakang pengurusan, anda pasti akan menghadapi banyak halaman tambah, padam, ubah suai dan semak, dan logik halaman ini kebanyakannya sama , seperti mendapatkan data senarai, Fungsi asas seperti fungsi halaman dan penapisan. Perbezaannya ialah item data yang dibentangkan. Terdapat juga beberapa butang tindakan. [Cadangan berkaitan: tutorial video vuejs, pembangunan bahagian hadapan web]

Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!

Untuk apabila terdapat hanya 1 atau 2 halaman pada mulanya Kebanyakan pembangun boleh terus menyalin kod halaman sebelumnya dan membuat salinan tambahan Semasa projek berjalan, bilangan halaman yang serupa mungkin meningkat, yang secara langsung membawa kepada peningkatan tahap gandingan kod projek.

Ini juga merupakan salah satu sebab utama mengapa beberapa fungsi atau komponen boleh guna semula harus diekstrak daripada projek

Di bawah, kami merangkum useList am untuk menyesuaikan diri dengan kebanyakan Halaman senarai penambahan , pemadaman, pengubahsuaian dan pertanyaan membolehkan anda menyelesaikan tugas dengan lebih cepat dan lebih cekap serta berhenti kerja tepat pada masanya~

Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!

Pengetahuan prasyarat

Encapsulation

Kita perlu mengekstrak beberapa parameter dan fungsi biasa dan merangkumnya menjadi universal hook , Akan lebih mudah dan senang untuk menggunakan semula fungsi yang sama pada halaman lain kemudian.

Tentukan data paging penting untuk halaman senarai

export default function useList() {
  // 加载态
  const loading = ref(false);
  // 当前页
  const curPage = ref(1);
  // 总数量
  const total = ref(0);
  // 分页大小
  const pageSize = ref(10);
}

Cara mendapatkan data senarai

Fikirkan dan biarkan fungsi useList menerima listRequestFn parameter, Digunakan untuk meminta data daripada senarai.

Tentukan pembolehubah list untuk menyimpan kandungan data yang dikembalikan oleh permintaan rangkaian Memandangkan jenis data senarai tidak boleh ditentukan secara langsung, jenis data senarai disediakan secara luaran melalui generik.

export default function useList<ItemType extends Object>(
  listRequestFn: Function
) {
  // 忽略其他代码
  const list = ref<ItemType[]>([]);
}

Buat fungsi useList dalam loadData untuk memanggil fungsi pemerolehan data Fungsi ini menerima parameter untuk mendapatkan bilangan halaman data yang ditentukan (pilihan, nilai lalai ialah curPage. ).

  • Proses pelaksanaan
  1. Tetapkan status pemuatan
  2. Panggil fungsi yang diluluskan secara luaran dan tetapkan data yang diperolehi kepada list dan Dalam total
  3. tutup keadaan pemuatan

Sintaks async/wait digunakan di sini Jika terdapat ralat permintaan atau ralat penyahbinaan, blok kod tangkapan akan digunakan, dan kemudian keadaan pemuatan akan ditutup

Perlu diperhatikan di sini bahawa bilangan dan jenis parameter yang diterima oleh fungsi listRequestFn dihantar dengan betul. Sila laraskan mengikut situasi sebenar

export default function useList<ItemType extends Object>(
  listRequestFn: Function
) {
  // 忽略其他代码
  // 数据
  const list = ref<ItemType[]>([]);
  // 过滤数据
  // 获取列表数据
  const loadData = async (page = curPage.value) => {
    // 设置加载中
    loading.value = true;
    try {
      const {
        data,
        meta: { total: count },
      } = await listRequestFn(pageSize.value, page);
      list.value = data;
      total.value = count;
    } catch (error) {
      console.log("请求出错了", "error");
    } finally {
      // 关闭加载中
      loading.value = false;
    }
  };
}

Jangan lupa, terdapat juga switching paging untuk dikendalikan

Gunakan fungsi watch untuk memantau data, apabila curPage, pageSize Apabila nilai berubah, panggil fungsi loadData untuk mendapatkan data baharu.

export default function useList<ItemType extends Object>(
  listRequestFn: Function
) {
  // 忽略其他代码
  // 监听分页数据改变
  watch([curPage, pageSize], () => {
    loadData(curPage.value);
  });
}

Pemerolehan data senarai asas kini dilaksanakan

Penapis data dilaksanakan

Dalam senarai data yang besar, penapisan data ialah fungsi penting

Biasanya, saya akan mentakrifkan medan penapis dalam ref, dan hanya buang ref ke fungsi permintaan apabila meminta.

Dalam fungsi useList, parameter kedua menerima objek filterOption, sepadan dengan medan keadaan penapis dalam senarai.

Laraskan fungsi loadData dan masukkan objek filterOption dalam fungsi permintaan

Perhatikan bahawa nombor dan jenis parameter yang diterima oleh fungsi listRequestFn diluluskan dalam sepadan. antara satu sama lain secara normal Sila laraskan mengikut situasi sebenar

export default function useList<
  ItemType extends Object,
  FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
  const loadData = async (page = curPage.value) => {
    // 设置加载中
    loading.value = true;
    try {
      const {
        data,
        meta: { total: count },
      } = await listRequestFn(pageSize.value, page, filterOption.value);
      list.value = data;
      total.value = count;
    } catch (error) {
      console.log("请求出错了", "error");
    } finally {
      // 关闭加载中
      loading.value = false;
    }
  };
}

Perhatikan bahawa jenis parameter filterOption di sini memerlukan jenis ref, jika tidak responsif akan hilang dan tidak akan berfungsi dengan baik

Kosongkan medan penapis

Pada halaman, terdapat butang set semula untuk mengosongkan keadaan penapis. Tindakan berulang ini boleh dikendalikan oleh fungsi set semula.

Tetapkan semua nilai kepada undefined dengan menggunakan Reflect dan minta data sekali lagi.

什么是 Reflect?看看这一篇文章Reflect 映射对象

export default function useList<
  ItemType extends Object,
  FilterOption extends Object
>(listRequestFn: Function, filterOption: Ref<Object>) {
  const reset = () => {
    if (!filterOption.value) return;
    const keys = Reflect.ownKeys(filterOption.value);
    filterOption.value = {} as FilterOption;
    keys.forEach((key) => {
      Reflect.set(filterOption.value!, key, undefined);
    });
    loadData();
  };
}

导出功能

除了对数据的查看,有些界面还需要有导出数据功能(例如导出 csv,excel 文件),我们也把导出功能写到useList

通常,导出功能是调用后端提供的导出Api获取一个文件下载地址,和loadData函数类似,从外部获取exportRequestFn函数来调用Api

在函数中,新增一个exportFile函数调用它。

export default function useList<
  ItemType extends Object,
  FilterOption extends Object
>(
  listRequestFn: Function,
  filterOption: Ref<Object>,
  exportRequestFn?: Function
) {
  // 忽略其他代码
  const exportFile = async () => {
    if (!exportRequestFn) {
      throw new Error("当前没有提供exportRequestFn函数");
    }
    if (typeof exportRequestFn !== "function") {
      throw new Error("exportRequestFn必须是一个函数");
    }
    try {
      const {
        data: { link },
      } = await exportRequestFn(filterOption.value);
      window.open(link);
    } catch (error) {
      console.log("导出失败", "error");
    }
  };
}

注意,传入的 exportRequestFn 函数接收的参数数量和类型是否正常对应上 请根据实际情况进行调整

优化

现在,整个useList已经满足了页面上的需求了,拥有了获取数据,筛选数据,导出数据,分页功能

还有一些细节方面,在上面所有代码中的try..catch中的catch代码片段并没有做任何的处理,只是简单的console.log一下

提供钩子

useList新增一个 Options 对象参数,用于函数成功、失败时执行指定钩子函数与输出消息内容。

定义 Options 类型

export interface MessageType {
  GET_DATA_IF_FAILED?: string;
  GET_DATA_IF_SUCCEED?: string;
  EXPORT_DATA_IF_FAILED?: string;
  EXPORT_DATA_IF_SUCCEED?: string;
}
export interface OptionsType {
  requestError?: () => void;
  requestSuccess?: () => void;
  message: MessageType;
}

export default function useList<
  ItemType extends Object,
  FilterOption extends Object
>(
  listRequestFn: Function,
  filterOption: Ref<Object>,
  exportRequestFn?: Function,
  options? :OptionsType
) {
  // ...
}

设置Options默认值

const DEFAULT_MESSAGE = {
  GET_DATA_IF_FAILED: "获取列表数据失败",
  EXPORT_DATA_IF_FAILED: "导出数据失败",
};

const DEFAULT_OPTIONS: OptionsType = {
  message: DEFAULT_MESSAGE,
};

export default function useList<
  ItemType extends Object,
  FilterOption extends Object
>(
  listRequestFn: Function,
  filterOption: Ref<Object>,
  exportRequestFn?: Function,
  options = DEFAULT_OPTIONS
) {
  // ...
}

在没有传递钩子的情况霞,推荐设置默认的失败时信息显示

优化loadDataexportFile函数

基于 elementui 封装 message 方法

import { ElMessage, MessageOptions } from "element-plus";

export function message(message: string, option?: MessageOptions) {
  ElMessage({ message, ...option });
}
export function warningMessage(message: string, option?: MessageOptions) {
  ElMessage({ message, ...option, type: "warning" });
}
export function errorMessage(message: string, option?: MessageOptions) {
  ElMessage({ message, ...option, type: "error" });
}
export function infoMessage(message: string, option?: MessageOptions) {
  ElMessage({ message, ...option, type: "info" });
}

loadData 函数

const loadData = async (page = curPage.value) => {
  loading.value = true;
  try {
    const {
      data,
      meta: { total: count },
    } = await listRequestFn(pageSize.value, page, filterOption.value);
    list.value = data;
    total.value = count;
    // 执行成功钩子
    options?.message?.GET_DATA_IF_SUCCEED &&
      message(options.message.GET_DATA_IF_SUCCEED);
    options?.requestSuccess?.();
  } catch (error) {
    options?.message?.GET_DATA_IF_FAILED &&
      errorMessage(options.message.GET_DATA_IF_FAILED);
    // 执行失败钩子
    options?.requestError?.();
  } finally {
    loading.value = false;
  }
};

exportFile 函数

const exportFile = async () => {
  if (!exportRequestFn) {
    throw new Error("当前没有提供exportRequestFn函数");
  }
  if (typeof exportRequestFn !== "function") {
    throw new Error("exportRequestFn必须是一个函数");
  }
  try {
    const {
      data: { link },
    } = await exportRequestFn(filterOption.value);
    window.open(link);
    // 显示信息
    options?.message?.EXPORT_DATA_IF_SUCCEED &&
      message(options.message.EXPORT_DATA_IF_SUCCEED);
    // 执行成功钩子
    options?.exportSuccess?.();
  } catch (error) {
    // 显示信息
    options?.message?.EXPORT_DATA_IF_FAILED &&
      errorMessage(options.message.EXPORT_DATA_IF_FAILED);
    // 执行失败钩子
    options?.exportError?.();
  }
};

useList 使用方法

<template>
  <el-collapse>
    <el-collapse-item title="筛选条件" name="1">
      <el-form label-position="left" label-width="90px" :model="filterOption">
        <el-row :gutter="20">
          <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
            <el-form-item label="用户名">
              <el-input
                v-model="filterOption.name"
                placeholder="筛选指定签名名称"
              />
            </el-form-item>
          </el-col>
          <el-col :xs="24" :sm="12" :md="8" :lg="8" :xl="8">
            <el-form-item label="注册时间">
              <el-date-picker
                v-model="filterOption.timeRange"
                type="daterange"
                unlink-panels
                range-separator="到"
                start-placeholder="开始时间"
                end-placeholder="结束时间"
                format="YYYY-MM-DD HH:mm"
                value-format="YYYY-MM-DD HH:mm"
              />
            </el-form-item>
          </el-col>
          <el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
            <el-row class="flex mt-4">
              <el-button type="primary" @click="filter">筛选</el-button>
              <el-button type="primary" @click="reset">重置</el-button>
            </el-row>
          </el-col>
        </el-row>
      </el-form>
    </el-collapse-item>
  </el-collapse>
  <el-table v-loading="loading" :data="list" border style="width: 100%">
    <el-table-column label="用户名" min-width="110px">
      <template #default="scope">
        {{ scope.row.name }}
      </template>
    </el-table-column>
    <el-table-column label="手机号码" min-width="130px">
      <template #default="scope">
        {{ scope.row.mobile || "未绑定手机号码" }}
      </template>
    </el-table-column>
    <el-table-column label="邮箱地址" min-width="130px">
      <template #default="scope">
        {{ scope.row.email || "未绑定邮箱地址" }}
      </template>
    </el-table-column>
    <el-table-column prop="createAt" label="注册时间" min-width="220px" />
    <el-table-column width="200px" fixed="right" label="操作">
      <template #default="scope">
        <el-button type="primary" link @click="detail(scope.row)"
          >详情</el-button
        >
      </template>
    </el-table-column>
  </el-table>
  <div v-if="total > 0" class="flex justify-end mt-4">
    <el-pagination
      v-model:current-page="curPage"
      v-model:page-size="pageSize"
      background
      layout="sizes, prev, pager, next"
      :total="total"
      :page-sizes="[10, 30, 50]"
    />
  </div>
</template>
<script setup>
import { UserInfoApi } from "@/network/api/User";
import useList from "@/lib/hooks/useList/index";
const filterOption = ref<UserInfoApi.FilterOptionType>({});
const {
  list,
  loading,
  reset,
  filter,
  curPage,
  pageSize,
  reload,
  total,
  loadData,
} = useList<UserInfoApi.UserInfo[], UserInfoApi.FilterOptionType>(
  UserInfoApi.list,
  filterOption
);
</script>

本文useList的完整代码在 github.com/QC2168/snip…

(学习视频分享:vuejs入门教程编程基础视频

Atas ialah kandungan terperinci Vue3 menulis halaman senarai seperti ini untuk menjadikan prestasi lebih baik dan lebih cekap!. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn