next.js搭建博客系统-08-ssr渲染首页文章列表

192 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第9天,点击查看活动详情

文章系列:

next.js搭建博客系统-01-环境搭建

next.js搭建博客系统-02-实现Layout布局

next.js搭建博客系统-03-登录组件(登录弹窗)

next.js搭建博客系统-04-登录模块(验证码和登录)

next.js搭建博客系统-05-数据库操作

next.js搭建博客系统-06-发布文章(1)

next.js搭建博客系统-07-发布文章(2)

next.js搭建博客系统-08-ssr渲染首页文章列表

next.js搭建博客系统-09-ssr渲染文章详情页

next.js搭建博客系统-10-发布评论

next.js搭建博客系统-11-标签管理(1)

next.js搭建博客系统-12-标签管理(2)

nextjs 提供 getServerSideProps 来获取数据,返回到props中,然后在react组件中通过props获取数据进行渲染,达到ssr效果。

1.引入数据库和tag,article两张表

import { prepareConnection } from 'db/index';
import { Article, Tag } from 'db/entity';

2.链接数据库

const db = await prepareConnection();

3.根据 关联的 user和tag查询出 所有 文章

const articles = await db.getRepository(Article).find({
    relations: ['user', 'tags'],
  });

4.根据 关联的 user 查询出 标签

 const tags = await db.getRepository(Tag).find({
    relations: ['users'],
  });

5.最后将 文章和标签通过props返回

return {
    props: {
      articles: JSON.parse(JSON.stringify(articles)) || [],
      tags: JSON.parse(JSON.stringify(tags)) || [],
    },
  };

6.在react组件中 通过props获取 文章和标签

const { articles = [], tags = [] } = props;

7.默认将 获取的 文章,存放到所有文章的state中

const [showAricles, setShowAricles] = useState([...articles]);

8.然后渲染当前所有的文章

<div className="content-layout">
        {showAricles?.map((article) => (
          <>
            <DynamicComponent article={article} />
            <Divider />
          </>
        ))}
      </div>

9.上面的文章列表通过 异步加载的方式加载

const DynamicComponent = dynamic(() => import('components/ListItem'));

10.接下来 我们一起编写ListItem组件

新建 components/ListItem/index.tsx components/ListItem/index.module.scss

通过 props 可以获取到 从 父组件传过来的 article和 user信息

拿到这两个信息后,将这两个字段里面的内容 渲染处理即可

需要注意的是,需要点击谋篇文章的时候,跳转到该文章的详情页面,所以需要使用 Link

另外一个需要注意的地方是,渲染文章的时候,文章是markdown格式

所以使用 markdown-to-txt 第三方包 来加载 markdown格式的数据

所以代码是这样的

import Link from 'next/link';
import { formatDistanceToNow } from 'date-fns';
import { IArticle } from 'pages/api/index';
import { Avatar } from 'antd';
import { EyeOutlined } from '@ant-design/icons';
import { markdownToTxt } from 'markdown-to-txt';
import styles from './index.module.scss';

interface IProps {
  article: IArticle;
}

const ListItem = (props: IProps) => {
  const { article } = props;
  const { user } = article;

  return (
    // eslint-disable-next-line @next/next/link-passhref
    <Link href={`/article/${article.id}`}>
      <div className={styles.container}>
        <div className={styles.article}>
          <div className={styles.userInfo}>
            <span className={styles.name}>{user?.nickname}</span>
            <span className={styles.date}>
              {formatDistanceToNow(new Date(article?.update_time))}
            </span>
          </div>
          <h4 className={styles.title}>{article?.title}</h4>
          <p className={styles.content}>{markdownToTxt(article?.content)}</p>
          <div className={styles.statistics}>
            <EyeOutlined />
            <span className={styles.item}>{article?.views}</span>
          </div>
        </div>
        <Avatar src={user?.avatar} size={48} />
      </div>
    </Link>
  );
};

export default ListItem;
.container {
    margin: 0 atuo;
    background-color: #fff;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px;
    cursor: pointer;
  
    .article {
      width: 90%;
  
      .userInfo {
        margin-bottom: 10px;
        display: flex;
        align-items: center;
  
        span {
          padding: 0 10px;
          border-right: 1px solid #e5e6eb;
        }
  
        span:first-of-type {
          padding-left: 0;
        }
  
        span:last-of-type {
          border-right: 0;
        }
  
        .name {
          color: #4e5969;
        }
  
        .name:hover {
          text-decoration: underline;
          color: #1e80ff;
        }
  
        .date {
          color: #86909c;
        }
      }
  
      .title {
        font-size: 20px;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
  
      .content {
        font-size: 16px;
        color: #86909c;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
      }
  
      .statistics {
        display: flex;
        align-items: center;
  
        .item {
          margin-left: 5px;
        }
      }
    }
  }
  

看下效果: