素人Web屋の備忘録

素人Web屋の備忘録。たまに雑記。

Vueを実践で使った話01

この前実際に仕事でVue.jsを使う機会があったので、実践編としてその使い方を残しておきます。
使いどころとしては複数のモーダルがあってモーダル表示でyoutubeを自動再生されるものです。
Gulpタスクはビルドするだけのシンプルなものにしました。
ディレクトリ構成もindex.htmlとJSに絞って書きます。

ソースはこちらにあります。
https://github.com/tomoyukionodera/vuefunc01

ディレクトリ構成

root/
 ├ gulpfile.js
 ├ js/
 │ └ main.js
 │ └ bundle.js
 └ index.html

gulpfile.js

"use strict"

const gulp = require('gulp');
const browserify = require('browserify');
const babelify = require('babelify');
const source = require('vinyl-source-stream');
const vueify = require('vueify');

gulp.task('browserify', function() {
  return browserify('./js/main.js')
  .transform(babelify,{presets:["env"]})
  .transform(vueify)
  .bundle()
  .on('error', function(err){
    console.log(err.message);
    console.log(err.stack);
  })
  .pipe(source('bundle.js'))
  .pipe(gulp.dest('./js/'));
})

main.js

const Vue = require('vue');

const bus = new Vue();

new Vue({
    el: '#trg',
    data: {
        artists: [
            {
                name: "あああああ",
                url: "https://www.youtube.com/embed/hLMJXH8TMJg?rel=0&autoplay=1"
            },
            {
                name: "いいいいい",
                url: "https://www.youtube.com/embed/PEBy3aegIvE?rel=0&autoplay=1"
            },
            {
                name: "ううううう",
                url: "https://www.youtube.com/embed/CUpgdwuEJxw?rel=0&autoplay=1"
            },
            {
                name: "えええええ",
                url: "https://www.youtube.com/embed/lTDeS-PmLgY?rel=0&autoplay=1"
            },
            {
                name: "おおおおお",
                url: "https://www.youtube.com/embed/0M3HoC2uGhM?rel=0&autoplay=1"
            },
            {
                name: "かかかかか",
                url: "https://www.youtube.com/embed/wf1MfO4V7cA?rel=0&autoplay=1"
            }
        ]
    },
    methods: {
        execute: function(artist) {
            bus.$emit('click.trg', artist.url)
        }
    }
})

new Vue({
    el: "#modal",
    data: {
        opened: false,
        url: ""
    },
    methods: {
        open: function(val){
            this.opened = true;
            this.url = val
        },
        close: function(){
            this.opened = false;
        }
    },
    created: function() {
        bus.$on('click.trg', this.open)
    }
})

index.html

<body>
    <div class="wrap">
        <ul id="trg" class="list">
            <li class="item" v-for="(artist,index) in artists" v-on:click="execute(artist)">
                <img v-bind:src="'images/voice' + index + '.jpg'" v-bind:alt="artist.name">
            </li>
        </ul>
        <div id="modal" v-cloak>
            <div class="overlay" v-if="opened" v-on:click.self="close">
                <div class="mcont">
                    <div class="btnclose" v-on:click="close"></div>
                    <iframe width="900" height="506" v-bind:src="this.url" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen></iframe>
                </div>
            </div>
        </div>
    </div>
    <script src="./js/bundle.js"></script>
</body>

それではmain.jsとindex.htmlについて解説していきます。

main.js

今回、トリガーとなる要素をクリックしたら、それに連動したモーダルが表示されるという動きを作成します。
想定として、トリガー要素のVueインスタンス(dataに各youtubeの埋め込みURLを持っている)をクリック=>連動したモーダルに埋め込みURLを渡しつつそれを使ってモーダル表示させます。
data受け渡しはイベントを使って行うので、イベントハブ用のVueインスタンスを最初に作っておきます。
あとは、<li>を展開するときにv-for="(artist,index) in artistsindexを渡すので、画像名には連番を振っています。
モーダル要素のVueインスタンスにはopened: false,、HTMLにはv-if="opened"を記述しているので最初はDOMにレンダリングされていません。

まずVueを読み込みます。
そしてイベントハブ用のVueインスタンスを作成します。(使い方は後ほど解説します。)

const Vue = require('vue');
const bus = new Vue();

次にトリガーとなる要素のVueインスタンスを作成していきます。

new Vue({
    el: '#trg',
    data: {
        artists: [
            {
                name: "あああああ",
                url: "https://www.youtube.com/embed/hLMJXH8TMJg?rel=0&autoplay=1"
            },
            {
                name: "いいいいい",
                url: "https://www.youtube.com/embed/PEBy3aegIvE?rel=0&autoplay=1"
            },
            {
                name: "ううううう",
                url: "https://www.youtube.com/embed/CUpgdwuEJxw?rel=0&autoplay=1"
            },
            {
                name: "えええええ",
                url: "https://www.youtube.com/embed/lTDeS-PmLgY?rel=0&autoplay=1"
            },
            {
                name: "おおおおお",
                url: "https://www.youtube.com/embed/0M3HoC2uGhM?rel=0&autoplay=1"
            },
            {
                name: "かかかかか",
                url: "https://www.youtube.com/embed/wf1MfO4V7cA?rel=0&autoplay=1"
            }
        ]
    },
    methods: {
        execute: function(artist) {
            bus.$emit('click.trg', artist.url)
        }
    }
})

そしてトリガーがクリックされたときのメソッドを定義します。

methods: {
    execute: function(artist) {
        bus.$emit('click.movie', artist.url);
    }
}

このメソッドはマウントポイント(<ul id="trg">)直下の<li>に設定します。
引数にartistを設定することで、"click.movie"イベントを発火させたときにそのartistに設定されているartist.urlを渡すことができます。

<li class="item" v-for="(artist,index) in artists" v-on:click="execute(artist)">
    <img v-bind:src="'images/voice' + index + '.jpg'" v-bind:alt="artist.name">
</li>

ここでいったんbusについて書きます。
以下の記述でトリガーがクリックされたときのメソッドを定義していますが、これは第一引数でイベント名、第二引数でイベント発火時に受けわたす値を設定しています。
これはemitという書き方をすることで実現できます。emitはイベント発火のメソッドです。

bus.$emit('click.movie', artist.url);

そしてemitで発火させたイベントを受け取る処理がモーダル要素のVueインスタンスに設定されている以下のメソッドです。onはイベント受け取りのメソッドです。
onメソッドは受け取ったイベントとメソッドを関連付ける役割をします。(javascriptのaddEventListennerみたいなもの)

トリガー要素クリック=>emitで定義したイベント名でイベント発火(このときartist.url持ってる)=>onでそのイベントに対応した(第二引数に指定した)メソッドを実行、という流れです。

created: function() {
    bus.$on('click.trg', this.open)
}

ちなみにonで関連付けたopenメソッドは以下なので受け取ったartist.urlを自分(モーダル要素のVueインスタンス)のdataに格納つつ自分のopenedプロジェクトをtrueに変えています。
これでクリックしたときにそれに対応したモーダルが表示されyoutubeが再生されます。

open: function(val){
    this.opened = true;
    this.url = val
}

ちょっとしたポイントとして、ブラウザロード時に一瞬モーダルがちらつく現象があるので、それは以下で回避しています。
index.html

<div id="modal" v-cloak>

index.css

[v-cloak] {
    display: none;
}

長くなりましたがこんな感じでモーダルを実装しました。
今回、こちらを参考にしてソースを書きました!ありがとうございます!!
https://liginc.co.jp/374210

読んでいただいた方いましたら、最後までお付き合いいただきありがとうございました!
当方経験が浅いためご指摘、アドバイスあれば教えてください。

読んでいただいてありがとうございました!!