この記事は その1:Rails編 の続きです。
何を作っているか
DBの操作と管理画面はRailsで作成しつつ、一般ユーザ向けの画面はSPAとかPWAにしたいというシーンは結構あるかと思います。既存アプリがRailsで動いているのを活かしながら、フロント側はSPA化するとか。基本はSPAなんだけど、管理画面はscaffoldでサクッと作って済ませたいとか。
そんなことを考えながら、このシリーズではRails(管理画面&API)
+Vue.js&Nuxt.js(SPA)
という構成のアプリを、以下の役割分担で作っています。
Rails(View) | Rails(API) | Vue&Nuxt | |
---|---|---|---|
コンテンツの表示(一般公開) | ○ | ○ | ○ |
コンテンツの編集(管理者限定) | ○ | - | - |
この記事では、Vue.jsとNuxt.jsでSPAの画面を作成し、Railsで作成したAPIと連携するところを掲載します。
アプリの題材はポートフォリオ。実はもうリリース済みです。
Vue.js&Nuxt.js側の準備
SPAモードで作ります。
言語はJavaScriptを選択しました。
Vuetifyは最初は1.5系が入ってしまうので、2.0系に後から上げます。
###2020.3.14追記
1.5系が入ってしまうというのは勘違いだった可能性があります。
nuxt-community/vuetify-moduleのCHANGELOGによれば、2019−07−23時点でVuetify2に対応したと書いてありました。
追記時点ではすでにVuetify2系にアップデート済みなので確認できませんが、yarn list
で確認を取っていればどのバージョンが入っているか確実に確認できたはずです。
ちなみに、追記時点はでvuetify@2.1.4
でした。
###追記おわり
##環境
- Vue.js 2.6.11
- Nuxt.js 2.11.0
- Vuetify 2.1.0
- 開発環境はmacOS Mojaveです。1
前提
- Nuxt.jsはローカルにインストール済み
- Githubはアカウント取得済み
プロジェクト生成
「portfolio-vue」と言うプロジェクト名で進めます。
Nuxt公式に従います。
https://ja.nuxtjs.org/guide/installation/
$ yarn create nuxt-app portfolio-vue
この後の選択肢は、以下のようにしました。
? Project name portfolio-vue
? Project description shozzy's portfolio
? Author name shozzy
? Choose the package manager Yarn
? Choose UI framework Vuetify.js
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios
? Choose linting tools Prettier
? Choose test framework Jest
? Choose rendering mode Single Page App
? Choose development tools jsconfig.json (Recommended for VS Code)
しばらく待ったのち、、、
🎉 Successfully created project portfolio-vue
To get started:
cd portfolio-vue
yarn dev
To build & start for production:
cd portfolio-vue
yarn build
yarn start
To test:
cd portfolio-vue
yarn test
✨ Done in 72.01s.
うまく行きました!
$ cd portfolio-vue
$ yarn dev
ブラウザから http://localhost:3000/ にアクセスします。
このような画面になれば成功です 2
VSCodeで「Add Folder to Workspace...」から「portfolio-vue」を選択します。
(不要なはず)Vuetifyを2.0系にする
2020.03.14追記:前述の通り、この手順は不要と思われます。そのため、記載内容を折りたたみました。
yarn list
で表示されるvuetifyのバージョンを確認しておきましょう。
以前の記載内容
変更前
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/axios',
],
変更後
modules: [
// Doc: https://axios.nuxtjs.org/usage
'@nuxtjs/vuetify',
'@nuxtjs/axios',
],
Vuetify公式によれば、執筆時点3での最新はv2.1.4のようなので、そこまで上げておきます。
https://github.com/vuetifyjs/vuetify/releases
$ yarn add vuetify@2.1.4
念のため、Vuetify2.0で動作しているか確認しておきます。
方法は、デフォルトで生成されるindex.vueの中身をVuetify2.0から導入された「v-row」「v-col」を用いるように変更し、それがブラウザで正しく表示されるかを確認します。4
変更前
<template>
<v-layout
column
justify-center
align-center
>
<v-flex
xs12
sm8
md6
>
<!-- 中略 -->
<v-card-title class="headline">
Welcome to the Vuetify + Nuxt.js template
</v-card-title>
<!-- 中略 -->
</v-flex>
</v-layout>
</template>
変更後
<template>
<v-row
column
justify-center
align-center
>
<v-col
xs12
sm8
md6
>
<!-- 中略 -->
<v-card-title class="headline">
Welcome to the Vuetify2.0 + Nuxt.js template
</v-card-title>
<!-- 中略 -->
</v-col>
</v-row>
</template>
GitHubへ初期コミット
ここらでGithubにfirst commitしておきます。
$ git init
Reinitialized existing Git repository in /Users/shozzy/portfolio-vue/.git/
どうやら最初からgitもセットアップされていたようです。
git initしても害はなさそうなので、そのまま進めました。
もしGithubを使うのが初めてであれば、メールアドレスの設定が必要です。
Githubのサイトへログインし、新しいリポジトリを作成します。
プライベートリポジトリでも問題ありません。
.gitignore はnuxtがいい感じに設定してくれているので、そのままでOKです。
$ git add .
$ git commit -m "first commit"
$ git remote add origin https://github.com/shozzy/portfolio-vue.git
$ git push -u origin master
ソース管理を開始できたので、ここから開発を進めていきます。
ブランチを切りつつチェックアウトしておきます。
$ git checkout -b create-pages
Nuxtのポートを変更
RailsもNuxtも、開発環境のポートがデフォルトでは3000です。
これでは同時に開発できないので、片方を変更する必要があります。
ここでは、Nuxtの方を8000番に変更しました。(http://localhost:8000/)
変更前
export default {
mode: 'spa',
変更後
export default {
server: {
port: 8000,
},
mode: 'spa',
中身を作る
ここから中身を作っていきます。
※説明を最小限にするため、解説している内容と、キャプチャ画像の見た目が異なる場合があります。
productsページ
先に、実際にAPIを使うページから作っていきます。
productsはこれまでの作品を紹介するページです。
コンテンツはAPIから取得したものを表示します。
productsページはhttp://localhost:8000/products
コンテンツを一覧で取得するAPIはhttp://localhost:3000/api/contents
で用意してあります。バックエンドはRails。
以下のファイルを新規作成します。
最初は単純に、APIで取得したJSONをそのまま表示するだけの内容です。
<template>
<div>
{{ contents }}
</div>
</template>
<script>
export default {
async asyncData({ app }) {
const contents = await app.$axios.$get('https://localhost:3000/api/contents')
return { contents }
},
}
</script>
、、、実は、これだとCORSエラーになります。
Nuxt側のOriginはlocalhost:8000
ですが、Rails側のOriginはlocalhost:3000
だからですね。
CORS (Cross-Origin Resource Sharing) を出来るようにする
クロスオリジンでのアクセスには制限がかかっています。CSRFを防ぐために。
そこで、Rails側でgem 'rack-cors'
を使ってNetlify側からのアクセスを許可します。
###Rails側
Rails側にrack-cors
の設定を追加します。
gem 'rack-cors'
$ bundle install
ここでconfig/initializers/cors.rb
の設定が必要になりますが、今回のRails環境はAPIモードではないので、このファイルは自動生成されません。なので、手動で作リます。
Rails.application.config.middleware.insert_before 0, Rack::Cors do
allow do
origins Rails.application.config.permitted_origin
resource '*',
headers: :any,
methods: [:get, :post, :put, :patch, :delete, :options, :head]
end
end
許可する呼び出し元は、ローカルと本番環境で当然異なるので、これも環境変数で定義します。5
開発環境ではdotenv-railsを利用します。
group :development do
#中略
gem 'dotenv-rails'
end
PERMITTED_ORIGIN = 'localhost:8000'
config.permitted_origin = ENV.fetch('PERMITTED_ORIGIN', 'http://localhost:8000')
これで、Vue側(localhost:8000)からのアクセスが許可されました。
###Vue側
ついでに、Vue側にも環境変数「API_BASE」を作成し、APIが存在するドメインを設定できるようにしておきます。
ローカルの開発環境と本番環境ではドメインが異なるためです。
ローカルではdotenvを用います。6
$ yarn add @nuxtjs/dotenv
API_BASE=http://localhost:3000
env: {
API_BASE: process.env.API_BASE,
},
buildModules: [
'@nuxtjs/dotenv',
],
これで、コード中でprocess.env.API_BASE
を用いて環境変数の内容を読み出せるようになりました。
<script>
export default {
async asyncData({ app }) {
const contents = await app.$axios.$get(process.env.API_BASE+'/api/contents')
return { contents }
},
}
</script>
###JSONをAPI経由で取得する
ようやく、JSONでデータを取得できるようになりました。
見た目を整える
JSONのままではサイトとして公開できませんので、見た目を調整していきます。
各アイテムを表示するカード部分は繰り返し登場するので、コンポーネント化しました。
<template>
<div>
<v-container fluid>
<v-row row wrap>
<v-col v-for="item in contents" v-bind:key="item.id" xs12 sm6 md4>
<ProductCard v-bind:title="item.title" v-bind:content="item.detail"/>
</v-col>
</v-row>
</v-container>
</div>
</template>
<script>
import ProductCard from '~/components/ProductCard.vue'
export default {
components: {
ProductCard
},
async asyncData({ app }) {
const contents = await app.$axios.$get('/api/contents')
return { contents }
},
}
</script>
コードを全て掲載すると長くなりすぎるので、詳細はGitHubにて。
まとめ
この記事では、コンテンツをREST API経由で取得し、SPAのサイト内で表示させるところまで作成しました。
次の記事(鋭意執筆中)では、本番環境へのデプロイを実施します。本番環境はNetlify+Herokuの組み合わせを想定しています。
推敲にはかなり時間をかけましたが、おかしいところがありましたら教えて頂けますと幸いです。
参考にしたWebサイト
執筆者の皆様、本当にありがとうございます!
- https://developers.google.com/web/updates/2019/02/rendering-on-the-web
- https://jp.vuejs.org/v2/cookbook/using-axios-to-consume-apis.html
- https://public-constructor.com/nuxtjs-with-axios/
- https://qiita.com/sygnas/items/7eac9491b37a1bcba0cb
- https://qiita.com/nishinoshake/items/eae57372e2fd360438e0
- https://qiita.com/IzumiSy/items/c10949e9a00d1c61613c
-
大枚はたいて買ったAdobeCS6を使い続けたいから上げられない、、、(どこかで諦めるしかありませんが) ↩
-
今回はVuetifyを採用したのでこの画面ですが、他のCSSフレームワークを選択した場合は、異なる見た目の画面になると思われます。 ↩
-
この部分を書いたのは2019/10/16。公開までだいぶ時間があいてしまいました。。。 ↩
-
もっとスマートな方法があるような気がしています。ご存知のかたいらっしゃいましたら、教えていただけると幸いです。 ↩
-
今回は完全にクローズドなAPIなのでアクセス元の制限をかけています。オープンなAPIなら制限なしもありえますし、特定のユーザだけに解放するようなAPIであればAPIキーでの認証になるはずです。 ↩
-
本番環境(Netlify)では "Environment variables" で定義しました。 ↩
-
コンテンツ自体も編集しながら進めているので、JSONのキャプチャと見た目を整えた後のキャプチャでは内容が異なっています。 ↩