Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!

青灯夜游
풀어 주다: 2022-12-23 19:24:51
원래의
2161명이 탐색했습니다.

관리 백엔드를 개발하는 과정에서 페이지 추가, 삭제, 수정 및 확인을 많이 접하게 되며, 이러한 페이지의 논리는 대부분 동일하므로 아래에서 코드 결합 정도가 높아집니다. , 이러한 재현 가능한 페이지에 대해 설명하겠습니다. 사용된 데이터는 후크로 추출되어 결합 문제를 해결할 뿐만 아니라 작업 효율성도 향상시킵니다.

Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!

관리 백엔드를 개발하는 과정에서 페이지 추가, 삭제, 수정, 확인을 많이 접하게 되는데 목록 가져오기 등 기본 기능 등 페이지의 로직은 대부분 동일합니다. 데이터, 페이징, 필터링 기능. 차이점은 제시된 데이터 항목입니다. 몇 가지 작업 버튼도 있습니다. [관련 권장사항: vuejs 동영상 튜토리얼, 웹 프론트엔드 개발]

Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!

처음에 1~2페이지만 있을 경우 대부분의 개발자는 이전 페이지 코드를 직접 복사하여 프로젝트로 사용할 수 있습니다. 진행됨에 따라 유사한 페이지 수가 증가할 수 있으며 이는 직접적으로 프로젝트 코드 결합 정도의 증가로 이어집니다.

이것은 일부 재사용 가능한 기능이나 구성요소를 프로젝트에서 추출해야 하는 주요 이유 중 하나이기도 합니다

아래에서는 대부분의 추가, 삭제, 수정 및 쿼리에 적응하기 위해 일반 useList를 캡슐화합니다. 목록 페이지를 사용하면 보다 빠르고 효율적으로 작업을 완료하고 정시에 퇴근할 수 있습니다~useList,适配大多数增删改查的列表页面,让你更快更高效的完成任务,准点下班 ~

Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!

前置知识

封装

我们需要将一些通用的参数和函数抽离出来,封装成一个通用hook,后续在其他页面复用相同功能更加简单方便。

定义列表页面必不可少的分页数据

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

如何获取列表数据

思考一番,让useList函数接收一个listRequestFn参数,用于请求列表中的数据。

定义一个list变量,用于存放网络请求回来的数据内容,由于在内部无法直接确定列表数据类型,通过泛型的方式让外部提供列表数据类型。

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

useList中创建一个loadData函数,用于调用获取数据函数,该函数接收一个参数用于获取指定页数的数据(可选,默认为curPage的值)。

  • 执行流程
  1. 设置加载状态
  2. 调用外部传入的函数,将获取到的数据赋值到listtotal
  3. Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!
  4. 전제 지식

      Vue
Vue Composition Api

캡슐화

몇 가지 공통 매개변수와 함수를 추출하여 공통 훅<으로 캡슐화해야 합니다. /code>를 사용하면 나중에 다른 페이지에서 동일한 기능을 재사용하는 것이 더 쉽고 편리해집니다. <blockquote><h3 data-id="heading-3">목록 페이지에 대한 필수 페이징 데이터 정의<p><div class="code" style="position:relative; padding:0px; margin:0px;"><pre class="brush:js;toolbar:false;">export default function useList&lt;ItemType extends Object&gt;( listRequestFn: Function ) { // 忽略其他代码 // 数据 const list = ref&lt;ItemType[]&gt;([]); // 过滤数据 // 获取列表数据 const loadData = async (page = curPage.value) =&gt; { // 设置加载中 loading.value = true; try { const { data, meta: { total: count }, } = await listRequestFn(pageSize.value, page); list.value = data; total.value = count; } catch (error) { console.log(&quot;请求出错了&quot;, &quot;error&quot;); } finally { // 关闭加载中 loading.value = false; } }; }</pre><div class="contentsignin">로그인 후 복사</div></div><h3 data-id="heading-4">목록 데이터를 가져오는 방법</p></blockquote>생각해보고< code> useList 함수는 목록의 데이터를 요청하는 데 사용되는 listRequestFn 매개변수를 받습니다.

네트워크 요청에 의해 반환된 데이터 내용을 저장하기 위해 list 변수를 정의합니다. 목록 데이터 유형은 내부적으로 직접 결정할 수 없기 때문에 목록 데이터 유형은 제네릭을 통해 외부적으로 제공됩니다.

export default function useList<ItemType extends Object>(
  listRequestFn: Function
) {
  // 忽略其他代码
  // 监听分页数据改变
  watch([curPage, pageSize], () => {
    loadData(curPage.value);
  });
}
로그인 후 복사
watch 函数监听数据,当curPagepageSize的值发生改变时调用loadData函数获取新的数据。

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;
    }
  };
}
로그인 후 복사

现在实现了基本的列表数据获取

实现数据筛选器

在庞大的数据列表中,数据筛选是必不可少的功能

通常,我会将筛选条件字段定义在一个ref中,在请求时将ref丢到请求函数即可。

在 useList 函数中,第二个参数接收一个filterOption对象,对应列表中的筛选条件字段。

调整一下loadData函数,在请求函数中传入filterOptionuseListloadData 함수를 생성하여 데이터 수집 함수를 호출합니다. 이 함수는 지정된 수의 데이터 페이지를 얻기 위한 매개변수를 받습니다(선택 사항, 기본값은 curPage 값).

    실행 프로세스

    로딩 상태 설정
    외부 함수를 호출하고 얻은 데이터를 listtotal에 할당

    로드 상태 닫기

    여기에서는 async/await 구문이 사용됩니다. 요청 오류 또는 분해 오류가 발생하면 catch 코드 블록이 사용되며 로딩 상태가 닫힙니다

    undefined

    주의해야 합니다. 여기서는 수신된 listRequestFn 함수가 수신된 매개변수의 수와 유형이 제대로 일치하는지 여부입니다. 실제 상황에 따라 조정해주세요🎜🎜
    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();
      };
    }
    로그인 후 복사
    로그인 후 복사
    🎜아직 페이징 전환이 남아 있으니 잊지 마세요🎜🎜curPage 시 데이터를 모니터링하려면 watch 기능을 사용하세요. , pageSize< /code> 값이 변경되면 <code>loadData 함수를 호출하여 새로운 데이터를 얻어옵니다. 🎜
    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");
        }
      };
    }
    로그인 후 복사
    로그인 후 복사
    🎜기본 목록 데이터 획득이 구현되었습니다🎜

    데이터 필터 구현🎜🎜거대한 데이터 목록에서 데이터 필터링은 필수 기능입니다🎜🎜일반적으로 I 필터 조건 필드는 ref에 정의하고 요청할 때 요청 함수에 ref를 던지기만 하면 됩니다. 🎜🎜useList 함수에서 두 번째 매개변수는 목록의 필터 조건 필드에 해당하는 filterOption 개체를 받습니다. 🎜🎜loadData 함수를 조정하고 요청 함수에 filterOption 개체를 전달합니다.🎜🎜🎜 전달된 listRequestFn 함수에서 수신한 매개변수의 개수와 유형이 제대로 대응하다 실제 상황에 따라 조정해주세요🎜🎜
    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
    ) {
      // ...
    }
    로그인 후 복사
    로그인 후 복사
    🎜🎜여기의 filterOption 매개변수 유형은 ref 유형이어야 합니다. 그렇지 않으면 응답성이 손실되어 제대로 작동할 수 없습니다.🎜🎜🎜필터 필드를 지우세요🎜🎜페이지에 필터 조건을 지우는 데 사용되는 재설정 버튼입니다. 이렇게 반복되는 작업은 재설정 기능으로 처리할 수 있습니다. 🎜🎜Reflect를 사용하여 모든 값을 🎜로 설정하고 데이터를 다시 요청하세요. 🎜

    什么是 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入门教程编程基础视频

    위 내용은 Vue3에서는 성능을 더 좋고 효율적으로 만들기 위해 이렇게 목록 페이지를 작성합니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿