jamstackが流行り始めている最近ですが、流行りに乗っかるためにNuxt.js + Nuxt CompostionAPI + TypeScriptとう環境下でmicroCMSを利用してSSGしてみた。
特に頑張るところはないんですが、TypeScriptが初心者でちょっと苦労した・・・。
サンプル
今回のサンプルはgithub上にあります。
https://github.com/polidog/nuxt-microcms/tree/compostion
まずはNuxtのプロジェクトを用意する
1
|
npx create-nuxt-app nuxt-microcms
|
必要となるnpmパッケージをいれておく
追加で必要になるものは以下の通り
- @nuxt/http
- @nuxtjs/dotenv
- @nuxtjs/composition-api
1
2
|
$ yarn add --dev @nuxtjs/dotenv
$ yarn add @nuxt/http @nuxtjs/composition-api
|
microCMS用のpluginを作成する
APIと通信する方法はいくつかあるが、今回はnuxt/httpを利用する形でmicrocms用のプラグインを用意した。
1
2
3
4
5
6
7
8
9
10
11
|
// plugins/microcms.ts
import { defineNuxtPlugin } from '@nuxtjs/composition-api'
export default defineNuxtPlugin(({ $http, env }, inject) => {
const $microcms = $http.create({
headers: { 'X-API-KEY': env.MICROCMS_API_KEY },
})
$microcms.setBaseURL(env.MICROCMS_API_URL)
inject('microcms', $microcms)
})
|
こんなな感じで$httpから新しいインスタンスを作り、APIをコールするのに必要な情報をセットする。
nuxt/composition-apiの defineNuxtPlugin
を使ってpluginを定義している。
ただ、これは使わなくてもいい気がしていて、下記の書き方もできる
1
2
3
4
5
6
7
8
9
10
|
const mircoCMS: Plugin = ({ $http, env }, inject) => {
const $microcms = $http.create({
headers: { 'X-API-KEY': env.MICROCMS_API_KEY },
})
$microcms.setBaseURL(env.MICROCMS_API_URL)
inject('microcms', $microcms)
}
export default mircoCMS
|
defineNuxtPlugin
を使う必要性がわからないんだけど、なにか良いことがあるのか?若干疑問…。
あとはnuxt.config.jsでpluginを登録。
1
2
3
4
5
|
// nuxt.config.js
...
plugins: ['@/plugins/microcms'],
...
|
microCMS pluginを使う
先程作成したpluginをpageコンポーネントで使ってみる。
今回はmicrocmsでページ専用のobject形式のAPIを用意
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
{
"createdAt": "2020-10-31T06:36:42.770Z",
"updatedAt": "2020-10-31T16:11:02.887Z",
"publishedAt": "2020-10-31T06:36:42.770Z",
"name": "パーティー野郎株式会社",
"message": "<p>ここにテキストが入ります。<br>ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。<br>ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。<br>ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。<br>ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。ここにテキストが入ります。</p>",
"news": [
{
"id": "3s-cjdi9s",
"createdAt": "2020-10-31T06:38:29.486Z",
"updatedAt": "2020-10-31T16:01:22.376Z",
"publishedAt": "2020-10-31T06:38:29.486Z",
"title": "お知らせA",
"image": {
"url": "https://images.microcms-assets.io/protected/ap-northeast-1:76b0f3c9-98ad-479c-b4d6-a4eabb4a5307/service/ssr-ssg/media/fff.png"
},
"content": "<p>お知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせAお知らせA</p>",
"date": "2020-10-30T15:00:00.000Z"
},
{
"id": "k_xtrtam0",
"createdAt": "2020-10-31T06:37:32.722Z",
"updatedAt": "2020-10-31T16:01:31.536Z",
"publishedAt": "2020-10-31T06:37:32.722Z",
"title": "お知らせB",
"image": {
"url": "https://images.microcms-assets.io/protected/ap-northeast-1:76b0f3c9-98ad-479c-b4d6-a4eabb4a5307/service/ssr-ssg/media/fff.png"
},
"content": "<p>お知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせB<br><br><br>お知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせBお知らせB</p>",
"date": "2020-10-30T15:00:00.000Z"
}
]
}
|
あとは適当にvueファイルを用意する
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
|
// pages/index.vue
<template>
<section v-if="contents" class="top">
<b-container>
<h2>{{ contents.name }}</h2>
<div class="jumbotron jumbotron-fluid">
<div class="container">
<div class="lead" v-html="contents.message" />
</div>
</div>
<div class="news">
<h2>お知らせ</h2>
<b-row>
<b-col
v-for="item in contents.news"
:key="item.id"
sm="auto"
xs="auto"
>
<b-card
:title="item.title"
:img-src="item.image.url"
:img-alt="item.title"
img-top
tag="article"
style="max-width: 20rem;"
class="mb-2"
>
<div v-html="item.content" />
<b-button href="#" variant="primary">詳細</b-button>
</b-card>
</b-col>
</b-row>
</div>
</b-container>
</section>
</template>
<script lang="ts">
import { defineComponent, useContext, useAsync } from '@nuxtjs/composition-api'
export default defineComponent({
setup() {
const { $microcms } = useContext()
const contents = useAsync(() => $microcms.$get('/pages-top'))
return {
contents,
}
},
})
</script>
<style scoped>
.top {
margin-top: 30px;
}
</style>
|
このまま yarn dev
して動かすと、APIから情報を取得できる
defineComponentとかuseContextとか
nuxt/compostion-apiを使っているので、defineComponent
使ってsetupメソッド定義して、そのなかでAPIからデータを取得する処理をしている。
Property ‘$microcms’ does not exist on type ‘UseContextReturn’の問題
ブラウザで実行するとうまく表示されているが、エラーが発生してた
1
2
3
4
5
6
7
8
|
TS2339: Property '$microcms' does not exist on type 'UseContextReturn'.
42 | export default defineComponent({
43 | setup() {
> 44 | const { $microcms } = useContext()
| ^^^^^^^^^
45 | const contents = useAsync(() => $microcms.$get('/pages-top'))
46 | return {
47 | contents,
|
UseContextReturnでは$microcms
の定義がないのでエラーになっているみたい。
UseContextReturnとはなんぞや?ということで型定義をみてみる
1
2
3
4
5
6
|
interface UseContextReturn extends Omit<Context, 'route' | 'query' | 'from' | 'params'> {
route: Ref<Route>;
query: Ref<Route['query']>;
from: Ref<Route['redirectedFrom']>;
params: Ref<Route['params']>;
}
|
てか、$http
とかどうなっているんだろうとnuxt/httpのコードを読んでたら以下のようなコードをみつけた
1
2
3
4
5
|
declare module '@nuxt/types' {
interface Context {
$http: NuxtHTTPInstance
}
}
|
https://github.com/nuxt/http/blob/5b3de263eb772c54d79362010060eb87212388e3/types/index.d.ts#L162-L166
とりあえず @/plugins/micorcms
に記述してみた
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
import { defineNuxtPlugin } from '@nuxtjs/composition-api'
import { NuxtHTTPInstance } from '@nuxt/http'
export default defineNuxtPlugin(({ $http, env }, inject) => {
const $microcms = $http.create({
headers: { 'X-API-KEY': env.MICROCMS_API_KEY },
})
$microcms.setBaseURL(env.MICROCMS_API_URL)
inject('microcms', $microcms)
})
declare module '@nuxt/types' {
interface Context {
$microcms: NuxtHTTPInstance
}
}
|
これで解決した。
asyncDataのような挙動を期待してたのに・・・
setupメソッドの中でuseAsync使うと非同期処理になるので、CSRしたときにレンダリング時にAPIからデータ取得が終わっているわけじゃないので、nullの可能性がある。。。
なので v-if
で判定して上げる必要があるよね。。。asyncDataのほうが使いやすかった感が・・・
SSGする
基本的には yarn generate
すればコンテンツが生成されるはず。
あとは確認するために yarn start
して localhost:3000
にブラウザにアクセスして確認すればいい。
ssr: false問題
なぜかうまく行かずCSRになってしまうことがあった。
nuxtでSSGしたい場合はnuxt.conf.jpでssr:false
にしてはいけない。
これをするとgenerateしてもCSRとして動いてしまう。
まとめ
- plugins + nuxt/httpを使ってmicroCMSのpluginを用意した
- useContextを使ってPage componentから
$microcms
を使ってapiにリクエストを送った
- asyncDataと違うのでv-if書いておいたほうがいい。
最後に
次はSSG + SSR + Firebaseの組み合わせてみた話を書こうと思います。