IT + X

ITと何かのコラボレーションは時代を変えるかも

Vue.jsを使ってみてよかったところ、イマイチなところ

Vue.jsはReact,Angularに並ぶJavascriptフレームワークです。
しばらくフロントエンド開発にはかかわらなかったのでいろいろハマりましたが、備忘録も兼ねてまとめてみました。

Vue.jsのいいところ

リアクティブシステム

Vue.jsはリアクティブシステムになっています。
リアクティブシステムとは、ある変数の値を変更するとブラウザ上の表示も自動でアップデートしてくれるしくみをいいます。
例えば、以下のようなJavascriptのコードの場合、

let price = 1000;
let quantity = 10;

total = price*quantity;

totalに計算結果が出力されますが、後でpriceの値を変更(例えばprice = 200)にしてもtotalの値は変わりません。
しかし、リアクティブシステムではpriceの変更にともなってtotalが更新されます。
Vue.jsでは以下のように書きます。

var vm = new Vue({
   el: '#app',
   data: {
     price: 1000,
     quantity: 10
   },
   computed: {
    totalPrice(){
       return this.price * this.quantity
     }
  }
})

<div id="app">
  <div>Price: ${{ price }}</div>
  <div>Total: ${{ totalPrice}}</div>
</div>

ブラウザのデベロッパーツールのコンソールを開いて以上のコードを入力したあと、vm.price = 2000と入力するとTotalが自動で20000に更新されます。 これによりネイティブアプリのようなダイナミックなウェブアプリを作ることができます。
もしリアクティブでないJavascriptで同じようなことをやろうとすると、自分で変数の更新をチェックし変更されたらターゲットフィールドの値を更新するという処理を自分で書かなければいけません。
ダイナミックなウェブアプリを開発するときにとても便利ですね。

UIを部品化できる

Vue.jsは、コンポーネントと呼ばれるしくみによってソフトウェアを部品化して再利用できるようになっています。
例えば、ボタンの押した回数を表示する部品はこんな感じに書けます。

Vue.component('counter', {
  template: '<button v-on:click="count++">You clicked me {{ count }} times.</button>'
})

コンポーネントをVue.jsで利用するためには以下のように書きます。

<div id="app">
  <counter></counter>
</div>
new Vue({ el: '#app' })

counterというタグでクリックすると数えてくれるパーツとして扱えます。

フレームワークがある幸せ

複数のメンバーでチームで開発する場合、フレームワークがあると規約に則って開発することができるのでメンバー間で話し合いながら作るときにはとても役に立ちます。
Vue.jsでは単一ファイルコンポーネントという形式があります。

<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports ={
  data: functon(){
     return {
        greeting: 'Hello'
     }
 }
}
</script>

<style scoped>
p {
  font-size: 2em;
  test-align: center;
}
</style>

templateタグのところが表示するHTMLで、scriptタグがJavascriptコード、styleタグがCSSコードを記述するところです。
通常、それぞれのタグ部分はHTMLファイル、JSファイル、CSSファイルに分けて記述されますが、一緒に書けるようにすることでモジュール性を強化しています。

さらに、vue-cliを使えば自動的にディレクトリ、ファイルを生成して整えてくれます。
vue-cliパッケージをnpmでインストールしてプロジェクトの雛形を作成してみましょう。

# npm install vue-cli
# npm install -g @vue/cli
# vue create <ディレクトリ名>

vue createでは以下のようなファイルが作成されます。

f:id:anthony-g:20190430123205p:plain

Vue.jsプログラムファイルはsrcディレクトリ下にあり、以下のようなファイル構成になっています。

f:id:anthony-g:20190501154027p:plain

App.vueとmain.jsがプログラムメインファイルで、componentsディレクトリ以下のファイルがコンポーネントのプログラムファイルです。
このようにvue-cliでプロジェクトの雛形が作成されます。

Vue.jsのイマイチなところ

コンポーネント間のデータやりとりが面倒

コンポーネント構造になっていることによってプログラムの規模が大きくなってもプログラムがスパゲッティー化しないのでいいのですが、コンポーネント間で情報をやり取りする必要が出てきて結構面倒くさいです。
コンポーネント間のデータのやり取りは2つの方法があります。

1. propによるデータのやりとり
親のコンポーネントから子のコンポーネントへデータを渡すには子コンポーネントにpropsを定義します。

main.js

// 子コンポーネント
const childComponent = {
   props: ['myMessage'],
   template: '<span>{{ myMessage }}</span>
}

// 親コンポーネント
new Vue({
    el: '#app',
    components: {
         'childComponent': child-component
    }
});

html

...
<child-component v-bind:my-message="こんにちは"></child-component>
...

HTML上では属性名は大文字小文字を区別しないので、タグ名はchild-component、属性名はmy-messageという感じでキャメル形式をケバブ形式に変換しています。 しかし、propsを使う方法は親から子への一方向でしか情報をやりとりできません。
子から親に情報を伝えるためにはemitを使って可能です。 情報の伝達方向でやり方が違うというのもいまいちな感じです。

2. Vuexによるデータのやりとり
しかし、コンポーネント間の関係が親子関係であるとはかぎりません。
そういうときはVuexを使います。
Vuexとは、Vue.jsアプリケーションローカルに持つことのできるDBのようなデータ格納領域を作成するライブラリで、全てのコンポーネントからアクセスすることができます。
まず、Vuexのストアを定義します。
store/index.js

import Vue from 'vue'
import Vuex from 'vuex'

Vue.use(vuex)

const store = new Vuex.Store({
  state: {
         myMessage: ""
  },
  mutations: {
   setMyMessage(state,{msg}){
      state.myMessage = msg
   }
})

stateに格納するデータを、myMessageと定義して文字列を持つようにします。 Vuexでデータを更新する場合は、commit()を呼ばなければいけないお約束になっていて、mutationsはその関数を定義するところです。 次に、main.jsでstoreをVue.jsに読み込みます。

src/main.js

...
import store from './store'
...
new Vue({
    render: h => h(App),
    store: store
}).$mount('#app')

次に、コンポーネントでstoreにアクセスしてみます。

src/components/xxx.vue

<template>
<div>
  <span>{this.$store.state</span>
   <input type="text" />
   <button @click="ok()">OK</button>
</div>
</template>
<script>
export default {
  name: "xxx",
  methods: {
     ok(){
         this.$store.commit('setMyMessage",{myMessage: "Hello World"})
     }
   }
}
</script>

これはテキストボックスに入力してボタンを押すとVuexの情報が更新される例です。 これでVuexを介してコンポーネント同士のデータのやり取りができるようになりますが結構面倒ですね。 Vuexについての詳しい解説は以下のURLを参照してください。

vuex.vuejs.org

リアクティブシステムがわかりにくい

リアクティブシステムはリッチなUIを作るには便利なのですが、どのタイミングで更新されるのかがわかりにくいというのがあります。
例えば以下の例の場合、

src/components/xxx.vue

<template>
<div v-if="showComponent">
...
</div
</template>
<script>
export default {
   name: "xxx",
   computed: {
      showComponent(){
         return this.$store.state.showFlag
      }
   }
}
</script>

VuexデータのshowFlagでコンポーネントを表示したり消したりしたいのですがうまく動きません。 (これで結構長い間悩みました。)
VuexストアのshowFlagが更新されたらshowComponentが呼ばれてほしいのですがそうはなりません。
v-ifのほうでリアクティブな変数をモニターしているみたいなのですが、computedメソッドの中までは見てないみたいです。
こういうところが最初とまどうところです。

学習コストがやや高め

通常プログラムは記述した順番に実行されるものですが、リアクティブシステムは何かを変更したらキックされるイベントドリブンなシステムなので慣れるまで少しかかります。
また、モジュール化でソースが複数ファイルに分かれてしまうので、なれないと全体が見えづらいというのもあります。

結論

ちょっと使った感じでは、Vue.jsは規模の大きなフロントエンド開発向けなのかなと感じました。
ただ、うまく動かないときに調べるのが結構大変だったのでデバッグがもう少しやりやすくなるといいと思いました。
Reactも少し触ってみましたが、なんでもかんでもJavascriptのコードにしてしまうReactよりVue.jsのほうがわかりやすい印象でした。
あと、VuexとVue-Routerはほぼ必須で使うコンポーネントだと思います。
最初は少しとっつきにくいですが、慣れれば使って損はないのではないでしょうか。

参照URL:
www.vuemastery.com