开发
Next.js站点生成RSS的坑
如何为Next.js项目生成RSS,以及如何避免一个低级错误
学习如何用Next.js生成RSS文件并避免出现尺寸巨大的问题。通过使用feed库,创建Feed对象并获取最新数据,最后在getServerSideProps函数中写入并设置缓存。确保在添加RSS条目前清空feed以避免无限循环。详细内容和代码示例可在页面中找到。
- #RSS
- #Next.js
- #feed
- #GraphQL
- #SSR
- #博客
- #缓存控制
- #性能优化
632
今天有人在我的博客上向我反应,这个站点的RSS文件把他服务器搞崩了,一看大小有95M。
之前我看到链接能打开,就没实际测试过。果然有大坑。
简单排查了一下,找到了原因,已经修复了这个问题。顺便分享一下如何给Next.js.js项目生成RSS,以及避免一个潜在的坑。
基本思路
Next.js官方并没有提供生成RSS的方法,所以我将使用feed来帮助我生成rss.xml文件。
大致流程是这样的:
在
/page
目录下新建一个rss.xml.tsx
的文件,将通过/rss.xml
的链接访问到RSS;创建一个
Feed
对象,这将是RSS的主体;使用
getServerSideProps
获取数据,添加到Feed
里;将数据写入。
代码
因为我使用了GraphQL,所以获取数据上可能会有点不同。
因为我希望每次访问RSS的时候,数据都能是最新的,所以会使用SSR来实现。
首先导入必要的依赖。
typescript
import client from "@/apollo-client";
import { gql } from "@apollo/client";
import { Feed } from "feed";
import { GetServerSideProps, GetServerSidePropsContext, GetServerSidePropsResult } from "next";
先创建一个Feed
对象:
typescript
const feed = new Feed({
title: "可可托海没有海的RSS",
description: "李大毛没有猫的个人网站",
id: "https://darmau.design/",
link: "https://darmau.design/",
language: "zh-CN",
image: "/img/default-cover.jpg",
favicon: "/img/logo.svg",
feedLinks: {
RSS2: "https://darmau.design/rss.xml",
},
copyright: `©${new Date().getFullYear()} 李大毛`,
author: {
name: "李大毛",
link: "https://darmau.design/",
},
});
再声明一个GraphQL查询语句:
typescript
const GET_RSS = gql`
query Articles($sort: [String], $pagination: PaginationArg) {
articles(sort: $sort, pagination: $pagination) {
data {
attributes {
title
url
publishDate
description
main
cover {
data {
attributes {
url
}
}
}
}
id
}
}
}
`;
然后增加一个getServerSideProps
。
typescript
export const getServerSideProps: GetServerSideProps = async (context: GetServerSidePropsContext): Promise<GetServerSidePropsResult<any>> => {
//code goes here
}
接下来的代码都是在该函数内。
typescript
const { res } = context;
const { data } = await client.query({
query: GET_RSS,
variables: {
sort: ["publishDate:desc"],
pagination: {
limit: 10,
},
},
});
const articles = data.articles.data;
这段代码的作用是将一个文章的数据存进articles
这个数组中,接下来分别将其添加进feed
中:
typescript
articles.forEach((article: ContentsProps) => {
feed.addItem({
title: article.attributes.title,
id: article.id,
link: `https://darmau.design/article/${article.attributes.url}`,
description: article.attributes.description,
content: article.attributes.main,
author: [
{
name: "李大毛",
link: "https://darmau.design/",
},
],
contributor: [
{
name: "李大毛",
link: "https://darmau.design/",
},
],
date: new Date(article.attributes.publishDate),
image: article.attributes.cover.data.attributes.url,
});
});
最后是设置缓存,将数据写入:
typescript
const cacheMaxAgeUntilStaleSeconds = 5;
const cacheMaxAgeStaleDataReturnSeconds = 30;
res.setHeader(
"Cache-Control",
`public, s-maxage=${cacheMaxAgeUntilStaleSeconds}, stale-while-revalidate=${cacheMaxAgeStaleDataReturnSeconds}`
);
res.setHeader("Content-Type", "text/xml");
res.write(feed.rss2());
res.end();
return { props: {} };
最后在最外部,将整个函数默认到导出:
typescript
export default function RSS() {}
这就完成了。
但!
有巨坑!
失控的尺寸
这个代码的问题就在于,每次访问该页面的时候,都会经历一次生成-添加RSS items的过程,导致feed
充斥着大量重复条目,尺寸越来越大,这也是最终变成95M的原因。事实上这是个无限循环,有多大取决于你的电脑何时卡。
解决办法也很简单,就是在获取数据前,先将feed
清空:
typescript
feed.items = []
这下就没问题了。
完整代码可以访问这里。