我有一个网站next.js与mdx。有些文章有标签,它们在标题中被指定,比如tags: ['functions', 'javascript', 'powerful code', 'js']
。我也有标签路由-沿着路径pages/blog/tag/[tag].js
页面被创建,其中所有具有相应标签的文章被收集。
我的服务函数是这样的,它创建了在正确的路由上生成文章所需的一切:
import fs from 'fs';
import matter from 'gray-matter';
export function getAllPosts() {
const files = fs.readdirSync('./content/posts');
const posts = files
.map((fileName) => {
const slug = fileName.replace(/.mdx$/, '');
const { frontmatter } = getPostBySlug(slug);
return {
slug,
...frontmatter,
};
})
return posts;
}
export function getPostBySlug(slug) {
const fileName = fs.readFileSync(`content/posts/${slug}.mdx`, 'utf-8');
const { data: frontmatter, content } = matter(fileName);
return {
frontmatter,
content,
};
}
export function getAllWork() {
const data = fs.readFileSync('content/work/data.json', 'utf-8');
const jsonData = JSON.parse(data);
return jsonData.work;
}
export function getAllPostsByTag({ tag }) {
const posts = getAllPosts();
return posts.filter((post) => post.tags.includes(tag));
}
这是文件看起来像pages/blog/tag/[tag].js
:
import React, { useState, useEffect } from "react"
import {getAllPosts, getAllPostsByTag} from "@/lib/getAllData";
import Head from "next/head";
import Article from "@/components/article";
export async function getStaticPaths() {
const posts = getAllPosts();
const tags = new Set(posts.flatMap((post) => post.tags));
return {
paths: [...tags].map((tag) => {
return {
params: {
tag
}
}
}),
fallback: false,
};
}
export async function getStaticProps({ params: { tag } }) {
const posts = getAllPostsByTag({tag});
return {
props: {
posts,
tag
},
};
}
export default function Tag({ posts, tag }) {
const numberPosts = 2
// State for the list
const [list, setList] = useState([...posts.slice(0, numberPosts)])
// State to trigger oad more
const [loadMore, setLoadMore] = useState(false)
// State of whether there is more to load
const [hasMore, setHasMore] = useState(posts.length > numberPosts)
// Load more button click
const handleLoadMore = () => {
setLoadMore(true)
}
// Handle loading more articles
useEffect(() => {
if (loadMore && hasMore) {
const currentLength = list.length
const isMore = currentLength < posts.length
const nextResults = isMore
? posts.slice(currentLength, currentLength + numberPosts)
: []
setList([...list, ...nextResults])
setLoadMore(false)
}
}, [loadMore, hasMore]) //eslint-disable-line
//Check if there is more
useEffect(() => {
const isMore = list.length < posts.length
setHasMore(isMore)
}, [list]) //eslint-disable-line
return (
<div>
<Head>
<title>NextJS Blog</title>
<meta name="description" content="Generated by create next app" />
<link rel="icon" href="/public/favicon.ico" />
</Head>
<section className='px-6'>
<div className='max-w-4xl mx-auto'>
<h1 className='text-3xl font-bold mb-6 p-4'>All `{tag}` posts</h1>
{list.map((post) => (
<Article key={post.slug} className='border-b-2' post={post} />
))}
</div>
{hasMore ? (
<div><button onClick={handleLoadMore}>Еще статьи</button></div>
) : (
<div><button disabled>Больше нет статей</button></div>
)}
</section>
</div>
)
}
事实是,当我的。md文件的前页的标签是用英语(拉丁字母)和一个单词(例如,'javascript'或'nextjs')写的时候,路由是正常创建的,但是如果我的标签是用西里尔字母和/或两个单词(例如,'мощный код'或'super code')写的,那么没有正确生成路由。
在文章列表中显示文章的组件中,我可以使用我自己的函数,如'transletter'和Lodash:
import Link from 'next/link'
import Date from '@/lib/date';
import { transliterate } from '@/lib/transletter';
const _ = require("lodash")
const getTagLink = (tag) => {
return (
<Link href={`/blog/tag/${_.kebabCase(transliterate(tag))}`} key={tag}>
{tag}
</Link>
);
};
export default function Article({ post }) {
return (
<article className={`bg-white p-4`}>
<Link href={`/blog/${post.slug}`}>
<h3 className='text-2xl mb-2 font-medium hover:text-red-400 cursor-pointer'>
{post.title}
</h3>
</Link>
<span className='text-gray-600 mb-4 block'>
<Date dateString={post.date} /> | {post.tags.map(tag => getTagLink(tag)).reduce((prev, curr) => [prev, ', ', curr])}
</span>
<p>{post.description}</p>
</article>
);
}
链接到标签页的${_.kebabCase(transliterate(tag))}
做的一切都是正确的-它将url的西里尔字符音译,并在单词之间添加连字符,即,而不是blog/tag/мощный код
变成blog/tag/moschnyi-kod
。但是,我没有这样的路由,因为[tag].js从字面上取标签名称——就像它写的那样。
我如何将_.kebabCase(transliterate(tag))
之类的东西添加到我的getStaticPath函数或其他地方,以便一切正常工作,以便生成所需的动态路由,其中标签页将接收一个url,标签名称翻译成拉丁语,并在单词
动态路由文件名[tag].js
取getStaticPaths
导出的内容。链接href
和从getStaticPaths
导出的路径必须匹配。
所以你需要在getStaticPaths
中做同样的转换。
我建议创建一个类似convertTagToSlug( tag )
的函数,并使用相同的函数来创建两者,链接href
和getStaticPaths
中的段塞,例如:
const convertTagToSlug = function( tag ){
return _.kebabCase( transliterate( tag ) );
};
你可能在几个地方都需要原始标签和段符,所以构建某种"库"可能是值得的。或者"tag registry",这样您将有一个对象,甚至是一个自动生成的.js文件,如:
// tag-registry.js
export const tagRegistry = {
'tag-1': { slug: 'tag-1', tag: 'tag 1' },
'tag-2': { slug: 'tag-2', tag: 'tag 2' },
};
这样你还需要加载和"解析";