jQuery から Vue.js への移行

  • 36
    Like
  • 0
    Comment

はじめに

最近耳にする Vue.js(ビュージェイエス) ってどんなもの? jQuery どう書き方違うの?とか、jQuery でやってたこういう事って Vue.js だとどうやるの?という jQuery から Vue.js へ移行しようかなぁ~と思っている人向けの小難しいことは省いた記事です。私もそちら側から来たものです。

Vue.js 日本公式ページ(日本語翻訳率が半端ないと評判)

jQuery と Vue.js 違い

jQuery はセレクタ操作に特化したライブラリなので HTML の一部をちょっとだけ弄るには簡単で手軽に扱えます。複雑な処理をしたい場合その都度セレクタから要素を探して操作するので、反映が遅かったり同じデータを表示するはずの複数の要素がちゃんと同期していなかったり管理が大変になってきます。

Vue.js は高速で動くバーチャル DOM という設計で作られており・・・(難しいので省略)簡単にいうと、JavaScript のデータと HTML の要素を紐付けて、データが変われば HTML にも勝手に反映してくれます。ロジックは実際の DOM とはほぼ分離されるので、メンテナンスする時もデザインとスクリプトを行ったり来たりする事が減ります。

SEO 的にどうなの?

Vue.js で持っているデータはブラウザのソースコードを見ても表示されません。でも最近のクローラーは Vue.js などの JavaScript で出力された部分も拾ってくれるので気にする程ではないと思います。

シェア用の OGP など考慮する場合は SSR (サーバーサイドレンダリング)というものもあり、最初のアクセスのみサーバー側で DOM を構築して、それ以降は バーチャル DOM で処理するようになります。

書き方の違い

文字を入れ替える

:mushroom: デモページ

まずは簡単に World を別の文字に変えるコードを作ってみます。

jQuery の場合

HTML
<div>
  Hello <span id="message">World</span> !
  <button id="update">change</button>
</div>
JavaScript
$(document).on('click', '#update', function() {
  $('#message').text('jQuery')
})

jQuery で書くとだいたいこんな感じでしょう。変える部分はタグで囲む必要がありますね。

Vue.js の場合

HTML
<div id="app">
  Hello {{ message }} !
  <button @click="update">change</button>
</div>
JavaScript
new Vue({
  el: '#app',
  data: {
    message: 'World'
  },
  methods: {
    update() {
      this.message = 'Vue.js'
    }
  }
})

Vue.js で書くとこんな感じになります。まだ違いは無いというかむしろ jQuery の方が楽そう。

データの文字列を画面に表示させる場合は {{ message }} と書きます。

this.message = 'Vue.js'

データを変更すれば一緒に {{ message }} この部分が変更されます。

リスト要素の追加と削除

:mushroom: デモページ

add ボタンを押すとリストに新しい要素を追加し、 remove ボタンを押すとその要素を削除します。

jQuery の場合

Ajax で取得したという想定で jQuery も最初はデータから表示するようにしました。

HTML
<div>
  <p>Length: <span id="length">0</span></p>
  <ul id="list"></ul>
  <button id="add">add</button>
</div>
JavaScript
(function() {
  var counter = 0
  var list = ['Apple', 'Banana', 'Strawberry']

  init()

  $(document).on('click', '#add', function() {
    addItem('Orange' + (++counter).toString())
  })
  $(document).on('click', '.remove', function(event) {
    $(event.target).parent().remove()
    updateLength()
  })

  function init() {
    for (var i = 0; i < list.length; i++) {
      addItem(list[i])
    }
    updateLength()
  }

  function addItem(name) {
    $('#list').append('<li>' + name + ' <button class="remove">remove</button></li>')
    updateLength()
  }

  function updateLength() {
    $('#length').text($('#list li').length)
  }
})()

jQuery を使うにしてもクラス化したりもっと綺麗に書く人もいると思うけどこんな感じだと思います。

まあ <span id="length">0</span> こいつがよくいる厄介なデータである。 これをリスト要素の数と同期させるには変更があったら毎回 updateLength() をしないといけない。凄く面倒くさい。

テンプレートもロジックに入り込んでいるのでメンテナンスが大変なやつです。

$('#list').append('<li>' + name + ' <button class="remove">remove</button></li>')

あと jQuery はセレクタがないと生きていけないタイプなので操作する場所が増えるに連れて ID やクラスなどの Attribute もどんどん増えていきます。

Vue.js の場合

HTML
<div id="app">
  <p>Length: {{ length }}</p>
  <ul>
    <li v-for="(item, i) in list">{{ item }}
      <button @click="list.splice(i, 1)">remove</button>
    </li>
  </ul>
  <button @click="add">add</button>
</div>
JavaScript
new Vue({
  el: '#app',
  data: {
    counter: 0,
    list: ['Apple', 'Banana', 'Strawberry']
  },
  computed: {
    length: function() {
      return this.list.length
    }
  },
  methods: {
    add: function() {
      this.list.push('Orange' + (++this.counter).toString())
    }
  }
})

だいぶ違いが見えてきました。

要素への追加と削除も、データ側の配列を操作するだけでOK。

this.list.push('Orange' + (++this.counter).toString())

厄介な length はというと、computed という関数型のデータで配列の長さを返すものを1個作っておけばそれがリアルタイムで表示されます。

computed: {
  // この部分がデータと同じ役割
  length: function() {
    return this.list.length
  }
},

勿論 {{ list.length }} と書いても大丈夫だけど、computedキャッシュされるのでフィルターとかの複雑な処理に便利です。

ステキですね。

表示・非表示の切り替えとトランジション/アニメーション

:mushroom: デモページ

よくあるタブ切り替えのコンテンツを作ってみましょう。

jQuery の場合

HTML
<div>
  <ul id="tab" class="tab">
    <li data-id="1" tabindex="0">menu1</li>
    <li data-id="2" tabindex="0">menu2</li>
    <li data-id="3" tabindex="0">menu3</li>
  </ul>
  <div id="content" class="content">
    <section data-id="1">
      <p>content1</p>
    </section>
    <section data-id="2">
      <p>content2</p>
    </section>
    <section data-id="3">
      <p>content3</p>
    </section>
  </div>
</div>
JavaScript
(function() {
  var current = 1
  init()

  $('#tab').on('click', 'li', function(event) {
    changeTab($(this).data('id'))
  })
  $("#tab").on('keypress', 'li', function(event) {
    if (event.keyCode == 13) {
      changeTab($(this).data('id'))
    }
  })

  function init() {
    changeTab(current)
  }

  function changeTab(id) {
    $('#content section:not([data-id="' + id + '"])').removeClass('active')
    $('#content section[data-id="' + id + '"]').addClass('active')
    $('#tab li').removeClass('active')
    $('#tab li[data-id="' + id + '"]').addClass('active')
  }
})()

アクティブな要素にクラスを付けたり消したりします。

display:none が付いてるので切り替えのフェードインにはトランジションの代用でアニメーションを使用しました。 visibility などを使ってもいいけど、このへんの実装も CSS 大好きじゃないとだれてきます。

私もフェードアウト作る時点でだれてしまいました。

Vue.js の場合

HTML
<div id="app">
  <ul class="tab">
    <li v-for="item in list" 
      @click="current=item.id" @keydown.enter="current=item.id"
      :class="{active:active(item.id)}" tabindex="0">{{ item.label }}</li>
  </ul>
  <div class="content-vue">
    <transition>
      <section v-for="item in list" :key="item.id" v-if="active(item.id)">
        <p>{{ item.content }}</p>
      </section>
    </transition>
  </div>
</div>
JavaScript
new Vue({
  el: '#app',
  data: {
    current: 1,
    list: [{
      id: 1,
      label: 'menu1',
      content: 'content1'
    }, {
      id: 2,
      label: 'menu2',
      content: 'content2'
    }, {
      id: 3,
      label: 'menu3',
      content: 'content3'
    }]
  },
  methods: {
    active: function(id) {
      return this.current == id
    }
  }
})

Vue.js は v-if で条件分岐が出来るので、簡単にアクティブなIDのコンテンツだけを表示させます。

<section v-for="item in list" :key="item.id" v-if="active(item.id)">
  <p>{{ item.content }}</p>
</section>

そして切り替えトランジションは <transition> で囲むだけで Vue が時間を管理し切り替えが始まったな~というタイミングでクラスが付き、終わったな~というタイミングで display:none や要素が削除されるので、あとは提供されたクラスにスタイルをつければいい。

CSS
.v-enter-active, .v-leave-active {
  transition: all .8s;
}

.v-leave-active {
  position: absolute;
}

/* 表示されるときは左から */
.v-enter {
  transform: translateX(-20px);
  opacity: 0;
}

/* 消えるときは上へ */
.v-leave-to {
  transform: translateY(-20px);
  opacity: 0;
}

複雑なトランジションが作れるし、もう display:none に悩まされる事はないですね。

まとめ

Vue.js と似たようなリアクティブのライブラリは他にもいろいろあるので、自分にあったライブラリを使うといいです。個人的には Vue.js は入門のハードルも低いし、追々大きなシステムにも対応出来るし、一番大事なとこで使ってて凄く楽しいのでオススメです。

Vue.js は 公式マニュアルの日本語翻訳率とスピードが早いので、まずは公式マニュアルを読むのが一番正確だし間違いないです。
それにプラスして色んな方が書いた Tips も沢山あるので探してみてください。私も書いているのでぜひ見てくださいね~(•ө•)ノ

:mushroom: Vue.js 日本公式ガイド
:mushroom: 私のブログ #Vue.js