Nuxt+Bulma+S3でライブハウス支援サイトを作った

最近Nuxt+Bulma+S3でコロナウィルスで苦しんでいるライブハウスを支援するまとめサイトを作ったので、それについて書いていきます。最終的に完成したものは以下になります。
https://besidethemusic.tokyo

作ろうと思った経緯などはこちらのnoteをご覧ください。
https://note.com/gch/n/n881c9d0cb72d

技術選定は何か自分が触ったことがない技術で作ってみたいということで、Nuxt+Bulmaにしました。バックエンドはCloudFront+S3で良く触ってる技術なのですが、がっつりTerraformで作りました。

NuxtはSSRやSPAとして使うケースが多いと思いますが、静的サイトジェネレーターとしても使うことができ便利です。

## Nuxt.js

Nuxt.jsはVue.jsベースのJavaScriptのフレームワークで、簡単にアプリケーションを作ることができます。完成したものはこちらです。
https://github.com/yuzoiwasaki/besidethemusic-nuxt

Nuxtでアプリケーションを作るにはまず `create-nuxt-app` します。

```sh
$ npx create-nuxt-app <project-name>
```

or 

```sh
$ yarn create-nuxt-app <project-name>
```

そうするといくつか質問されるので答えていきましょう。内容は後から変更することも可能です。今回はCSSフレームワークにBulmaを使ったため、UIフレームワークはBulmaを選択しました。また、NuxtのモードはSSRではなくSPAを選択します。

全ての質問に答え終わるとアプリケーションの雛形が作成されます。この状態で `yarn dev` としてサーバを立ち上げ `localhost:3000` にアクセスするとデフォルトページが立ち上がっています。ここから自分好みにカスタマイズしていきましょう。

Nuxtのディレクトリ構成

```sh
.
└── besidethemusic
    ├── README.md
    ├── assets
    ├── components
    ├── layouts
    ├── middleware
    ├── node_modules
    ├── nuxt.config.js
    ├── package.json
    ├── pages
    ├── plugins
    ├── static
    ├── store
    └── yarn.lock
```

デフォルトのトップページが `pages/index.vue` なので、こちらのファイルを編集していきます。何も考えずに作るならひたすらこのファイルにコードを書いていけばいいのですが、肥大化してしまうのでコンポーネント化して `components` 以下に置きました。

```vue:pages/index.vue
<template>
  <div id="main">
    <Hero />
    <Statement />

    <SupportSite />
    <Crowdfunding />

    <Footer />
  </div>
</template>

<script>
import Hero from '@/components/Hero.vue'
import Statement from '@/components/Statement.vue'
import SupportSite from '@/components/SupportSite.vue'
import Crowdfunding from '@/components/Crowdfunding.vue'
import Footer from '@/components/Footer.vue'

export default {
  components: {
    Hero,
    Statement,
    SupportSite,
    Crowdfunding,
    Footer
  }
}
</script>

...
```

メタタグ系は `nuxt.config.js` に記載していきます。

```javascript:nuxt.config.js
const title = 'Beside the Music'
const description = 'コロナウィルスで苦しんでいるライブハウスを支援するためのまとめサイトです。みんなで音楽を支援しよう！いつも音楽のそばに'
const type = 'website'
const url = 'https://besidethemusic.tokyo'
const image = 'https://besidethemusic.tokyo/images/ogp.jpg'

export default {
  mode: 'spa',

  /*
  ** Headers of the page
  */
  head: {
    title: title,
    meta: [
      { charset: 'utf-8' },
      { name: 'viewport', content: 'width=device-width, initial-scale=1' },
      { hid: 'description', name: 'description', content: description },
      { hid: 'og:type', property: 'og:type', content: type },
      { hid: 'og:title', property: 'og:title', content: title },
      { hid: 'og:description', property: 'og:description', content: description },
      { hid: 'og:url', property: 'og:url', content: url },
      { hid: 'og:image', property: 'og:image', content: image },
      { hid: 'fb:app_id', property: 'fb:app_id', content: '667885473990531' },
      { hid: 'twitter:card', name: 'twitter:card', content: 'summary_large_image' },
      { hid: 'twitter:site', name: 'twitter:site', content: '@besidethemusic' },
    ],

...
```

今回はwebpackなどは使わないため、アセットは `static` 以下に置きました。

サイトが完成したら `yarn generate` とすると `dist` 以下に成果物が生成されるので、こちらを丸ごとホスティングサイトにアップロードします。


## Bulma

BulmaはBootstrapのようなCSSフレームワークで、CSSのみで完結し軽量な点が特徴です。Flexboxをベースにしているため、簡単にレイアウトを作ることができます。レスポンシブに対応しているのも嬉しいところです。
https://bulma.io

Bootstrapで作るとjQueryがセットになってしまうのと、どうしてもBootstrapっぽさが出てしまうので、Bulmaはなかなか良かったです。

Vue.jsだとBuefyというVuejs+Bulmaで実装されたUIコンポーネントライブラリもあるので、こちらでも良かったかもしれません。
https://buefy.org

BulmaはBootstrapに馴染みがある人なら問題なく触れると思います。自分のように普段あまりCSSを書かない人間にとってはこうしたCSSフレームワークはありがたいです。

こちらにBulmaで作られたサイトがまとまっておりとても参考になります。
https://bulma.io/expo


## S3

インフラはAWSのCloudFront+S3で作りました。自分はSREなのでこちらの作業は速かったです。全てTerraformで作ってますので、興味がある人は覗いてみてください。
https://github.com/yuzoiwasaki/besidethemusic-terraform


## ハマったところ

どれも初歩的なものなのですが、同じようにつまづいた人のために実装中にハマったところを書いておきます。

### Component template should contain exactly one root element. If you are using v-if on multiple elements, use v-else-if to chain them instead.

`pages/index.vue` を編集すると上記のメッセージが出ました。原因はルートエレメントが複数あるからのようです。

こうではなく

```vue:pages/index.vue
<template>
  <div class="container">
  <div>

  <div class="container">
  <div>
</template>
```

こうします

```vue:pages/index.vue
<template>
  <div id="main">
    <div class="container">
    <div>

    <div class="container">
    <div>
  </div>
</template>
```

### index.htmlを開いても何も表示されない

`yarn generate` してできた `index.html` をクリックしても何も表示されませんでした。が、S3にアップロードすると問題なく表示されたためそういうもののようです。

### TwitterのみOGPが表示されない

FacebookやLINE、Slackなどは問題なくOGPが表示されたのに、なぜかTwitterだけ表示されませんでした。結論としてはパスの指定の仕方が悪かったようで、URLでフルパス指定したら表示されました。

Facebookでもエラーになっていたようですが、推測して表示できていたようです。シェアデバッガーでエラーが出ていたので気づきました。
https://developers.facebook.com/tools/debug/?locale=ja_JP

Twitter Card Validatorでは成功したログしか出ていなかったため解決に苦労しました。Twitter。。

## 最後に

以上、Nuxt+Bulma+S3でサイトを作ったまとめでした！初歩的な内容が多いですが、少しでも誰かの参考になれば幸いです！最後までお読みいただきありがとうございました！