はじめに
運営してるゲームサークルのWebのフロントを Vue.js
でリプレースした話です。
ちなみに、実際のWebはこちら
ソースはこちら
開発環境
Windows 10 Pro
Rails:5.2.1
npm:6.1.0
yarn:1.7.0
Vue.js:2.5.16
やったこと
Webpackerの導入
もともとはRailsでWebサイトを構築しており、Vue.jsをwebpacker経由で導入する必要があった。
そのため既存のRails環境にVue.jsを追加するを参考にして以下のように導入した。
gem 'webpacker'
まず、Gemfile
にwebpacker
を追加して、bundle install
それから、以下のコマンドでVue.js
を導入。
rake webpacker:install
rake webpacker:install:vue
あとは、application.html.erb
にこれを追加するだけ
head
= javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'
これで、Vue.js
を既存のRailsアプリに導入できた。
各ページをVue.jsへリプレース
テンプレートの作成
うちのWebでは各ページごとにタイトルやちょっとした説明文などを書いてた。
その辺をtemplate.vue
とtemplate.js
で共通のコンポーネントにして使いまわすことにした。
<template>
<div id="template">
<title>{{title}}</title>
<h1>{{about}}</h1>
<h2>{{message}}</h2>
<br>
<br>
<br>
</div>
</template>
<script>
export default {
data: function () {
return {
title: page_title,
about: page_about,
message: page_message
}
}
}
</script>
import Vue from 'vue/dist/vue.esm'
import Template from './components/template.vue'
document.addEventListener('DOMContentLoaded', () => {
const el = document.body.appendChild(document.createElement('template'))
const template = new Vue({
el,
render: h => h(Template)
})
console.log(template)
})
あとは、作ったテンプレートを各ページで以下のように呼び出してやるだけ
ちなみに、page_title
などで各ページの説明文などをJavaScript
経由で渡してる
javascript:
var page_title = "About";
var page_about = "About";
var page_message = "「人と人を繋ぐゲームデザイン」をモットーに日々ゲーム制作に力を入れているゲームデザインサークルです。";
= javascript_pack_tag 'template'
ModelのデータをVue.js 側に受け渡す
下記のようにJavaScript
を経由して、JSON
形式で渡している
javascript:
var products = #{{@products.to_json}};
var members = #{{@members.to_json}};
= javascript_pack_tag 'product'
= javascript_pack_tag 'member'
個別に作成しておいたproduct.vue
などを= javascript_pack_tag 'product'
で呼び出している。
<template>
<div id="product">
<h1>Product</h1>
<h2 v-for="p in product">
<p><a href="p.link">{{p.name}}</a></p>
<p>{{p.about}}</p>
<br><br>
</h2>
</div>
</template>
<script>
export default {
data: function () {
return {
product: products
}
}
}
</script>
import Vue from 'vue/dist/vue.esm'
import Product from './components/product.vue'
document.addEventListener('DOMContentLoaded', () => {
const el = document.body.appendChild(document.createElement('product'))
const product = new Vue({
el,
render: h => h(Product)
})
console.log(product)
})
キモはproduct: products
と<h2 v-for="p in product">
product: products
でJSON
形式でRailsからデータを受け取り、<h2 v-for="p in product">
でそれを一つづつ取り出してる。
だいたいのページはこれを流用する形でリプレースできた
画像の動的描画
うちのWebには制作したノベルゲームの一覧ページがある。そのページではsplitメソッド、eachメソッドやimage_tag
を使って描画していた
def game
@games = Game.page(params[:page]).per(PER)
@games.each do |game|
game.images = game.images.split(",")
end
end
- @games.each do |game|
l
h1
= game.title
- image_path = game.images.split(",")
= image_tag image_path[0], :width =>"300", :height => "200"
= image_tag image_path[1], :width =>"300", :height => "200"
br
br
h2
= game.about
= button_to "ダウンロード", game.link
で、Vue.js
でリプレースするにあたって以下のようにしてみた
javascript:
var page_title = "Game";
var page_about = "Game";
var page_message = "ゲームリンクスでこれまでに制作してきたゲームです。ダウンロードボタンをクリックするとダウンロード先へ移動します。";
var games = #{{@games.to_json}};
= javascript_pack_tag 'template'
= javascript_pack_tag 'game'
<template>
<div id="game">
<h2 v-for="g in game">
<p><h1>{{g.title}}</h1></p>
<p v-for="img in g.images">
<img :src="require('../../../assets/images/' + img.replace('\'./ ', ''))" width="300" height="200">
</p>
<p><h1>{{g.about}}</h1></p>
<p><a href="g.link" class="button">ダウンロード</a></p>
</h2>
</div>
</template>
<script>
for(var i = 0; i < games.length; i++){
games[i].images = games[i].images.split(",");
for(var j = 0; j < 2; j++){
games[i].images[j] = games[i].images[j].replace('"', '').replace('[', '').replace('"', '').replace(']', '').replace(' ', '');
console.log(games[i].images[j]);
}
}
export default {
data: function () {
return {
game: games
}
}
}
</script>
import Vue from 'vue/dist/vue.esm'
import Game from './components/game.vue'
document.addEventListener('DOMContentLoaded', () => {
const el = document.body.appendChild(document.createElement('game'))
const game = new Vue({
el,
render: h => h(Game)
})
console.log(game)
})
var games = #{{@games.to_json}};
でデータをJSON
形式で受け渡してる
それをfor
で回して成型
for(var i = 0; i < games.length; i++){
games[i].images = games[i].images.split(",");
for(var j = 0; j < 2; j++){
games[i].images[j] = games[i].images[j].replace('"', '').replace('[', '').replace('"', '').replace(']', '').replace(' ', '');
console.log(games[i].images[j]);
}
}
そして、game
へとデータを渡している
export default {
data: function () {
return {
game: games
}
}
}
あとはv-for
で画像ファイルの名前を一つずつとりだしている。
<template>
<div id="game">
<h2 v-for="g in game">
<p><h1>{{g.title}}</h1></p>
<p v-for="img in g.images">
<img :src="require('../../../assets/images/' + img.replace('\'./ ', ''))" width="300" height="200">
</p>
<p><h1>{{g.about}}</h1></p>
<p><a href="g.link" class="button">ダウンロード</a></p>
</h2>
</div>
</template>
v-bind
したsrc
とrequire
で画像へのパスを読み込んで描画させている。
これで動的に画像を描画できた。
スライダーの作成
トップページにスライダーを作っていたので、それもVue.js
で置き換えた。
元々のソースは以下
title GAMELINKS
css class="text-align:center"
== render 'slider'
.flexslider
ul class="slides"
- @slide.each do |slide|
li
= image_tag slide
*= require flexslider.css
//= require jquery.flexslider
$(window).load(function() {
$('.flexslider').flexslider({
animation: "slide"
});
});
それを以下のように変更した。
title GAMELINKS
javascript:
var slides = #{{@slide.to_json}};
= javascript_pack_tag 'slide'
<template>
<div id="slide">
<slider animation="fade" height="600px">
<p style="line-height: 600px; font-size: 5rem; text-align: center;" v-if="!list.length">Loading...</p>
<slider-item v-for="i in list">
<p style="line-height: 680px; font-size: 5rem; text-align: center;">
<img :src="require('../../../assets/images/' + i.path)" width="800" height="600" >
</p>
</slider-item>
</slider>
</div>
</template>
<style>
p {
margin: 0;
}
</style>
<script>
import { Slider, SliderItem } from 'vue-easy-slider'
var array = [];
for(var i = 0; i < slides.length; i++){
array.push({path: slides[i]});
}
export default {
data: function () {
return {
list: array
}
},
mounted () {
setTimeout(() => {
this.list
}, 1000)
},
components: {
'Slider': Slider,
'SliderItem': SliderItem
}
}
</script>
import Vue from 'vue/dist/vue.esm'
import Slide from './components/slide.vue'
document.addEventListener('DOMContentLoaded', () => {
const el = document.body.appendChild(document.createElement('slide'))
const slide = new Vue({
el,
render: h => h(Slide)
})
console.log(slide)
})
vue-easy-slider
というVue.js
で使えるシンプルなスライダーがあったのでそれを使っている。
@slide
を例のごとくJSON
で渡してやり、それをvue-easy-slider
で扱えるデータに成型しなおしている
あとは画像の動的描画で使ったのと同じ方法(v-for
+ v-bind:src
+ require
)で画像を読み込んで表示させている。
おわりに
とりあえず、こんな感じで凡そのページは改修できた。
まだいくつか手を加えていきたいのでその辺はまた追記していこうかと思う。
あと、今回初めてまもとにJavaScript
を書いたのでおかしなところがあれば編集リクエストなどを投げていただければと思います。