NextJS中实现文件系统生成Markdown帖子的随机与标签筛选
问题描述
使用NextJS开发产品展示项目,通过File System Module(fs)读取Markdown文件生成静态帖子页面。当前遇到两个问题:
- 无法实现随机帖子组件:尝试用lodash的
sampleSize实现,但未达到预期效果 - 无法实现按标签筛选帖子的功能
尝试的随机帖子代码:
const _ = require("lodash"); const randomizePost = data.map((post) => { const { title, path } = post; return ( <PostList key={`${title}`} title={title} path={path}></PostList> ); }); let result = _.sampleSize(randomizePost, 4); return <ul className="list-group post-list">{result}</ul>;
解决方案
一、修复随机帖子组件
你的问题出在先渲染组件再抽样,React元素是复杂对象,直接对其抽样可能导致渲染异常或性能问题。正确做法是先对原始帖子数据抽样,再渲染组件:
import _ from 'lodash'; // 假设data是从getStaticProps获取的所有帖子原始数据数组 const RandomPosts = ({ posts }) => { // 先对原始数据随机抽取4条 const randomPosts = _.sampleSize(posts, 4); return ( <ul className="list-group post-list"> {randomPosts.map(post => ( // 用path作为key(唯一且稳定),避免title重复导致的key冲突 <PostList key={post.path} title={post.title} path={post.path} /> ))} </ul> ); }; export default RandomPosts;
二、实现按标签筛选功能
要实现标签筛选,需要先建立标签-帖子的索引映射,步骤如下:
1. 在数据获取阶段提取标签并构建索引
在getStaticProps或专门的文件处理函数中,读取所有Markdown文件时,提取每个帖子的标签字段,然后整理成索引:
import fs from 'fs'; import path from 'path'; import matter from 'gray-matter'; // 假设用gray-matter解析Markdown FrontMatter const postsDirectory = path.join(process.cwd(), 'posts'); export const getAllPostsWithTags = () => { // 获取所有Markdown文件名 const fileNames = fs.readdirSync(postsDirectory); const allPostsData = fileNames.map(fileName => { // 移除文件扩展名 const id = fileName.replace(/\.md$/, ''); // 构建文件路径 const fullPath = path.join(postsDirectory, fileName); // 读取文件内容 const fileContents = fs.readFileSync(fullPath, 'utf8'); // 解析FrontMatter(假设你的Markdown里有tags字段) const matterResult = matter(fileContents); return { id, path: `/posts/${id}`, // 帖子的静态路径 title: matterResult.data.title, tags: matterResult.data.tags || [] // 提取标签,默认空数组 }; }); // 构建标签索引:{ tagName: [post1, post2, ...] } const tagIndex = allPostsData.reduce((index, post) => { post.tags.forEach(tag => { if (!index[tag]) { index[tag] = []; } index[tag].push(post); }); return index; }, {}); return { allPostsData, tagIndex }; };
2. 在筛选组件中使用标签索引
在页面组件中,获取标签索引和所有帖子,然后根据选中的标签过滤帖子:
import { useState } from 'react'; import { getAllPostsWithTags } from '../lib/posts'; const TagFilterPage = ({ allPostsData, tagIndex }) => { // 获取所有唯一标签 const allTags = Object.keys(tagIndex); // 选中的标签状态,默认显示全部 const [selectedTag, setSelectedTag] = useState('all'); // 根据选中标签筛选帖子 const filteredPosts = selectedTag === 'all' ? allPostsData : tagIndex[selectedTag] || []; return ( <div> {/* 标签选择器 */} <div className="tag-selector"> <button onClick={() => setSelectedTag('all')} className={selectedTag === 'all' ? 'active' : ''} > 全部 </button> {allTags.map(tag => ( <button key={tag} onClick={() => setSelectedTag(tag)} className={selectedTag === tag ? 'active' : ''} > {tag} </button> ))} </div> {/* 筛选后的帖子列表 */} <ul className="list-group post-list"> {filteredPosts.map(post => ( <PostList key={post.path} title={post.title} path={post.path} /> ))} </ul> </div> ); }; // 在getStaticProps中获取数据 export async function getStaticProps() { const { allPostsData, tagIndex } = getAllPostsWithTags(); return { props: { allPostsData, tagIndex } }; } export default TagFilterPage;
关键注意事项
- 确保Markdown文件的FrontMatter中包含
tags字段(比如tags: ["产品设计", "前端"]) - 标签索引要在构建静态页面时生成(
getStaticProps),避免客户端重复计算 - 随机帖子组件中,始终基于原始数据抽样,而非已渲染的React元素
内容的提问来源于stack exchange,提问作者Deny Farras Febriyanto
相关产品推荐
相关产品推荐

