Nuxt3 ContentV2生成rss2022-12-19

由于最近吧博客转到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

serverqueryContentqueryContent 都是 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,时序图如下:

wDD38W

这里我们还是感知到这里花的时间挺多的,我们让其在服务器开启的时候就预处理执行这个接口,到nuxt.config.ts配置文件下添加下面这些代码:

export default defineNuxtConfig({
  nitro: {
    prerender: {
      routes: ['/rss.xml']
    }
  }
})

把时序图变成这样:

VeAHP2

prerender 属性指定了预渲染 Nuxt.js 应用程序中特定路由的选项。预渲染是一种技术,它提前为特定路由生成静态 HTML 文件,以便服务器可以直接将它们提供给客户端,而无需每次执行服务器端渲染过程。这可以提高应用程序的性能,特别是对于接收流量较多的路由。

再次访问,几乎是瞬间就获取到xml

rOWiyb

更新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,
    // ---------------------
  })
}

上传后再次访问,发现进去文章后就有内容了,需求达成!