Sinatra+Vue.jsでSPAを作っていきます。
SinatraやVue.jsが初めてという方は
sinatra+vue+webpackで作成したアプリをHerokuで公開する(初心者向け) - Qiita
こちらも合わせてどうぞ。
完成したコードはこちらで見ることができます。
フロントエンドの準備
vue-cliを使うのでインストールします。
$ npm install -g vue-cli
vue templateを使います。
$ vue init isuke/vuejs-template#v0.8.0 sinatara-vue-sample
isuke/vuejs-template
は著者が、公式のvuejs-templates/webpack-simple
をもとに作ったvue.jsのテンプレートです。
vuejs-templates/webpack-simple
とくらべて以下の違いがります。
- babelは使わずにcoffeescriptを使う
- scssは使わずにstylusを使う
- vue-routerが入っている
- axiosが入っている
基本的には同じなのでbabelが使いたい方などはvuejs-templates/webpack-simple
を使ってもらっても大丈夫です。
$ yarn # npm installでも可
$ npm run dev
http://localhost:8080/ をブラウザで開いてみましょう。
一行もコードを書かずにこれだけでwebページができていることがわかると思います。
[Bye]リンクをクリックしてページ遷移ができていることも確認してみてください。
サーバーサイドの作成
ここからsinatraを使っていきます。
$ bundle init
# frozen_string_literal: true
source "https://rubygems.org"
ruby '2.4.1'
gem 'rack-proxy'
gem 'sinatra'
gem 'sinatra-contrib'
$ bundle install
rack-proxy
という見慣れないものがありますが、これは後で説明します。
sinatraのコードを書いていきます。
require 'sinatra/base'
require "sinatra/reloader"
class Server < Sinatra::Base
configure :development do
register Sinatra::Reloader
end
get '/companies.json' do
companies = []
1.upto(50) do |i|
companies << {
id: i,
name: "Company #{i}"
}
end
companies.to_json
end
end
require './server'
run Server
何の変哲もない APIが一つあるだけです。
立ち上げて、確認してみましょう。
$ bundle exec rackup
jsonが返ってきました。
次のステップに進みましょう。
フロントとサーバーの連携
フロントはnpm run dev
でlocalhost:8080
サーバーはbundle exec rackup
でlocalhost:9292
にそれぞれアクセスできるようになりました。
これをくっつけましょう。
先程のconfig.ru
をrack-proxy
をつかって書き換えます。
require './server'
require 'rack-proxy'
class AppProxy < Rack::Proxy
def rewrite_env(env)
env['HTTP_HOST'] = 'localhost:8080'
env
end
end
run Rack::URLMap.new(
'/api' => Server,
'/' => AppProxy.new
)
サーバーを2つとも立ち上げてlocalhost:9292を見てみましょう。
$ npm run dev
$ bundle exec rackup
ここでトップページが見れたらOKです。
なぜlocalhost:9292でトップページが見れたのかというと、先程のconfig.ru
の設定によりlocalhost:9292がlocalhost:8080にマップされたからですね。
さて、これでフロントからAPIが叩けるようになったので、ちょっと試してみましょう。
Bye.vueを以下のように書き換えます。
<template lang="pug">
.bye
button(@click="load") load
ul(v-for="company in companies")
li {{company.name}}
router-link(:to="{ name: 'top'}") TOP
</template>
<script lang="coffee">
module.exports = {
name: 'bye'
data: ->
companies: []
methods:
load: ->
axios.get('/api/companies.json')
.then (responce) =>
@companies = responce.data
}
</script>
<style lang="stylus">
.bye {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
h1 {
font-weight: normal;
}
a {
color: #42b983;
}
}
</style>
[load]ボタンを押してデータが表示されていれば成功です。
ついでにHot Module Replacementが動作しているかもためしてみましょう。
Compnayデータが表示された状態でBye.vue
のstyleを適当に変えてみてください。
.bye {
font-family: 'Avenir', Helvetica, Arial, sans-serif;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
text-align: center;
color: #2c3e50;
margin-top: 60px;
+ background-color: red;
h1 {
font-weight: normal;
}
a {
color: #42b983;
}
}
ブラウザを更新せずに反映されたら成功です。
Hot Module Replacementが動作しています。
Productionモードで動かす
最後にProductionでの動かし方です。
まずnpm run build
を実行してsrc
ディレクトリの中をコンパイルします。
コンパイルした結果はdist
に吐かれます。
$ npm run build
次にURLが
-
/api
で始まる場合はsinatra server -
/dist
で始まる場合はdist
ディレクトリ -
/
の場合はindex.html
を返すようにconfig.ru
を書き換えます。
require './server'
require 'rack-proxy'
class Index
def call(env)
[
200,
{ 'Content-Type' => 'text/html' },
[File.read('./index.html')]
]
end
end
class AppProxy < Rack::Proxy
def rewrite_env(env)
env['HTTP_HOST'] = 'localhost:8080'
env
end
end
if ENV["APP_ENV"] == "production"
run Rack::URLMap.new(
'/api' => Server,
'/dist' => Rack::Directory.new("./dist"),
'/' => Index.new
)
else
run Rack::URLMap.new(
'/api' => Server,
'/' => AppProxy.new
)
end
あとは環境変数をつけて実行してあげるだけです。
$ APP_ENV=production bundle exec rackup
以上です。お疲れ様でした。