开发
Next.js站点生成RSS的坑
如何为Next.js项目生成RSS,以及如何避免一个低级错误
学习如何用Next.js生成RSS文件并避免出现尺寸巨大的问题。通过使用feed库,创建Feed对象并获取最新数据,最后在getServerSideProps函数中写入并设置缓存。确保在添加RSS条目前清空feed以避免无限循环。详细内容和代码示例可在页面中找到。
- #RSS
- #Next.js
- #feed
- #GraphQL
- #SSR
- #博客
- #缓存控制
- #性能优化
658
今天有人在我的博客上向我反应,这个站点的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 = []
这下就没问题了。
完整代码可以访问这里。