はじめに
最近耳にする 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 で処理するようになります。
書き方の違い
文字を入れ替える
まずは簡単に World
を別の文字に変えるコードを作ってみます。
jQuery の場合
<div>
Hello <span id="message">World</span> !
<button id="update">change</button>
</div>
$(document).on('click', '#update', function() {
$('#message').text('jQuery')
})
jQuery で書くとだいたいこんな感じでしょう。変える部分はタグで囲む必要がありますね。
Vue.js の場合
<div id="app">
Hello {{ message }} !
<button @click="update">change</button>
</div>
new Vue({
el: '#app',
data: {
message: 'World'
},
methods: {
update() {
this.message = 'Vue.js'
}
}
})
Vue.js で書くとこんな感じになります。まだ違いは無いというかむしろ jQuery の方が楽そう。
データの文字列を画面に表示させる場合は {{ message }}
と書きます。
this.message = 'Vue.js'
データを変更すれば一緒に {{ message }}
この部分が変更されます。
リスト要素の追加と削除
add
ボタンを押すとリストに新しい要素を追加し、 remove
ボタンを押すとその要素を削除します。
jQuery の場合
Ajax で取得したという想定で jQuery も最初はデータから表示するようにしました。
<div>
<p>Length: <span id="length">0</span></p>
<ul id="list"></ul>
<button id="add">add</button>
</div>
(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 の場合
<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>
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
はキャッシュされるのでフィルターとかの複雑な処理に便利です。
ステキですね。
表示・非表示の切り替えとトランジション/アニメーション
よくあるタブ切り替えのコンテンツを作ってみましょう。
jQuery の場合
<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>
(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 の場合
<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>
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
や要素が削除されるので、あとは提供されたクラスにスタイルをつければいい。
.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 も沢山あるので探してみてください。私も書いているのでぜひ見てくださいね~(•ө•)ノ