Tech Racho エンジニアの「?」を「!」に。
  • Ruby / Rails以外の開発一般

WP REST API × NuxtJS で SSG するサイト作り

ebi です。定期ネタの WordPress 芸をやっていきます。
今回は一度やってみたかった WP REST API を利用したサイトに少し手出してみます。 React より Vue.js の方がまだ触ったことあるので NuxtJS を使ってみます。
また、 SSR だとホスティングするサーバをどうするかの問題が出てくるので SSG 前提のサイト構成にしてみようと思います。

そんなわけで実験がてらこう言う構成のサイト作りをやってみることにしました。
GitHub にコードも晒しておくので最終的なコードの細かい詳細見たい方は後で確認してください。

CMS として WordPress を採用している理由

正直自分自身にとって馴染みがある以外の理由は特にないです。
近年、 ContentFulmicroCMS などのヘッドレスCMSが台頭してきているので、好みでそう言う CMS を選んでも良いと思います。
その辺、あえて雑多な感想を羅列しておくと

  • WordPress 自体はこれからもしばらくはサイト作成ツールとしての利用でも使われ続けるであろうから、知名度やノウハウ、メンテナンスの継続性にある程度の保証はあると思うので、最近聞くようになったなレベルのヘッドレスCMSよりその点での信用度はある(使い慣れて損がない)
  • 当たり前だが、 REST API のみを使う場合、配布されている無料・有償の WordPress テーマを利用したサイトのデザインテンプレートを使うのは難しい(根性で一部移植とはもちろんやろうと思えばできるだろうけど。あとその辺が使いまわせるようWordPress単体として静的ファイル化するプラグインも一応あるにはある)
    あくまで活用するのはユーザ管理や記事投稿の部分になるが、とは言え ACF などの管理画面カスタマイズのノウハウがある場合はそう言ったものを活用することはできる
  • 既存の記事データを応用したい場合、管理されている記事コンテンツの内容やその中身、テーマテンプレートの作られ方や、依存プラグインなどの性質によって難易度や応用できる度は非常に左右される
    Techracho みたいなブログサイトとして持っている単純な記事データは割と使い回せる見込みが高い。そのため、サイト表示の速度改善やセキュリティリスクの削減のために WordPress サイト周りのチューニングやサーバ構成の見直しを図るより、思い切って今回のようなサイト構成リニューアルを試みるのは割と悪くない話だと思う

WordPress の環境準備

WordPress が動く docker 環境を用意する

本題じゃないのであまり解説はしません。

  • Dockerfile を用意します。 wp-cli 使いたい以上の説明は特にないです。
FROM wordpress:5.7-php7.4-apache

# install wp-cli
RUN set -ex; \
  curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar; \
  chmod +x wp-cli.phar; \
  mv wp-cli.phar /usr/local/bin/wp
  • docker-compose.yml を用意します。今回は WordPress テーマの方は完全にどうでもいいので mount すらしてません。
version: '3.1'

services:
  wordpress:
    build: .
    ports:
      - 80:80
    environment:
      WORDPRESS_DB_HOST: db
      WORDPRESS_DB_USER: username
      WORDPRESS_DB_PASSWORD: password
      WORDPRESS_DB_NAME: wordpress
    volumes:
      - wordpress:/var/www/html
  db:
    image: mysql:5.7
    environment:
      MYSQL_DATABASE: wordpress
      MYSQL_USER: username
      MYSQL_PASSWORD: password
      MYSQL_RANDOM_ROOT_PASSWORD: '1'
    volumes:
      - db:/var/lib/mysql

volumes:
  wordpress:
  db:
  • docker 環境を立ち上げて WordPress の初期設定をします
ebi@LAPTOP-R8D4FNMF:~/git-clone/for-techracho/wp-rest-api-and-nuxtjs$ docker-compose up -d --build
Creating network "wp-rest-api-and-nuxtjs_default" with the default driver
Creating volume "wp-rest-api-and-nuxtjs_wordpress" with default driver
Creating volume "wp-rest-api-and-nuxtjs_db" with default driver
Building wordpress
[+] Building 6.2s (6/6) FINISHED
 => [internal] load build definition from Dockerfile                                                                                                                                                    0.1s
 => => transferring dockerfile: 259B                                                                                                                                                                    0.0s
 => [internal] load .dockerignore                                                                                                                                                                       0.0s
 => => transferring context: 2B                                                                                                                                                                         0.0s
 => [internal] load metadata for docker.io/library/wordpress:5.7-php7.4-apache                                                                                                                          3.1s
 => CACHED [1/2] FROM docker.io/library/wordpress:5.7-php7.4-apache@sha256:37be4c1afbce8025ebaca553b70d9ce5d7ce4861a351e4bea42c36ee966b27bb                                                             0.0s
 => => resolve docker.io/library/wordpress:5.7-php7.4-apache@sha256:37be4c1afbce8025ebaca553b70d9ce5d7ce4861a351e4bea42c36ee966b27bb                                                                    0.0s
 => [2/2] RUN set -ex;   curl -O https://raw.githubusercontent.com/wp-cli/builds/gh-pages/phar/wp-cli.phar;   chmod +x wp-cli.phar;   mv wp-cli.phar /usr/local/bin/wp                                  2.8s
 => exporting to image                                                                                                                                                                                  0.1s
 => => exporting layers                                                                                                                                                                                 0.1s
 => => writing image sha256:28908b82cc118042c71162d7a6d63593878e1d73e077da9a4aa7fd87db0b8ca0                                                                                                            0.0s
 => => naming to docker.io/library/wp-rest-api-and-nuxtjs_wordpress                                                                                                                                     0.0s

Use 'docker scan' to run Snyk tests against images to find vulnerabilities and learn how to fix them
Creating wp-rest-api-and-nuxtjs_wordpress_1 ... done
Creating wp-rest-api-and-nuxtjs_db_1        ... done
ebi@LAPTOP-R8D4FNMF:~/git-clone/for-techracho/wp-rest-api-and-nuxtjs$ docker-compose exec wordpress wp core install --url=localhost --title='Only Use REST API Site' --admin_user=admin --admin_email=admin@e
xample.com --allow-root
Admin password: GYfzQb35Ln*DraH5f6
Success: WordPress installed successfully.
  • http://localhost を開いてみてサイト表示を確認できたのでおしまいです

適当な記事データを準備する

WordPress のテーマを探したことはありますか?デモサイトのリンクも貼ってあったりするのですがどのテーマも似たような記事が入っているのです。
つまりは公式からそう言うダミー記事(テーマユニットテスト用の)データが用意されているってことです。
本家の案内はここです。日本語版っぽいのを用意している人もどうやらいるようでしてここから拾えます。
今回は英語版を使いました。

  • これを使ってダミーの記事を読み込みます。
    docker-compose exec wordpress bash してこんな感じ。
root@2a69f8496065:/var/www/html# wp plugin install wordpress-importer --activate --allow-root
Installing WordPress Importer (0.7)
Downloading installation package from https://downloads.wordpress.org/plugin/wordpress-importer.0.7.zip...
Unpacking the package...
Installing the plugin...
Plugin installed successfully.
Activating 'wordpress-importer'...
Plugin 'wordpress-importer' activated.
Success: Installed 1 of 1 plugins.
root@2a69f8496065:/var/www/html# curl -O https://raw.githubusercontent.com/WPTRT/theme-unit-test/master/themeunittestdata.wordpress.xml
  % Total    % Received % Xferd  Average Speed   Time    Time     Time  Current
                                 Dload  Upload   Total   Spent    Left  Speed
100  550k  100  550k    0     0  1092k      0 --:--:-- --:--:-- --:--:-- 1090k
root@2a69f8496065:/var/www/html# ls
index.php    readme.html                      wp-activate.php  wp-blog-header.php    wp-config-docker.php  wp-config.php  wp-cron.php  wp-links-opml.php  wp-login.php  wp-settings.php  wp-trackback.php
license.txt  themeunittestdata.wordpress.xml  wp-admin         wp-comments-post.php  wp-config-sample.php  wp-content     wp-includes  wp-load.php        wp-mail.php   wp-signup.php    xmlrpc.php
root@2a69f8496065:/var/www/html# wp import themeunittestdata.wordpress.xml --authors=create --allow-root
Starting the import process...
​
Processing post #1724 ("Keyboard navigation") (post_type: post)
-- 1 of 180 (in file themeunittestdata.wordpress.xml)
-- Mon, 19 Jul 2021 20:48:16 +0000
-- Imported post as post_id #1724
-- Added terms (79) for taxonomy "post_tag"
​
以下略
  • 記事が増えたのを確認できたらおしまい。画像入ってる記事もあるので動作確認ケースは網羅されてて良さ気です。

WP REST API の軽い動作確認

詳しくはドキュメントを参照してください
http://localhost/wp-json/wp/v2/posts とかで JSON を受け取れることが確認できます。
(以下スクショはFirefoxでアクセスしたので勝手に整形されてます)

NuxtJS の初期設定する

create-nuxt-app をしてみる

ので、また NuxtJS 用の node が入った docker 環境から準備していきます。

  • 適当に Node 公式の docker イメージをベースにして nuxtjs サービスを docker-compose.yml に追加しました。 volumes の指定でこのあと作成する NuxtJS アプリの素材置き場を予めマウントしています。
  nuxtjs:
    image: node:14
    user: node
    working_dir: /home/node
    tty: true
    ports:
      - 3333:3000
    volumes:
      - ./frontend/for-wp-rest-api:/home/node/for-wp-rest-api

あとは再度 docker-compose up -d して、 exec してみて作業していきます。

ebi@LAPTOP-R8D4FNMF:~/git-clone/for-techracho/wp-rest-api-and-nuxtjs$ docker-compose exec nuxtjs bash

node@e8b19f43f548:~$ npm install create-nuxt-app
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated

> ejs@2.7.4 postinstall /home/node/node_modules/ejs
> node ./postinstall.js

Thank you for installing EJS: built with the Jake JavaScript build tool (https://jakejs.com/)

npm WARN saveError ENOENT: no such file or directory, open '/home/node/package.json'
npm notice created a lockfile as package-lock.json. You should commit this file.
npm WARN enoent ENOENT: no such file or directory, open '/home/node/package.json'
npm WARN node No description
npm WARN node No repository field.
npm WARN node No README data
npm WARN node No license field.

+ create-nuxt-app@3.7.1
added 382 packages from 199 contributors and audited 382 packages in 13.494s

9 packages are looking for funding
  run `npm fund` for details

found 1 moderate severity vulnerability
  run `npm audit fix` to fix them, or `npm audit` for details

node@af31c278a423:~$ npm init nuxt-app for-wp-rest-api

create-nuxt-app v3.7.1
✨  Generating Nuxt.js project in for-wp-rest-api
? Project name: for-wp-rest-api
? Programming language: JavaScript
? Package manager: Npm
? UI framework: Vuetify.js
? Nuxt.js modules: Axios - Promise based HTTP client
? Linting tools: ESLint, Prettier
? Testing framework: None
? Rendering mode: Universal (SSR / SSG)
? Deployment target: Static (Static/Jamstack hosting)
? Development tools: (Press <space> to select, <a> to toggle all, <i> to invert selection)
? Continuous integration: None
? Version control system: None
npm WARN deprecated core-js@2.6.12: core-js@<3.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could
 cause a slowdown up to 100x even if nothing is polyfilled. Please, upgrade your dependencies to the actual version of core-js.
npm WARN deprecated querystring@0.2.1: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated querystring@0.2.0: The querystring API is considered Legacy. new code should use the URLSearchParams API instead.
npm WARN deprecated chokidar@2.1.8: Chokidar 2 will break on node v14+. Upgrade to chokidar 3 with 15x less dependencies.
npm WARN deprecated resolve-url@0.2.1: https://github.com/lydell/resolve-url#deprecated
npm WARN deprecated urix@0.1.0: Please see https://github.com/lydell/urix#deprecated
npm WARN deprecated fsevents@1.2.13: fsevents 1 will break on node v14+ and could be using insecure binaries. Upgrade to fsevents 2.


> core-js@2.6.12 postinstall /home/node/for-wp-rest-api/node_modules/@nuxt/babel-preset-app/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

> core-js@2.6.12 postinstall /home/node/for-wp-rest-api/node_modules/@nuxt/babel-preset-app/node_modules/core-js
> node -e "try{require('./postinstall')}catch(e){}"

Thank you for using core-js ( https://github.com/zloirock/core-js ) for polyfilling JavaScript standard library!

The project needs your help! Please consider supporting of core-js on Open Collective or Patreon: 


中略

> for-wp-rest-api@1.0.0 lint:js /app/for-wp-rest-api
> eslint --ext ".js,.vue" --ignore-path .gitignore . "--fix"


�  Successfully created project for-wp-rest-api

  To get started:

        cd for-wp-rest-api
        npm run dev

  To build & start for production:

        cd for-wp-rest-api
        npm run build
        npm run start


  For TypeScript users.

  See : https://typescript.nuxtjs.org/cookbook/components/
  • 言われた通りに dev 環境を立ち上げたいのだけど、どうやら Docker 環境で立ち上げた develoment 環境のサイト画面をホスト側のブラウザで開くには設定変更が必要そうなので、ドキュメントに従って nuxt.config.js に一部だけ追記します
  server: {
    host: '0' // デフォルト: localhost
  },
  • こんな感じで development サーバが立ち上がって、 http://localhost:3333 にアクセスして初期表示っぽい画面が出たらおしまい
node@6240940fa15f:~$ cd for-wp-rest-api/
node@6240940fa15f:~/for-wp-rest-api$ npm run dev

> for-wp-rest-api@1.0.0 dev /app/for-wp-rest-api
> nuxt


   ╭────────────────────────────────────────╮
   │                                        │
   │   Nuxt @ v2.15.7                       │
   │                                        │
   │   ▸ Environment: development           │
   │   ▸ Rendering:   server-side           │
   │   ▸ Target:      static                │
   │                                        │
   │   Listening: http://172.30.0.3:3000/   │
   │                                        │
   ╰────────────────────────────────────────╯

ℹ Preparing project for development                                                                                                                                                                17:33:48
ℹ Initial build may take a while                                                                                                                                                                   17:33:48
ℹ Discovered Components: .nuxt/components/readme.md                                                                                                                                                17:33:48
✔ Builder initialized                                                                                                                                                                              17:33:48
✔ Nuxt files generated                                                                                                                                                                             17:33:48

✔ Client
  Compiled successfully in 8.03s

✔ Server
  Compiled successfully in 7.04s

ℹ Waiting for file changes                                                                                                                                                                         17:33:57
ℹ Memory usage: 216 MB (RSS: 469 MB)                                                                                                                                                               17:33:57
ℹ Listening on: http://172.30.0.3:3000/   

  • ホットリロードも上手く動いていました。ただ、これだと全然 SSG した静的サイト状態ではありません。
    じゃあどうするかと言うと、 npm run generatenuxt generate してくれて、 dist ディレクトリ配下に静的ファイルが置かれる。これをデプロイすればよい
    また、 npm run startnuxt start されて、この状態だと静的サイトのホスティングをしてくれるそうです

WP REST API との連携を試す

NuxtJS では pages ディレクトリに置く .vue ファイルで勝手にルーティング作ってくれます。便利ですね。
と言うことで、とりあえずTOPページと各投稿ページを作ってみます。

  • index.vue をこんな感じで書き換えました。サイト内の全投稿データを一度に取ってきているわけではないですが、その辺りは今の段階では無視します。
<template>
  <div>hogehuga
    <ul>
      <li v-for="post in posts" :key="post.id">
        id: {{ post.id }}<br>
        slug: {{ post.slug }}<br>
        title: {{ post.title.rendered }}
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    async asyncData({$axios}) {
      const posts = await $axios.$get('http://wordpress/wp-json/wp/v2/posts')
      return { posts }
    }
  }
</script>

  • pages ディレクトリ配下に articles ディレクトリを切って、 _id.vue ファイルを作っていきます
    とこれを追加しながら気付いたんですが、SSRする時のWordPressサイトは wordpress を見に行けばいいんだけど、クライアントサイドでは localhost を見に行きたいのでその辺の分岐を判断させる process.client とかも足しました
<template>
  <div>this is article detail page.
    <h2>{{ post.title.rendered }}</h2>
    <!-- eslint-disable-next-line vue/no-v-html -->
    <div v-html="post.content.rendered"></div>
  </div>
</template>

<script>
  export default {
    async asyncData(context) {
      const wpSiteDomain = process.client ? 'localhost' : 'wordpress'
      const post = await context.$axios.$get(`http://${wpSiteDomain}/wp-json/wp/v2/posts/${context.params.id}`)
      return { post }
    }
  }
</script>

TOPページの方も同様の変更を加えたのと、リンクを足しました

<template>
  <div>hogehuga
    <ul>
      <li v-for="post in posts" :key="post.id">
        id: {{ post.id }}<br>
        slug: {{ post.slug }}<br>
        <NuxtLink :to="'/articles/'+post.id">title: {{ post.title.rendered }}</NuxtLink>
      </li>
    </ul>
  </div>
</template>

<script>
  export default {
    async asyncData({$axios}) {
      const wpSiteDomain = process.client ? 'localhost' : 'wordpress'
      const posts = await $axios.$get(`http://${wpSiteDomain}/wp-json/wp/v2/posts`)
      return { posts }
    }
  }
</script>

最終的にこんな感じになりました。良さそう!

  • 一通り準備してみたので npm run generate してみます
    どうやらリンクを NuxtLink でちゃんと貼っておけば(ここ伏線)勝手にクローリングして動的ルーティングも解決してくれるらしい?天才か? 🤔
node@6240940fa15f:~/for-wp-rest-api$ npm run generate

> for-wp-rest-api@1.0.0 generate /home/node/for-wp-rest-api
> nuxt generate

ℹ Doing webpack rebuild because pages/index.vue modified                                                                                                                                           20:31:24
ℹ Production build                                                                                                                                                                                 20:31:24
ℹ Bundling for server and client side                                                                                                                                                              20:31:24
ℹ Target: static                                                                                                                                                                                   20:31:24
ℹ Using components loader to optimize imports                                                                                                                                                      20:31:24
ℹ Discovered Components: node_modules/.cache/nuxt/components/readme.md                                                                                                                             20:31:24
✔ Builder initialized                                                                                                                                                                              20:31:24
✔ Nuxt files generated                                                                                                                                                                             20:31:24

✔ Client
  Compiled successfully in 18.10s

✔ Server
  Compiled successfully in 7.55s


Hash: 95aae7fb42d112dc083f
Version: webpack 4.46.0
Time: 18097ms
Built at: 07/20/2021 8:31:43 PM
                         Asset       Size  Chunks                                Chunk Names
../server/client.manifest.json   16.8 KiB          [emitted]
                    026bb8a.js   2.36 KiB       8  [emitted] [immutable]         runtime
                    06c3360.js    224 KiB       1  [emitted] [immutable]         commons/app
                    506c179.js   3.91 KiB       0  [emitted] [immutable]         app
                    7a8387e.js   6.58 KiB       3  [emitted] [immutable]         components/tutorial
                    8f34255.js  946 bytes       4  [emitted] [immutable]         components/vuetify-logo
                      LICENSES  407 bytes          [emitted]
                    a020b88.js    534 KiB       9  [emitted] [immutable]  [big]  vendors/app
                    a0680e3.js  826 bytes       5  [emitted] [immutable]         pages/articles/_id
                    a8fbb47.js   12.7 KiB      10  [emitted] [immutable]         vendors/pages/inspire
                    ca3c55d.js   1.85 KiB       2  [emitted] [immutable]         components/nuxt-logo
                    f6103f7.js  683 bytes       7  [emitted] [immutable]         pages/inspire
                    fb35e2f.js  920 bytes       6  [emitted] [immutable]         pages/index
 + 2 hidden assets
Entrypoint app = 026bb8a.js 06c3360.js a020b88.js 506c179.js

WARNING in asset size limit: The following asset(s) exceed the recommended size limit (244 KiB).
This can impact web performance.
Assets:
  a020b88.js (534 KiB)

Hash: 0ea6a566ee2cb2b83c27
Version: webpack 4.46.0
Time: 7546ms
Built at: 07/20/2021 8:31:50 PM
                     Asset       Size  Chunks             Chunk Names
   components/nuxt-logo.js   5.89 KiB       1  [emitted]  components/nuxt-logo
    components/tutorial.js   7.13 KiB       2  [emitted]  components/tutorial
components/vuetify-logo.js   4.98 KiB       3  [emitted]  components/vuetify-logo
     pages/articles/_id.js   2.05 KiB       4  [emitted]  pages/articles/_id
            pages/index.js   2.25 KiB       5  [emitted]  pages/index
          pages/inspire.js   10.6 KiB       6  [emitted]  pages/inspire
                 server.js    649 KiB       0  [emitted]  app
      server.manifest.json  779 bytes          [emitted]
 + 7 hidden assets
Entrypoint app = server.js server.js.map
ℹ Full static generation activated                                                                                                                                                                 20:31:51
ℹ Generating output directory: dist/                                                                                                                                                               20:31:51
ℹ Generating pages with full static mode                                                                                                                                                           20:31:51
✔ Generated route "/inspire"                                                                                                                                                                       20:31:51
✔ Generated route "/"                                                                                                                                                                              20:31:52
✔ Generated route "/articles/1784"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1"                                                                                                                                                                    20:31:54
✔ Generated route "/articles/1781"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1782"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1786"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1785"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1778"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1787"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1783"                                                                                                                                                                 20:31:54
✔ Generated route "/articles/1788"                                                                                                                                                                 20:31:54
✔ Client-side fallback created: 200.html                                                                                                                                                           20:31:54
✔ Static manifest generated              

  • 簡単じゃん?これで完璧? 😄
    ⇒ そこまで甘くはない 😡
    こんな感じで画像パスはWordPressサイトを向いているままなので、デプロイしたいならWordPressがアップロードした画像を持って来るのと、画像パスを置き換えをしないといけなそうです(冒頭の図で説明してますが)

WordPress でアップロードした画像ファイルの取り扱いを解決する

あまりモチベーションが湧く楽しそうな課題じゃないので一旦飛ばします 😇

と言うか、元々は端末によって利用する画像選ぶとかそう言う画像最適化モジュール(参考)って言うのがフロントエンド的にあって凄いらしいので触りたかったのですが、HTML全体をWordPressから貰ったもの表示するだけとなると画像表示用のコンポーネントを使うようにさせる用途と合わない気がしました。
画像圧縮をやるにしてもWordPressからアップロードする時点でプラグインで済ませれば良い気がしてきたので今回はやりません。
となると、やるべきことは画像アップロードディレクトリの同期と generate 完了後のHTMLが参照している img の src を書き換えるだけになりました。

仕上げる。ページ整えたり実際にデプロイしたり

味気ないのでもうちょい真面目にページ増やしたりしました。

  • Vuetify の pagination コンポーネントを使おうとしたら、リンククリック時の制御を指定すると言う形でしかカスタマイズできなかった
    するとボタン部分を NuxtLink 化できなそうで nuxt generate でページネーションのリンクを踏んだあとの2ページ目以降のページが生成されませんでした 😭
    と言うことで、自前で適当にページネーションのロジック書いてコンポーネント作りました。玄人はどうしてんの…… 🙄
  • せっかく本来 WordPress の Gutenberg で作ったブロックである程度レイアウト指定がされているのがこのままだと引き継げません
    その場合、この辺りの CSS ファイルを assets 配下に持って来て nuxt.config.js で指定してスタイルを当てるとなんとなく綺麗になります。もっと良い持ってき方がありそうな気もしますが。
  // Global CSS: https://go.nuxtjs.dev/config-css
  css: [
    '@/assets/css/wp-block-library/style'
  ],
  • GitHub Pages へのデプロイを考えるにあたって、ドキュメントルートの調整を行っておきます。
    これは nuxt.config.js の末尾に以下を足せば良さそうです(参考
  router: {
    base: '/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/'
  },

上記設定後に npm run generate したファイルを軽く覗いてみると、以下のようになったことが確認でき、これで GitHub Pages 環境にデプロイしても動きそうです
( development の base も変わっちゃうので、base: process.env.NODE_ENV == 'production' ? '/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/' : '/' とかにしてもいいかも)

</div></div></div></div></div></div> <div class="row my-5 justify-center"><a href="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/archives/1" class="v-btn v-btn--is-elevated v-btn--has-bg v-btn--router theme--light v-size--default"><span class="v-btn__content">
      all article archives
    </span></a></div></div></div></main> <footer class="v-footer v-sheet theme--light v-footer--absolute" style="left:0;right:0;bottom:0"><span>© 2021</span></footer></div></div></div></div><script defer src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/static/1628038218/state.js"></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/b410278.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/81a6dc0.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/c9dd595.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/18164a9.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/9d78f39.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/6215724.js" defer></script><script src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/_nuxt/22b3dc8.js" defer></script>
  </body>
</html>

画像周りリベンジ

方針としては、 nuxt generate の処理にカッコよくフックする感じでパス変換の処理を仕込めると良さ気なのでそれを目指します。
フックの案内はここにあります。

  • WordPress からアップロードした画像はなんやかんやで NuxtJS 管理下の static ディレクトリ下に持って来れたものとします。ここに置いておくと nuxt generate した後も dist フォルダ配下に置いてくれるようになるのでここに置いてます
    今回は docker cp wp-rest-api-and-nuxtjs_wordpress_1:/var/www/html/wp-content/uploads ./frontend/for-wp-rest-api/static と詰まらない処理で終わらせました
  • Node.js で真面目に処理を書けそうにないので、せめてファイル探索や文字列置換の処理が楽になりそうなそれらしきパッケージを見つけてきてまず npm i -D replace-in-file でインストールしました
  • nuxt.config.js の末尾に generate:done にフックする処理を足します
  hooks: {
    'generate:done': async generator => {
      const replace = require('replace-in-file')
      const options = {
        files: 'dist/**/*.html',
        from: 'src="http://localhost/wp-content/uploads',
        to: 'src="/for-techracho/wp-rest-api-and-nuxtjs/frontend/for-wp-rest-api/dist/uploads',
      }
      const results = replace.sync(options);
      console.log('Replacement results:', results);
    }
  }

これで npm run generate を実行すると、こんな感じで generate 完了後に独自処理として今追加したファイル内の文字置換も実行されるようになりました。

node@3f442c55f636:~/for-wp-rest-api$ npm run generate

> for-wp-rest-api@1.0.0 generate /home/node/for-wp-rest-api
> nuxt generate

ℹ Doing webpack rebuild because nuxt.config.js modified                                                                                                         17:07:07
ℹ Production build                                                                                                                                              17:07:08
ℹ Bundling for server and client side                                                                                                                           17:07:08
ℹ Target: static                                                                                                                                                17:07:08
ℹ Using components loader to optimize imports                                                                                                                   17:07:08
ℹ Discovered Components: node_modules/.cache/nuxt/components/readme.md                                                                                          17:07:08
✔ Builder initialized                                                                                                                                           17:07:08
中略
✔ Generated route "/articles/1163"                                                                                                                              17:07:41
✔ Generated route "/articles/1031"                                                                                                                              17:07:41
✔ Generated route "/articles/555"                                                                                                                               17:07:41
✔ Client-side fallback created: 200.html                                                                                                                        17:07:41
✔ Static manifest generated                                                                                                                                     17:07:41
Replacement results: [                                                                                                                                          17:07:41
  {
    file: 'dist/200.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/1/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/2/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/3/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/4/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/5/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/6/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/7/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/8/index.html',
    hasChanged: false
  },
  {
    file: 'dist/archives/9/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1000/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1011/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1016/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1031/index.html',
    hasChanged: true
  },
  {
    file: 'dist/articles/1148/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1149/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1150/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1151/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1152/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1158/index.html',
    hasChanged: true
  },
  {
    file: 'dist/articles/1161/index.html',
    hasChanged: false
  },
  {
    file: 'dist/articles/1163/index.html',
    hasChanged: true
  },
中略

デプロイ先で動作確認

  • デプロイしたけどちょっとおかしい。どうやら _nuxt 以下のファイルが上手く取り扱えていないようです。調べてみると、 GitHub Pages では _ 始まりのファイルやフォルダ名がNGらしい
  • と言うことで nuxt.config.js の build プロパティに以下のように追記して再生成&デプロイをしました
  // Build Configuration: https://go.nuxtjs.dev/config-build
  build: {
    publicPath: '/nuxtjs/'
  },
  • 多分これで無事デプロイできたと思います
    (見ても大して面白くないですが実際に動くのを見たければこちらから

感想など

  • ルーティングやレイアウトの仕組み整備がしてあって nuxt generate でサイト生成するまでのハードルは低くて良いと思いました
  • 実際やってみると、まともなサイトを作ろうと思う上ではまだまだ解決しないといけない課題は色々ありますが、ノウハウ溜まれば二個目、三個目の案件は自信を持ってやれそうです
    あるいは NuxtLink の件みたいに SSG がまだ茨の道かもしれないので、素直に Netlify みたいなお馴染みのサービスを使って SSR 寄りでホスティングする方が楽な気もします
  • 別に WordPress とかでコンテンツ管理する前提じゃないにしても、コーポレートサイトとか作る上で素の HTML 書くくらいならこう言うフレームワークで構成されていく方が楽しい嬉しい時もありそうなので、 NuxtJS 単体でも何かで使う機会があると良いですね
  • Techracho みたいにひたすらページ数多くなりそうなサイトで SSG する時の処理時間とかはどうなの?ってところは要検証ですがチラっと調べた限り、千や一万ページオーダーならイケるのかもしれないです(microCMS運営さんの検証記事


CONTACT

TechRachoでは、パートナーシップをご検討いただける方からの
ご連絡をお待ちしております。ぜひお気軽にご意見・ご相談ください。