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 中实现分页的需求,然后我们还实现了文章流和文章详情,到这里,我们的社区平台基本上也算是做到了管理文章+查看文章的功能。

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

相关文章
|
1月前
|
前端开发 JavaScript 测试技术
React 分页组件 Pagination
本文介绍了如何在 React 中从零构建分页组件,涵盖基础概念、常见问题及解决方案。通过示例代码详细讲解了分页按钮的创建、分页按钮过多、初始加载慢、状态管理混乱等常见问题的解决方法,以及如何避免边界条件、性能优化和用户反馈等方面的易错点。旨在帮助开发者更好地理解和掌握 React 分页组件的开发技巧,提升应用的性能和用户体验。
66 0
|
3月前
|
前端开发 数据安全/隐私保护
crypto-js中AES的加解密封装
文章介绍了如何在前端使用crypto-js库进行AES加密和解密,提供了加解密的函数封装示例,并演示了如何加密和解密字符串或对象。
311 1
crypto-js中AES的加解密封装
|
2月前
|
JavaScript 前端开发
JavaScript分页功能
JavaScript分页功能
|
1月前
|
前端开发 UED 开发者
React 数据表格分页实现
本文详细介绍了如何在React中实现数据表格的分页功能,包括基础实现、常见问题及解决方案。通过状态管理和事件处理,我们可以有效地减少页面加载时间,提升用户体验。文章提供了完整的代码示例,帮助开发者解决分页按钮样式、按钮过多和初始加载慢等问题,并给出了相应的优化方案。
99 53
|
4月前
|
资源调度 JavaScript Linux
【Azure 应用服务】本地Node.js部署上云(Azure App Service for Linux)遇到的三个问题解决之道
【Azure 应用服务】本地Node.js部署上云(Azure App Service for Linux)遇到的三个问题解决之道
|
20天前
|
前端开发 JavaScript 关系型数据库
基于 Vue2.0 + Nest.js 全栈开发的后台应用
Vue2 Admin 是一个基于 Vue2 和 Ant Design Pro 开发的前端项目,配合 Nest.js 构建的后端,提供了一个完整的全栈后台应用解决方案。该项目支持动态国际化、用户权限管理、操作日志记录等功能,适合全栈开发者学习参考。线上预览地址:https://vue2.baiwumm.com/,用户名:Admin,密码:abc123456。
|
29天前
|
前端开发 UED 开发者
React 分页组件 Pagination
本文介绍了如何在 React 中实现分页组件,从基础概念到常见问题及解决方案。分页组件用于将大量数据分成多个页面,提升用户体验。文章详细讲解了分页组件的基本结构、快速入门步骤、以及如何处理页面跳转不平滑、页码过多导致布局混乱、边界条件处理和数据加载延迟等问题。通过本文,读者可以全面了解并掌握 React 分页组件的开发技巧。
33 2
|
2月前
|
消息中间件 JavaScript 前端开发
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
用于全栈数据流的 JavaScript、Node.js 和 Apache Kafka
47 1
|
2月前
|
前端开发 JavaScript 程序员
【从前端入门到全栈】Node.js 之核心概念
【从前端入门到全栈】Node.js 之核心概念