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;
}
}
}
}
看下效果: