开发

Next.js站点生成RSS的坑

如何为Next.js项目生成RSS,以及如何避免一个低级错误

学习如何用Next.js生成RSS文件并避免出现尺寸巨大的问题。通过使用feed库,创建Feed对象并获取最新数据,最后在getServerSideProps函数中写入并设置缓存。确保在添加RSS条目前清空feed以避免无限循环。详细内容和代码示例可在页面中找到。

  1. #RSS
  2. #Next.js
  3. #feed
  4. #GraphQL
  5. #SSR
  6. #博客
  7. #缓存控制
  8. #性能优化

632

The image features a black and white logo for the company "NEX" with the letters "NEX" and "X" in a diagonal line. The logo is centered on a white background, with a diagonal line running across it. The text "NEX" is in the upper left corner, while "X" is in the lower right corner. The logo is simple and elegant, with a clean design.The image features a black and white logo for the company "NEX" with the letters "NEX" and "X" in a diagonal line. The logo is centered on a white background, with a diagonal line running across it. The text "NEX" is in the upper left corner, while "X" is in the lower right corner. The logo is simple and elegant, with a clean design.

今天有人在我的博客上向我反应,这个站点的RSS文件把他服务器搞崩了,一看大小有95M。

之前我看到链接能打开,就没实际测试过。果然有大坑。

简单排查了一下,找到了原因,已经修复了这个问题。顺便分享一下如何给Next.js.js项目生成RSS,以及避免一个潜在的坑。

基本思路

Next.js官方并没有提供生成RSS的方法,所以我将使用feed来帮助我生成rss.xml文件。

大致流程是这样的:

  1. /page目录下新建一个rss.xml.tsx的文件,将通过/rss.xml的链接访问到RSS;

  2. 创建一个Feed对象,这将是RSS的主体;

  3. 使用getServerSideProps获取数据,添加到Feed里;

  4. 将数据写入。

代码

因为我使用了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 = []

这下就没问题了。

完整代码可以访问这里