由于最近吧博客转到nuxt3了,没了以前hexo提供的rss,就需要自己在nuxt3上写了,查了一下资料(简中互联网还是没有),很快就搞定了,写一篇文章来过一遍
思路就是在服务端提供一个路由文件(接口),用ContentV2提供的API获取所有帖子列表,再添加到node-rss对象里,最终把feed当作结果,响应请求格式为text/xml
返回出去,不难
主要用到的依赖是下面两个
pnpm add --save-dev rss
# node-rss 是js写的,如果你用的typescript,请额外安装@types/rss
pnpm add @nuxt/content
queryContent获取内容
由于我们是在服务端执行queryContent
,所以要换一个新的API叫serverqueryContent
serverqueryContent
和 queryContent
都是 Nuxt.js 框架中可用的方法
serverqueryContent
是一种在服务器端渲染过程中从外部 API 或文件系统获取内容的方法。它在服务器端调用,用于在渲染页面时预取所需的数据。这样,服务器就可以将完全渲染的页面发送给客户端,而不是发送空白页面并等待客户端发出额外的请求获取数据。
queryContent
是一种在客户端渲染过程中从外部 API 或文件系统获取内容的方法。它在客户端调用,用于在页面已被发送到客户端后获取所需的数据来渲染页面。这通常用于数据不是必需的初始页面渲染,或者数据仅在用户与页面交互时才需要。
总之,serverqueryContent
用于服务器端渲染过程中的预取数据,而 queryContent
用于客户端渲染过程中在发送初始页面后获取数据。
我的content结构目录如下:
content
├─1.index.md
├─2.about.md
├─_posts
| ├─article1.md
| └article2.md
在项目主目录下新建一个server目录,目录下新建routes目录,这个目录的文件下会被转成接口,再新建一个rss.xml.ts,后面我们就可以访问http://localhost/rss.xml
了,这里先获取所有帖子内容,代码如下:
import { serverQueryContent } from '#content/server'
export default defineEventHandler(async (event) => {
const Posts = await serverQueryContent(event).sort({ date: -1 }).where({ _partial: true }).find()
})
defineEventHandler()
用于为 Nuxt.js 应用程序中的特定事件定义自定义事件处理程序函数。它允许您指定发出特定事件时应采取的操作。
serverQueryContent()
用法和queryContent()
一样,具体查看queryContent(),这里我让其按时间倒序排序,这样就能按时间先后获取帖子了。
用node-rss 生成feed
node-rss的使用其实挺简单的,回想一下我们订阅rss的时候有什么内容?有rss主体信息和rss内容信息,也就是帖子信息
使用的时候也是如此,先添加rss主体信息:
import RSS from 'rss'
import { serverQueryContent } from '#content/server'
export default defineEventHandler(async (event) => {
const Posts = await serverQueryContent(event).sort({ date: -1 }).where({ _partial: true }).find()
const feed = new RSS({
title: '羽森(Orin)',
site_url: 'http://oshino.cc',
feed_url: 'http://oshino.cc/rss.xml',
})
})
主体信息和内容信息都有哪些字段,具体可以查看node-rss
然后再添加帖子信息,我们获取到的posts是一个list,这里进行for循环添加即可,最终再把feed 返回出去代码如下:
import RSS from 'rss'
import { serverQueryContent } from '#content/server'
export default defineEventHandler(async (event) => {
const feed = new RSS({
title: '羽森(Orin)',
site_url: 'http://oshino.cc',
feed_url: 'http://oshino.cc/rss.xml',
})
const blogPosts = await serverQueryContent(event).sort({ date: -1 }).where({ _partial: true }).find()
for (const doc of blogPosts) {
feed.item({
title: doc.title ?? '-',
url: `http://oshino.cc${doc._path}`,
date: doc.date,
description: doc.description,
author: '羽森'
})
}
const feedString = feed.xml({ indent: true })
event.res.setHeader('content-type', 'text/xml')
event.res.end(feedString)
return feed
})
feed.xml()
让feed对象生成xml文件,indent是美化的意思。
第二行将 HTTP 响应的 content-type
头设置为 text/xml
。这告诉客户端响应主体包含 XML 数据。
第三行通过在响应对象上调用 end 方法并将 feed 字符串作为参数来发送 RSS 源的 XML 字符串表示。这完成了 HTTP 响应并将其发送回客户端。
以上代码就可以生成xml,当我们访问http://localhost/rss.xml
就能获取到rss了
如果比较随便,到这就可以,如果还想优化一下,可以继续看下去
服务端提前渲染rss
到上面为止,都还是客户端渲染,因为我们只提供了接口,当我们在浏览器点击RSS跳转时,调用接口,执行上面的代码,生成xml,时序图如下:
这里我们还是感知到这里花的时间挺多的,我们让其在服务器开启的时候就预处理执行这个接口,到nuxt.config.ts配置文件下添加下面这些代码:
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/rss.xml']
}
}
})
把时序图变成这样:
prerender 属性指定了预渲染 Nuxt.js 应用程序中特定路由的选项。预渲染是一种技术,它提前为特定路由生成静态 HTML 文件,以便服务器可以直接将它们提供给客户端,而无需每次执行服务器端渲染过程。这可以提高应用程序的性能,特别是对于接收流量较多的路由。
再次访问,几乎是瞬间就获取到xml
更新2023-01-06(进去文章后直接显示内容
今天看Reeder的时候突然想到我的订阅并没有直接显示内容,而是显示一个title和description,需要再次点击后才会跳转到我的博客里,虽然这样能够给我带来流量,但我习惯点击进后就直接显示内容的,所以想着想着还是来查看一下怎么弄吧。
在研究了几个周刊的rss格式之后,发现他们的description属性并不是单纯的描述,而是塞了整个解析后的html内容,原来如此,这样子的话,只需要将我们的description属性改为body即可,如下:
for (const doc of blogPosts) {
feed.item({
title: doc.title ?? '-',
url: `http://oshino.cc${doc._path}`,
date: doc.date,
author: '羽森',
// -------修改这里-------
description: doc.body,
// ---------------------
})
}
上传后再次访问,发现进去文章后就有内容了,需求达成!