LoginSignup
3
7

More than 5 years have passed since last update.

WebサイトのフロントエンドをVue.js でリプレースしてみた

Posted at

はじめに

運営してるゲームサークルの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を追加するを参考にして以下のように導入した。

Gemfile
gem 'webpacker'

まず、Gemfilewebpackerを追加して、bundle install

それから、以下のコマンドでVue.jsを導入。

terminal
rake webpacker:install
rake webpacker:install:vue

あとは、application.html.erbにこれを追加するだけ

application.html.slim
head
    = javascript_pack_tag 'application', 'data-turbolinks-track': 'reload'

これで、Vue.jsを既存のRailsアプリに導入できた。

各ページをVue.jsへリプレース

テンプレートの作成

うちのWebでは各ページごとにタイトルやちょっとした説明文などを書いてた。
その辺をtemplate.vuetemplate.jsで共通のコンポーネントにして使いまわすことにした。

template.vue
<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>
template.js
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経由で渡してる

about.html.slim
javascript:
  var page_title    = "About"; 
  var page_about    = "About";
  var page_message  = "「人と人を繋ぐゲームデザイン」をモットーに日々ゲーム制作に力を入れているゲームデザインサークルです。";

= javascript_pack_tag 'template'

ModelのデータをVue.js 側に受け渡す

下記のようにJavaScriptを経由して、JSON形式で渡している

about.html.slim
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'で呼び出している。

product.vue
<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: productsJSON形式でRailsからデータを受け取り、<h2 v-for="p in product">でそれを一つづつ取り出してる。

だいたいのページはこれを流用する形でリプレースできた

画像の動的描画

うちのWebには制作したノベルゲームの一覧ページがある。そのページではsplitメソッド、eachメソッドやimage_tagを使って描画していた

gamelinks_controller.rb
  def game
    @games = Game.page(params[:page]).per(PER)

    @games.each do |game|
      game.images = game.images.split(",")
    end
  end
game.html.slim
  - @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でリプレースするにあたって以下のようにしてみた

game.html.slim
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'
game.vue
<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>
game.js
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したsrcrequireで画像へのパスを読み込んで描画させている。

これで動的に画像を描画できた。

スライダーの作成

トップページにスライダーを作っていたので、それもVue.jsで置き換えた。

元々のソースは以下

home.html.slim
title GAMELINKS

css class="text-align:center"
    == render 'slider'
_slider.htmlslim
.flexslider
  ul class="slides"
    - @slide.each do |slide|
        li
            = image_tag slide
application.css
 *= require flexslider.css
application.js
//= require jquery.flexslider

$(window).load(function() {
    $('.flexslider').flexslider({
      animation: "slide"
    });
  });

それを以下のように変更した。

home.html.slim
title GAMELINKS

javascript:
    var slides = #{{@slide.to_json}};

= javascript_pack_tag 'slide'
slide.vue
<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>

slide.js
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を書いたのでおかしなところがあれば編集リクエストなどを投げていただければと思います。

参考資料

既存のRails環境にVue.jsを追加する

Vue.jsの外から情報を渡したい

RailsのオブジェクトをJavaScriptに渡す

RailsからJavaScriptへ配列を渡す方法

shhdgit/vue-easy-slider

Vue.js

3
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
7