今回達成すること
今回は、ログイン前のトップページに表示されるウェルカムページを作成していきます。
以下の手順で進めていきます。
- アプリ全体で利用する、共通タイトルの設定
 - ログイン前のヘッダー(header.vue)の編集
 - Nuxt.jsにsassを導入
 - 新規会員登録ページ(signup.vue)を作成
 - ログイン前後、共通で利用するフッター(footer.vue)の編集
 - ログイン前のレイアウトファイル(home.vue)の編集
 
なお、最終的にトップページはこのようになります。
完成イメージ


筆者の
作業に移る前に
今回の記事は全てNuxt.js上での作業となります。
ターミナルは、Rails内のNuxt.jsプロジェクト(筆者の場合、myapp/frontend)に移動しておいてください。
froutend $ # ここに移動しておく
共通タイトルの設定
まず、アプリで使用する共通タイトルを設定していきます。
ヘッダーに表示したり、フッターのコピーライトに表示するアプリ名のことですね。
今のタイトルはどこから表示されているか?

現状、筆者の場合では「frontend」という名前がページタイトルとして表示されています。
これはNuxt.jsの作成時に決めたプロジェクト名が反映されています。
どこで設定されているかというと、nameというプロパティです。
frontend/package.json
{
  "name": "frontend",
	...
}
ここのnameを、headプロパティでタイトルとして設定しています。
frontend/nuxt.config.js
const config = {
  mode: 'spa',
  head: {
    title: process.env.npm_package_name || '',
    ...
つまり、
共通タイトルを書き換えて表示する
仕組みがわかったところで、
frontend/package.json
{
  "name": "demoApp", // 自由な名前でOK
	...
}
クライアント側で使えるようにenvプロパティを登録
今のままコンポーネント内でprocess.env.npm_package_nameを利用しようとしても、空の値が返されます。
何故かというと、process.envの中身はサーバー側の値であり、クライアント(ブラウザ)側からは読み込めないためです。
そこで、サーバー側の値をクライアント側でも利用できるよう、envプロパティを登録します。
frontend/nuxt.config.js
const config = {
	...
  
  // 追記
  env: {
    APP_NAME: process.env.npm_package_name
  }
}
この設定により、クライアント側でprocess.env.APP_NAMEという環境変数が利用できるようになりました。
共通タイトルを確認しましょう
ログイン前のヘッダーコンポーネント、
frontend/components/shared/header.vue
<template>
  <div>
    {{ appName }}
  </div>
</template>
<script>
export default {
  data: () => ({
    appName: process.env.APP_NAME
  })
}
</script>
package.jsonで指定したアプリ名が表示されます。

ログイン前のヘッダー「header.vue」を編集する
さて、次はこんな感じ↓にログイン前のヘッダーを編集していきます。
完成イメージ

componentsディレクトリ内の
frontend/components/shared/header.vue
<template>
  <div>
    <v-app-bar
      elevation="1"
    >
      <v-toolbar-title>
        <nuxt-link
          to="/"
        >
          {{ appName }}
        </nuxt-link>
      </v-toolbar-title>
      <v-spacer />
      <v-toolbar-items
        v-for="(link, i) in links"
        :key="i"
      >
        <v-btn
          :to="link.to"
          text
          class="font-weight-bold"
        >
          {{ link.title }}
        </v-btn>
      </v-toolbar-items>
    </v-app-bar>
  </div>
</template>
<script>
export default {
  data: () => ({
    appName: process.env.APP_NAME,
    links: [
      { title: '新規会員', to: '/signup' },
      { title: 'ログイン', to: '/login' }
    ]
  })
}
</script>
- 
v-app-barVuetifyの
v-toolbarでも同じヘッダーを作成できますが、今回はv-app-barを採用しました。v-app-barは、様々なスクロールイベントを付与することができます。 - 
elevation="1"これはツールバーの影の濃さを数字で指定します。0〜24の値を指定できます。
 
ただ...ちょっとアプリ名がリンク色に染まってブサイクですね...。

Nuxt.jsにsassを導入する
sassを導入してオリジナルのCSSクラスを作成しましょう。
今回は公式のインストール方法に従ってsassを導入します。
1. node-sass と sass-loaderをインストールする
ターミナルより、sass実行に必要なモジュールをインストールします。
frontend $ yarn add node-sass sass-loader
2. main.scssファイルの作成
assets直下にcssディレクトリと、
 frontend % mkdir assets/css && touch assets/css/main.scss
assetsディレクトリの構成は以下のようになります。
assets
├── README.md
└── css
    └── main.scss
3. nuxt.config.jsにCSSファイルを登録
続いて
frontend/nuxt.config.js
const config = {
	...
  
  css: [
    '@/assets/css/main.scss'	// 追記
  ],
  
}
以上で設定は完了です。
これでsassが利用できるようになりました。
main.scssにオリジナルクラスを作成する
frontend/assets/css/main.scss
$black: rgba(0, 0, 0, 0.87);
// commons //////////////////////////
body {
  word-wrap: break-word;
  overflow-wrap: break-word;
}
a.link-black {
  color: $black !important;
  text-decoration: none;
}
- 
$black: rgba(0, 0, 0, 0.87);$blackには、Vuetifyのブラックと一致する色を指定しています。 
header.vueにクラスを付け加える
作成したlink-blackクラスをアプリ名に付け加えましょう。
frontend/components/shared/header.vue
<template>
  ...
  <nuxt-link
    to="/"
    class="link-black font-weight-bold" <!-- 追記 -->
  >
    {{ appName }}
  </nuxt-link>
よし、これでアプリ名がブサイクではなくなりました!

新規会員登録ページ「signup.vue」を作成する
今のままヘッダーの「新規会員」をクリックすると、ページが無いためエラーになります。
そこで
frontend $ touch pages/signup.vue
作成した
frontend/pages/signup.vue
<template>
  <div>
    新規会員登録ページ
  </div>
</template>
<script>
export default {
}
</script>
これにて「header.vue」は完成です
以上でログイン前のheader.vueは完成です。

共通のフッター「footer.vue」を編集する
続いて
完成イメージ

frontend/components/shared/footer.vue
<template>
  <v-footer
    padless
  >
    <v-col
      class="text-center"
      cols="12"
    >
      <div class="body-2">
        &copy;{{ copyRightYear }} — <strong>{{ appName }}</strong>
      </div>
    </v-col>
  </v-footer>
</template>
<script>
export default {
  data: () => ({
    appName: process.env.APP_NAME
  }),
  computed: {
    copyRightYear () {
      const start = 2019
      const now = new Date().getFullYear()
      return (start === now) ? start : `${start} - ${now}`
    }
  }
}
</script>
- 
padlessこれはfooterの
paddingを削除するVuetifyのプロパティです。 - 
copyRightYear ()現在の年度とアプリ制作年度を比較して、コピーライトの表記を自動化しています。
2019年の場合 => 「©2019 — demoApp」
2020年の場合 => 「©2019 - 2020 — demoApp」
 
OK!完成イメージ通りのフッターが作成できましたね。
最後。レイアウトファイル「home.vue」を編集する
layputsディレクトリ内に作成した
よくwebアプリのトップページにある「ようこそ」や「アプリの説明」を表示するページのことです。
<v-content>タグ内に、自由にコンテンツを作成してください。
frontend/layouts/home.vue
<template>
  <v-app>
    <my-header />
    <v-content>
     <!-- ここに自由に追記 -->
    </v-content>
    <my-footer />
  </v-app>
</template>
<script>
import myHeader from '~/components/shared/header.vue'
import myFooter from '~/components/shared/footer.vue'
export default {
  components: {
    myHeader,
    myFooter
  },
  data: () => ({
    appName: process.env.APP_NAME
  })
}
</script>
- 
<v-content>大切なのは、この
<v-content>タグの中にメインコンテンツを書くということです。もしこのタグで囲まない場合、コンテンツの高さが足りない時にフッターが上がってきます。
これはVuetifyのレイアウトの仕組みによるものです。
 
v-contentタグで囲まない場合の悪い例

本番環境にpushしておきましょう
さて、home.vueの編集が完了したら、Railsプロジェクトに戻ってHerokuにPushしましょう。
frontend $ cd ../
myapp $ git add -A
myapp $ git commit -m "welcome_page_layouts"
myapp $ git checkout master
myapp $ git merge login_layouting
myapp $ git push
myapp $ git push heroku
pushが完了したら元のレイアウトブランチに戻っておきます。
myapp $ git checkout login_layouting
myapp $ git branch                  
* login_layouting
  master
まとめ
さて、今回の学んだポイントをおさらいします。
- アプリの共通タイトルは
package.json のnameプロパティで管理する。 - サーバーサイドとフロントエンドで環境変数を共有したい場合は、
nuxt.config.js のenvに登録する。 - Nuxt.jsにsassを導入する時は、
node-sassとsass-loaderモジュールをインストールする。 - メインコンテンツは、
<v-content>タグで囲む。(Vuetifyの場合) 
以上となります。
お疲れでした!(長い戦いだった。。。)
さて、次回は?
さてさて、次回「新規会員登録ページ」のレイアウト構築と、メールアドレス、パスワードの入力フォームの導入を行います。
どうぞ、お楽しみに!
筆者のhome.vue
筆者のサンプルコードです。
frontend/layouts/home.vue
<template>
  <v-app>
    <my-header />
    <v-content>
      <v-parallax
        dark
        src="https://cdn.vuetifyjs.com/images/backgrounds/vbanner.jpg"
      >
        <v-row
          align="center"
          justify="center"
        >
          <v-col
            class="text-center"
            cols="12"
            sm="8"
          >
            <h1 class="display-1 mb-4">
              ようこそ!{{ appName }}へ
            </h1>
            <v-list-item
              href="https://blog.cloud-acct.com/categories/spa"
              target="_blank"
              rel="noopener noreferrer"
            >
              <v-list-item-content>
                <h2 class="subtitle-1 font-weight-medium white--text">
                  このアプリの作り方を見る
                </h2>
              </v-list-item-content>
            </v-list-item>
          </v-col>
        </v-row>
      </v-parallax>
      <v-container>
        <v-row
          justify="center"
        >
          <v-col
            class="text-center"
            cols="12"
          >
            <div>
              {{ appName }}では以下の技術を採用しています。
            </div>
          </v-col>
          <v-col
            v-for="(item, i) in cardItems"
            :key="i"
            class="text-center"
            cols="12"
            :sm="12 / cardItems.length"
          >
            <v-avatar
              :color="item.color"
              :size="(600 / cardItems.length) - 30"
            >
              <span class="white--text display-1">
                {{ item.name }}
              </span>
            </v-avatar>
            <v-card
              flat
              color="transparent"
            >
              <v-card-title class="justify-center">
                {{ item.name }} {{ item.version }}
              </v-card-title>
              <v-card-text>
                {{ item.description }}
              </v-card-text>
            </v-card>
          </v-col>
          <v-col
            class="text-center"
            cols="12"
          >
            <div>
              このアプリの作り方は、チュートリアル形式で公開されています。
            </div>
            <a
              href="https://blog.cloud-acct.com/categories/spa"
              target="_blank"
              rel="noopener noreferrer"
            >
              このアプリの作り方を見る
            </a>
          </v-col>
        </v-row>
      </v-container>
    </v-content>
    <my-footer />
  </v-app>
</template>
<script>
import myHeader from '~/components/shared/header.vue'
import myFooter from '~/components/shared/footer.vue'
export default {
  components: {
    myHeader,
    myFooter
  },
  data: () => ({
    appName: process.env.APP_NAME,
    cardItems: [
      {
        name: 'Rails',
        color: '#CC3C33',
        version: '6.0.0',
        description: 'サーバーサイドにRails APIモードを採用'
      },
      {
        name: 'Nuxt.js',
        color: '#00C550',
        version: '2.10.2',
        description: 'フロントエンドにNuxt.js SPAモードを採用'
      },
      {
        name: 'Vuetify',
        color: '#2271C6',
        version: '2.1.9',
        description: 'UIフレームワーク'
      }
    ]
  })
}
</script>