React&Nest.js全栈社区平台(五)——👋封装通用分页Service实现文章流与详情

简介: React&Nest.js全栈社区平台(五)——👋封装通用分页Service实现文章流与详情

前言

前面我们已经实现了文章发布与文章管理功能,今天我们来实现首页的文章流列表以及查看文章详情的功能。

往期文章

仓库地址

  • 切图仔做全栈:React&Nest.js 社区平台(一)——基础架构与邮箱注册、JWT 登录实现
  • 切图仔做全栈:React&Nest.js社区平台(二)——👋手把手实现优雅的鉴权机制
  • React&Nest.js全栈社区平台(三)——🐘对象存储是什么?为什么要用它?
  • React&Nest.js社区平台(四)——✏️文章发布与管理实战

后端实现

对于文章列表来说,无论前端以什么样的形式去展现,表格也好,滚动刷新的列表也好,它本质上是一个分页的需求。

对于分页的需求相信每个前端都不陌生,你平时对接的时候把页码跟每一页的条数提供给后端后,后端会根据这些信息取出对应的条数返回给前端。

比如下面的 sql 语句,它指的是在 users 表中,从 第10条 开始,取 10条 数据,大多数的分页场景都是基于这个 sql 语句。

SELECT * FROM users offset 10 limit 10;

这一期我们会封装一个通用的分页 service ,接受任意一个 entity 对象,去查询分页信息。

通用分页Service封装

首先先定义一下分页 service 的返回值:

export class PaginationResult<T> {
  total: number;
  pageSize: number;
  currentPage: number;
  totalPage: number;
  isEnd: boolean;
  list: T[];
}

它接收一个范型参数 T ,对应的是传入的 entity 对象,其他的参数解释如下:

  • total :总条数
  • pageSize :每页条数
  • currentPage :当前页
  • totalPage :总共有多少页
  • isEnd :是否还有下一页
  • list :查询出来的列表对象

整个分页 service 实现如下:

export class PaginationService<T> {
  constructor(private repository: Repository<T>) {}

  async paginate(params: {
    page: number;
    pageSize: number;
    options?: FindOneOptions<T>;
  }): Promise<PaginationResult<T>> {
    const { page, pageSize, options = {} } = params;
    const [result, total] = await this.repository.findAndCount({
      take: pageSize,
      skip: pageSize * (page - 1),
      ...options,
    });

    const paginationResult = new PaginationResult<T>();
    paginationResult.list = result;
    paginationResult.total = total;
    paginationResult.pageSize = pageSize;
    paginationResult.currentPage = page;
    paginationResult.totalPage = Math.ceil(total / pageSize);
    paginationResult.isEnd = paginationResult.totalPage === page;

    return paginationResult;
  }
}

解释一下上面的代码:

  • 整个类接收一个范型对象 T ,它对应的是需要查询的 entity 对象
  • 构造函数中接收一个 repository ,它是范型 T(entity对象) 所对应的 repository
  • 首先接收页码参数 page ,每一页的条数 pageSize ,以及一个拓展查询条件 optionsoptions 的类型是 TypeORM 中的 FindOneOptions
  • 使用 findAndCount 查询出当前条件的条数以及结果,其中 take 对应原生 sql 语句的 limitskip 对应原生 sql 语句的 offset
  • 最后组装一下参数返回给调用方

文章列表接口

有了上面这个通用的分页器之后,我们可以在 artice.service 中实现一下获取分页文章列表的方法。

  async getArticles(params: { page: number; pageSize: number }) {
    const paginationService = new PaginationService<ArticleEntity>(
      this.articleRepository,
    );
    const res = await paginationService.paginate({
      ...params,
      options: {
        select: ['id', 'categoryId', 'introduction', 'title', 'creatorName'],
        where: { status: 1, isDeleted: 0 },
      },
    });
    return res;
  }

使用 ArticleEntity 跟注入的 articleRepositorynew 一个分页 service ,然后调用它的 paginate 方法。

这里把前端传过来的页码和每页的条数传进去,别忘了只能把已发布的文章 (status=1) 和未删除的文章查出来 (is_deleted=0)

值得一提的是,因为我们使用了范型以及对 options 定义了 FindOneOptions 这个类型,所以在开发的过程中, ts 的类型推断可以帮我们自动提示一些东西。

比如说我要选择某一些字段,在 select 数组中书写时,会对应提示 ArticleEntity 里有的字段,十分的方便。

image.png

整个接口的返回值如下图所示:

image.png

前端实现

前端方面使用 react-infinite-scroll-component 这个无限滚动组件配合 antd 的列表组件来实现一个滚动加载的分页列表。

      <div id="scrollableDiv" className={styles.list}>
        <InfiniteScroll
          dataLength={article.total}
          next={() => setPageNo(pageNo + 1)}
          hasMore={!article.isEnd}
          loader={<Skeleton avatar paragraph={{ rows: 1 }} active />}
          endMessage={<Divider plain>到底了~</Divider>}
          scrollableTarget="scrollableDiv"
        >
          <List
            dataSource={article.list}
            renderItem={(item: any) => (
              <List.Item
                className={styles.listItem}
                onClick={() => navigate(`/detail?id=${item.id}`)}
                key={item.id}
              >
                <List.Item.Meta
                  title={item.title}
                  description={
                    <>
                      <div style={{ margin: "8px 0" }}>{item.introduction}</div>
                      <Tag>{categoryMap[item.categoryId]}</Tag>
                    </>
                  }
                />
              </List.Item>
            )}
          />
        </InfiniteScroll>
      </div>

当页面滚动到底部时,会去拉取下一页的列表,同时根据我们上面 service 中返回的 isEnd 字段判断列表是否已经完全加载完毕。

image.png

image.png

有了文章列表之后必不可少的当然是文章详情,文章详情的接口就是根据文章 id 去查询一条记录,代码比较简单,这里就不放出来了。

前端要做的事情就是解析数据库里面存储的 markdown 内容为 html ,然后渲染到页面上,这里我使用的是 react-markdown 这个库,当然你可以搭配别的选择,或者自研一个。

至于样式什么的,就看你自己自由发挥了。

<div className={styles.content}>
    <ReactMarkdown children={article.content} />
</div>

image.png

最后

以上就是本文的全部内容,我们一起封装了一个通用的分页类,它可以方便的在各个 Entity 中实现分页的需求,然后我们还实现了文章流和文章详情,到这里,我们的社区平台基本上也算是做到了管理文章+查看文章的功能。

如果你觉得有意思的话,点点关注点点赞吧~欢迎在评论区交流

相关文章
|
2月前
|
前端开发 数据安全/隐私保护
crypto-js中AES的加解密封装
文章介绍了如何在前端使用crypto-js库进行AES加密和解密,提供了加解密的函数封装示例,并演示了如何加密和解密字符串或对象。
260 1
crypto-js中AES的加解密封装
|
2月前
|
前端开发 JavaScript 网络架构
react对antd中Select组件二次封装
本文介绍了如何在React中对Ant Design(antd)的Select组件进行二次封装,包括创建MSelect组件、定义默认属性、渲染Select组件,并展示了如何使用Less进行样式定义和如何在项目中使用封装后的Select组件。
89 2
react对antd中Select组件二次封装
|
19天前
|
监控 前端开发 JavaScript
React 静态网站生成工具 Next.js 入门指南
【10月更文挑战第20天】Next.js 是一个基于 React 的服务器端渲染框架,由 Vercel 开发。本文从基础概念出发,逐步探讨 Next.js 的常见问题、易错点及解决方法,并通过具体代码示例进行说明,帮助开发者快速构建高性能的 Web 应用。
51 10
|
18天前
|
资源调度 前端开发 数据可视化
构建高效的数据可视化仪表板:D3.js与React的融合之道
【10月更文挑战第25天】在数据驱动的时代,将复杂的数据集转换为直观、互动式的可视化表示已成为一项至关重要的技能。本文深入探讨了如何结合D3.js的强大可视化功能和React框架的响应式特性来构建高效、动态的数据可视化仪表板。文章首先介绍了D3.js和React的基础知识,然后通过一个实际的项目案例,详细阐述了如何将两者结合使用,并提供了实用的代码示例。无论你是数据科学家、前端开发者还是可视化爱好者,这篇文章都将为你提供宝贵的洞见和实用技能。
41 5
|
2月前
|
前端开发
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
本文介绍了在React项目中如何添加路径别名alias以简化模块引入路径,设置组件props的默认值,以及如何二次封装Ant Design的Modal组件。文章还提供了具体的代码示例,包括配置Webpack的alias、设置defaultProps以及封装Modal组件的步骤和方法。
67 1
React添加路径别名alias、接受props默认值、并二次封装antd中Modal组件与使用
|
1月前
|
前端开发
react 封装防抖
react 封装防抖
32 4
|
1月前
|
开发框架 前端开发 JavaScript
React、Vue.js 和 Angular主流前端框架和选择指南
在当今的前端开发领域,选择合适的框架对于项目的成功至关重要。本文将介绍几个主流的前端框架——React、Vue.js 和 Angular,探讨它们各自的特点、开发场景、优缺点,并提供选择框架的建议。
42 6
|
2月前
|
前端开发 JavaScript 开发者
React 和 Vue.js 框架的区别是什么?
React 和 Vue.js 框架的区别是什么?
|
2月前
|
前端开发 JavaScript API
React、Vue.js 和 Angular前端三大框架对比与选择
前端框架是用于构建用户界面的工具和库,它提供组件化结构、数据绑定、路由管理和状态管理等功能,帮助开发者高效地创建和维护 web 应用的前端部分。常见的前端框架如 React、Vue.js 和 Angular,能够提高开发效率并促进团队协作。
102 4