こんにちは、Pちゃんです。
これは DeNA Advent Calendar 2018 の6日目の記事です。
TL;DR
- DeNA のデザイン本部が運営する DeNA DESIGN BLOG というブログがある
- 最近のリニューアルで WordPress から Nuxt.js (静的生成) に移行した
- RSSが消失した
- 生やした
DeNA DESIGN BLOG の仕組みにについて
DeNA DESIGN BLOG は、主に以下のようなスタックで構成されています。
- Nuxt v2.x
- TypeScript
- Markdown
上の図のとおり、 $ nuxt generate
をすると
- 自作スクリプトや processmd を使って、全記事データを1つのJSONファイルに固める
- 全記事データが入ったJSONファイルを元に、Vueテンプレートに流し込んでトップページや記事ページのHTMLファイルを書き出す
と言った処理が行われます。 Nuxt.js の静的生成でブログを作っているところは、どこもこんな感じの流れなのではないでしょうか?
@nuxt/feed は静的生成するときは向かない
さて、 Nuxt.js で RSS を生やそうとググると、まず初めに目にするのが nuxt-community によってメンテされている @nuxtjs/feed だと思います。
良さそうに見えるんですが、静的生成するときには向かないです。
理由は2つあって、1つは axios で本番サーバーからデータを取ってきて RSS を生成するみたいな使い方が想定されていること、2つは RSS の生成が generate:before
というタイミングで実行されることです。
axios で本番サーバーからデータを取ってくることを想定されていることの何がいけないのか?
@nuxtjs/feed の example を見てみると、以下のように書かれています。
//Import axios into your nuxt.config.js
const axios = require('axios')
// In your `feed` array:
async create (feed) {
feed.options = {
title: 'My blog',
link: 'https://my-url.com/feed.xml',
description: 'This is my personal feed!',
}
const posts = await (axios.get('https://api.blog.lichter.io/posts')).data
posts.forEach(post => {
feed.addItem({
title: post.title,
id: post.url,
link: post.url,
description: post.description,
content: post.content
})
})
feed.addCategory('Nuxt.js')
feed.addContributor({
name: 'Alexander Lichter',
email: 'example@lichter.io',
link: 'https://lichter.io/'
})
}
axios.get
でブログのAPIを叩いて、それを元に RSS を生成と言った処理が書かれていますが、これができるのはサーバーで動的にコンテンツを生成できる環境だけなので、静的生成でブログを作る際は利用できないです。
静的生成の場合、サーバーに上がっているのは、新しい記事を書く前にデプロイされたデータですからね...。
とは言え、別に axios で API を叩くところを fs.readFile とかで、記事一覧の JSON ファイル取ってくれば良くね? となるんですが...
次項の問題があります。
RSS の生成が generate:before
というタイミングで実行される
上記のとおりなんですが、 @nuxtjs/feed は generate:before
というタイミングに hook して動作するように書かれています。
generate:before
がどのようなタイミングかと言うと、まあ読んで字の如くなのですが、 generate が完了する前なので、最新の状態ではない記事一覧を参照して RSS を生成してしまいます。
しかもこれは固定なので、外から引数を渡したりして変更することができません。
generate が完了したあとに RSS 生成できるようになれば完璧なのに...!
どうやって解決したのか
どうしようもないので、自前でモジュールを作りました。
generate:done
に hook させて RSS を生成するようにしました。 RSS を生成する部分は @nuxtjs/feed の中を参考にして feed を使用しました。
以下、様子です。(フィード生成の部分、ちょっと端折ってます)
const fs = require('fs')
const { Feed } = require('feed')
const { promisify } = require('util')
module.exports = function() {
// generate が終わったタイミングで実行する
this.nuxt.hook('generate:done', async generator => {
// 全ての記事データが1つになった JSON ファイルを読み込む
const entriesJson = await promisify(fs.readFile)('記事一覧JSONのパス', 'utf-8')
const entries = await JSON.parse(entriesJson).data
// 全ての記事を公開日が新しい順にソートする
entries.sort(function(a, b) {
return a.published_at < b.published_at ? 1 : -1
})
// ブログ自体のデータを流し込む
const feed = new Feed({
title: 'ブログ自体のタイトルを書くよ',
description:
'ブログ自体の説明を書くよ',
generator: 'feed',
})
// 最新10件の記事データを流し込む
for (let i = 0; i < 10; i++) {
let entry = entries[i]
// ブログ記事のデータを流し込む
feed.addItem({
title: entry.title),
id: `https://design.dena.com/${entry.category}/${entry.id}`,
link: `https://design.dena.com/${entry.category}/${entry.id}`,
author: [
{
name: entry.author
}
],
date: new Date(entry.published_at)
})
}
// RSS 2.0 形式で ./dist/feed.xml に書き込む
await promisify(fs.writeFile)('./dist/feed.xml', feed.rss2())
// ログ
console.log('Output feed.xml')
})
}
まとめ
- Nuxtの静的サイトでRSSを生やしたいなら、@nuxtjs/feed を参考にしつつ自前でモジュール実装するのが良さそう
- https://design.dena.com/feed.xml が爆誕しました