クラウドワークス エンジニアブログ

日本最大級のクラウドソーシング「クラウドワークス」の開発の裏側をお届けするエンジニアブログ

jQueryからVue.jsに乗り換えて良かった点・悪かった点をまとめてみた!

今年の3月からクラウドワークスで働き出した高梨です!

さっそくですが、
最近、jQueryで書いていたJavaScriptをVue.jsに変えていくということをやりました!

「がっつりcomponent化して、API用意して、SPAにして・・・」

みたいな感じではなく、
最低限jQueryでやっていた表示・非表示の処理だったり、金額を計算するといった処理を
そのままVue.jsに置き換えていくという感じですね。

実際、Google Trendを見ても
ここ数年でVue.jsの人気が急激に伸びているのは事実です。
(実はReactにも大差をつけている)

Google Trend JS比較】
https://trends.google.com/trends/explore?date=today%205-y&q=vue.js,react.js,angular.js

ここでは、実際に実装してみて感じた良かった点や悪かった点を
簡単に2つずつお話しさせていただきたいと思います。

すでにがっつりVue.jsを使っているという方には物足りない内容かと思いますが、
「脱jQueryを考えている」
「Vue.jsなどの他のライブラリに興味を持っている」
といった方はぜひ参考にしていただけると幸いです。

基本、コードの例を交えてお話ししていきたいと思いますが、
説明不足の箇所はVue.js公式ドキュメントで該当する箇所のリンクを貼っていますので、
「詳しく理解したい!」
という方は是非利用してみてください!

ここでのお話を聞いて
「Vue.jsいい感じじゃん!」
と思っていただけた方は最後に公式のチュートリアルのリンクも
載せておきましたので、
是非そちらからVue.jsの世界に踏み込んでいただけると幸いです。

目次

良かった点

その1. イベントバインドさせる処理を一掃できる!

jQueryでコードを書いていくと、
表示・非表示や計算といったロジック系の処理以外に、
対象になる要素にイベントをバインドさせるという処理を
記述していきますね。

こんなやつ
$('#app').on('click', function () { ...

コードの量が増えていけばいくほど、
イベントをバインドさせる処理と実際のロジックが乱雑になっていき、
どんどん見にくいコードになっていきます。
(挙げ句の果てに、一箇所変えると思わぬ場所が動作しなくなったりする始末)

Vue.jsだとこのイベントをバインドさせるという処理自体を
全て消すことができます。

実際に例で見ていきましょう!

「ボタンをクリックしたら他の要素が表示される」
というよくあるコードで説明していきます。

ボタンをクリックしたらmessageが表示される処理

# HTML
<div>
  <button id='btn'>ボタン</button>

  <div id='message' class='message'>
    <p>クリックしました!</p>
  </div>
</div>
# CSS
.message {
  display: none;
}
# JavaScript(jQuery)
$(document).on('click', '#btn', function () {
  $('#message').show();
}

$(document).on('click', '#message', function () {
  $(this).hide();
}

例えば、このコードをVue.jsに置き換えるとこんな感じになります。

ボタンをクリックしたらmessageが表示される処理

# HTML
<div id='app'>
  <button v-on:click='displayMessage = true'>ボタン</button>

  <div v-if='displayMessage' v-on:click='displayMessage = false'>
    <p>クリックしました!</p>
  </div>
</div>
# JavaScript(Vue.js)
import Vue from 'vue/dist/vue.esm';

export default new Vue({
  el: '#app',
  data: function () {
    return {
      displayMessage: false
    }
  }
});

Vue.jsのコードを少し説明しますね!

まず、全体をappというidで囲っていますが、 Vue.jsでは、対象になる箇所の外枠を指定する必要がありまして、 ここでは、外枠をappというidで囲い、 JavaScript側のelというプロパティにappを指定することで、 Vue.jsを使えるようにしています。

次に表示・非表示のロジックの話をしていきます。

HTML側にv-ifという見慣れない属性がついていますが、v-ifはその値がtrueの時のみ
タグとその中身が表示されるという処理になります。

そして、v-ifの値はJavaScriptのコードとして読み込まれることになっているので
v-if='displayMessage'は、displayMessage(変数)の値がtrueの時のみ
タグとその中身が表示されるという処理になります。

この場合でいうと、最初にJavaScript側でdisplayMessagefalseを格納しているので、
最初v-ifがついたタグは非表示になりますね。

v-ifの詳しい説明】
条件付きレンダリング — Vue.js

次に、v-on:clickの意味ですが、
v-on:clickのついた要素をクリックした時に、
その値にある処理を実行するという意味になります。

click以外にもこんな感じで基本的にJavaScriptにあるイベントが使えます!
submitkeyupscroll・・・

上のコードでいうと、
v-on:click='displayMessage = true'となっていますので、
この要素をクリックするとdisplayMessage(変数)にtrueを格納する
という処理になりますね。

v-onの詳しい説明】
イベントハンドリング — Vue.js

つまり、初期表示からボタンをクリックした時までの流れは次の通りです。

  • 初期表示
    1. displayMessage(変数)にfalseが格納される
    2. HTML側のv-ifの値がfalseになっているため、タグとその中身が非表示になる
  • ボタンをクリック
    1. displayMessage(変数)にtrueが格納される
    2. v-ifの値がtrueになるため、タグとその中身が表示される

まだ、このぐらいのコードならjQueryの方が馴染みもあり、
Vue.jsで書いた方が複雑に見えるかもしれませんね。

ただ、jQuery側では、
「クリックした対象とイベントを指定する」
「イベント発生時の処理を記述する」
の両方を書いていく必要があるのに対して、

Vue.jsでは「イベント発生時の処理を記述する」のみでよくなるわけなので、
コードの量が10倍、20倍と増えていくことを考えると、
Vue.jsの方が断然書く量も減り、ロジックのみの記述で済むようになるわけです。

もちろん、メソッドも簡単に定義できるので、
複雑な処理もよりシンプルに記述していくことができます。

実際に先ほどのコードをメソッドを使って書いてみると
以下の通りです。

ボタンをクリックしたらmessageが表示される処理

# HTML
<div id='app'>
  // クリックした時にtoggleMessageメソッドを実行する
  <button v-on:click='toggleMessage'>ボタン</button>

  <div v-if='displayMessage' v-on:click='toggleMessage'>
    <p>クリックしました!</p>
  </div>
</div>
# JavaScript(Vue.js)
import Vue from 'vue/dist/vue.esm';

export default new Vue({
  el: '#app',
  data: function () {
    return {
      displayMessage: false
    }
  },
  methods: {
    toggleMessage: function () {
      // this.displayMessageでdataの中に定義されているdisplayMessageの値が取得できる
      this.displayMessage = !this.displayMessage;
    }
  }
});

methodsの詳しい説明】
API — Vue.js

その2. HTML側の値とJavaScriptの処理が勝手に連動してくれている!

上でも少しだけお話ししましたが、
Vue.jsでは、表示している値が変わった時に、
イベントをバインドすることなく
勝手に表示を変更したり、計算をしてくれたりします。

例えば、よくある合計金額を計算するという処理を例にとってみます。
jQueryで書くとこんな感じでしょうか?

合計金額を計算する処理

# HTML
<div>
  <!-- 単価 -->
  <input name='unit_price' type='number' id='unit-price'>

  <!-- 商品数 -->
  <input name='quantity' type='number' id='quantity'>

  <!-- 合計金額 -->
  <input name='amount' type='number' id='amount'>
</div>
# JavaScript(jQuery)
var unitPrice, quantity;

// #unit-priceと#quantityそれぞれにkeydown時のイベントをバインド
$(document).on('keydown', '#unit-price, #quantity', function () {
  unitPrice = $('#unit-price').val();
  quantity = $('#quantity').val();

  // 単価または商品数が未入力の場合は合計金額を初期化して処理を終了する
  if (!unitPrice || !quantity) {
    $('#amount').val(null);
    return;
  }

  $('#amount').val(unitPrice * quantity);
});

これをVue.jsに置き換えるとこんな感じになりますね。

合計金額を計算する処理

# HTML
<div id='app'>
  <!-- 単価 -->
  <input name='unit_price' type='number' v-model='unitPrice'>

  <!-- 商品数 -->
  <input name='quantity' type='number' v-model='quantity'>

  <!-- 合計金額 -->
  <input name='amount' type='number' v-model='amount'>
</div>
# JavaScript(Vue.js)
import Vue from 'vue/dist/vue.esm';

export default new Vue({
  el: '#app',
  data: function () {
    return {
      unitPrice: null,
      quantity: null
    }
  },
  computed: {
    // 合計金額 (単価 × 商品数)
    amount: function () {
      if (!this.unitPrice || !this.quantity) {
        return null;
      }
      return this.unitPrice * this.quantity;
    }
  }
});

これでjQuery側のコードとほぼ全く同じ挙動を実現できます。

少しVue.js側のコードを説明しますね!

1つ目として、HTML側のタグにv-modelという新しい属性がついているかと思いますが、
Vue.jsでは、この中身の変数の値とフォームの値が連動するようになっています。

つまり、unit_priceのフォームに100と入力すると、
同時にunitPriceというJavaScript側の変数の値も100に更新されるわけです。

v-modelの詳しい説明】
フォーム入力バインディング — Vue.js

次に2つ目として、
JavaScript側のコードにcomputedという新しいプロパティが出てきていますが、
computedの中で定義したメソッドは、そのメソッドの中身が更新されたときに、
再度実行されることになっています。

つまり、今回のコードでいうと、
amountの中にあるunitPricequantityの値が更新されるたびに
amountが再実行され、結果的に合計金額が更新されるというわけです。

computedの詳しい説明】
API — Vue.js

なので、例えば
フォームのunit_priceを入力した時の流れでいうとこうなりますね!

  1. フォームのunit_priceの中身を変更する
  2. unitPrice(変数)の値が入力された値に更新される
  3. unitPrice(変数)が更新されたことでamount(computed)が実行される
  4. amount(computed)の返り値がフォームのamountに反映される(合計金額が表示される)

今、初めてVue.jsのコードを見るという方にとっては
なんだか複雑な気がするかもしれませんが、
jQueryで色々な処理が乱雑するのと比べると、ロジックのみの記述でよくなるので、
コード量が減るのはもちろん、可読性も断然良くなるかと思います。

悪かった点

その1. 初期表示が崩れがち...

当たり前ですが、Vue.jsもJavaScriptです。
ページの描画が始まってから、処理が走るまでには多少ラグがあります。

それにあたり、初期表示が崩れるという問題が発生しがちですね。

例えば以下のコード

# HTML
<div v-if='displayValue'>
  <p>displayValueの値はtrueです!</p>
</div>

v-ifの中身はその値がtrueの時に
表示されるということになっていますが、
実はdisplayValueの中身がfalseでも
JavaScriptの処理が走るまでの一瞬表示されてしまったりします。

それが数ミリ秒分のレイアウト崩れに繋がったりするわけですね。

もちろん、その処理が実行されれば崩れも直りますが、
ユーザーに見える画面ですし、気になりますね。。

対処法としては、
v-cloakという属性をタグにつけるというものがあります。

v-cloakはVue.jsの処理が終わるまでは普通に表示されていて、
処理が終わったら勝手に消えてくれるので、
下のように記述しておくと、Vue.jsの処理が終わるまで
指定した要素が消えていてくれます。

# HTML
<div v-if='displayValue' v-cloak>
  <p>displayValueの値はtrueです!</p>
</div>
# CSS
[v-cloak] {
  display: none;
}

v-cloakの詳しい説明】
API — Vue.js

その2. jQueryの便利メソッドが使えない...

もし、「完全に」jQueryからVue.jsに移行するという場合は、
当然ですが、jQueryのメソッドは使えなくなります。

普段検証しているブラウザが多い場合は、
生のJavaScriptだとjQueryに比べて対応していないメソッドが多いので、
使うメソッドが対象のブラウザで使えるのかを確認していく必要が多くなります。

自分が実際に実装した時だと、
ChromeSafari以外にもFirefoxやEdge, IEでも
使えるようにする必要があったので、
時間的な問題を考慮して、局所的にjQueryのメソッドを利用しています。

言い忘れていましたが、
Vue.js内でjQueryを併用して使うことができるので、
一応、部分的にjQueryを使うということができるわけですね。

ただし、jQueryを多く使い出すと結局Vue.jsにした意味が薄くなるので、
本当は完全にVue.jsに置き換えた方がいいのですが、
今回は時間的な問題で断念しました。

調べてみると、それっぽいメソッドがいくつかあったので、
本当はjQueryでいうセレクター($('#app'))みたいなのを
Vue.jsでもできるのかもしれません。

まとめ

以上、jQueryからVue.jsに乗り換えて良かった点・悪かった点でした!

いかがだったでしょう?

他にもアニメーションでJavaScriptのコードが不要になったり、
ライフサイクルというのがいい感じだったり、
そして何より書いていて楽しいといった様々な点で良かったなというのが
個人的な感想です。

あくまでも、まだ最低限置き換えただけなので、
component化して、API用意して、SPAにして
みたいなことは全然やっていないのですが、
個人的には、これだけでも大量に入り乱れたjQueryコードよりは
だいぶメンテナンス性を担保しやすいんじゃないかなという印象でした。

実際に置き換えにかかった時間は、最初ほぼ知識0の状態から
1週間ほどの時間です。(コード量はJavaScript1000行ちょっと)

今後、component化して、サービスをSPAにしていくかという話も出ているので、
その話が進んだ頃にまたブログにアップできればと思います。

これを機に
「Vue.jsいいかも?」
「ちょっと使ってみようかな?」
という人が一人でも出てきてもらえると幸いです。

【公式のチュートリアルはこちら】
jp.vuejs.org

少し試してみるだけならCDN読み込んですぐにできるので、
ぜひ試して見てください!

では、ここまでご覧いただきありがとうございました!

クラウドソーシングのクラウドワークスでは、成長し続けたいエンジニアを募集しています!

www.wantedly.com

© 2016 CrowdWorks, Inc., All rights reserved.