はじめに
長年jQueryでフロントを書いてきて、そろそろVue.jsに手を出したい――という方向けの記事です。
jQueryは今でも立派に動きますし、決して悪いライブラリではありません。ただ、画面が複雑になってくると「どのボタンを押したらどのDOMが書き換わるんだっけ?」という追いかけっこが辛くなってきます。Vue.jsに乗り換えると、この辛さがかなり楽になります。
この記事の核になる考え方は一つだけです。
jQueryは「DOMを直接いじる」、Vue.jsは「データを変えればDOMが勝手に変わる」
jQueryでは「ボタンが押されたらこの要素をshow()して、テキストを書き換えて、クラスを付けて……」とDOM操作の手順を書きます。Vue.jsでは「このデータがtrueなら表示される」「このデータがテキストとして表示される」と宣言しておいて、あとはデータを書き換えるだけ。DOMはVue.jsが勝手に面倒を見てくれます。
この頭の切り替えさえできれば、あとは書き方を覚えるだけです。本記事では、jQueryでよく書くパターンをVue.jsに書き換えながら、その切り替えを体感していきます。
準備:最小構成でVue.jsを動かす
ビルドツールもnpmも不要、<script>タグ1行から始められます。
<!DOCTYPE html>
<html>
<head>
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>
</head>
<body>
<div id="app">
{{ message }}
</div>
<script>
const { createApp, ref } = Vue;
createApp({
setup() {
const message = ref('こんにちは Vue.js');
return { message };
}
}).mount('#app');
</script>
</body>
</html>
これだけで動きます。以降のサンプルも全てこの形のsetup()内にrefを置いていく前提で書きます。
本編:書き換え対比集
1. 要素の表示・非表示
jQuery
$('#box').show();
$('#box').hide();
Vue.js
<div v-if="isVisible">表示される箱</div>
<!-- または -->
<div v-show="isVisible">表示される箱</div>
const isVisible = ref(true);
// 消したいとき
isVisible.value = false;
v-ifは要素自体をDOMから消し、v-showはdisplay:noneにします。頻繁に切り替えるならv-show、条件でそもそも描画したくないならv-if。
2. クリックイベント
jQuery
$('.btn').on('click', function() {
alert('クリックされた');
});
Vue.js
<button @click="handleClick">ボタン</button>
const handleClick = () => {
alert('クリックされた');
};
@clickはv-on:clickの省略形です。HTMLを見ただけで「このボタンは何をするか」が分かるのがポイント。jQueryだと別ファイルのJSを追いかける必要がありましたよね。
3. テキストの書き換え
jQuery
$('#msg').text('更新されたメッセージ');
Vue.js
<p id="msg">{{ message }}</p>
const message = ref('初期メッセージ');
// 書き換え
message.value = '更新されたメッセージ';
{{ }}(マスタッシュ構文)に変数を入れておくだけ。変数を書き換えれば画面も自動で変わります。.text()を呼ぶ必要はありません。
4. フォーム入力値の取得
jQuery
const name = $('#name').val();
// 逆にセットしたいとき
$('#name').val('太郎');
Vue.js
<input v-model="name">
<p>入力値: {{ name }}</p>
const name = ref('');
これが双方向バインディング。v-modelを付けるだけで、inputの値とname変数が常に同期します。「値を取る」「値をセットする」という発想がそもそも消えます。初めて見るとちょっと感動します。
5. クラスの付け外し
jQuery
$('#tab').addClass('active');
$('#tab').removeClass('active');
$('#tab').toggleClass('active');
Vue.js
<div :class="{ active: isActive }">タブ</div>
const isActive = ref(false);
// トグル
isActive.value = !isActive.value;
:classに{ クラス名: 真偽値 }の形で渡せばOK。複数クラスも同じノリで書けます。
<div :class="{ active: isActive, disabled: isDisabled }">
6. リストの動的生成
jQuery
const items = ['りんご', 'みかん', 'ぶどう'];
$.each(items, function(i, item) {
$('#list').append('<li>' + item + '</li>');
});
Vue.js
<ul>
<li v-for="item in items" :key="item">{{ item }}</li>
</ul>
const items = ref(['りんご', 'みかん', 'ぶどう']);
配列に追加すれば、画面のliも勝手に増えます。
items.value.push('なし'); // 自動で<li>なし</li>が追加される
:keyはVue.jsが要素を区別するための目印です。本来は重複しないIDを指定するのがベスト。
7. Ajax通信と結果表示
jQuery
$.ajax({
url: '/api/users',
success: function(data) {
$('#user-list').empty();
$.each(data, function(i, user) {
$('#user-list').append('<li>' + user.name + '</li>');
});
}
});
Vue.js
<ul>
<li v-for="user in users" :key="user.id">{{ user.name }}</li>
</ul>
const users = ref([]);
const loadUsers = async () => {
const res = await fetch('/api/users');
users.value = await res.json();
};
loadUsers();
取得したデータをusers.valueに代入するだけで終わり。empty()してからappend()でループする、あの手順が丸ごと消えます。
8. 条件による要素切り替え
jQuery
if (isLoggedIn) {
$('#user-menu').show();
$('#login-btn').hide();
} else {
$('#user-menu').hide();
$('#login-btn').show();
}
Vue.js
<div v-if="isLoggedIn">ようこそ、{{ userName }}さん</div>
<button v-else>ログイン</button>
const isLoggedIn = ref(false);
v-if / v-else-if / v-elseが使えます。show/hideを交互に呼ぶコードから解放されます。
9. 親子要素間のデータ受け渡し
jQuery
<div id="card" data-user-id="123"></div>
const userId = $('#card').data('user-id');
Vue.js(コンポーネント化した場合)
<user-card :user-id="123"></user-card>
// UserCardコンポーネント側
const UserCard = {
props: ['userId'],
setup(props) {
console.log(props.userId); // 123
return {};
},
template: '<div>User: {{ userId }}</div>'
};
data-*属性でやり取りしていた値の受け渡しは、Vue.jsではpropsとして明示的に宣言します。どんなデータが渡ってくるかがコードを読むだけで分かります。
(なお、SFC(単一ファイルコンポーネント)で<script setup>を使う場合はconst props = defineProps(['userId'])とより簡潔に書けますが、ビルド環境が必要になります。)
10. グローバル状態の共有
jQuery
window.currentUser = { name: '太郎' };
// あるいはDOMに隠し持たせる
$('body').data('user', { name: '太郎' });
Vue.js
import { reactive } from 'vue';
export const store = reactive({
currentUser: { name: '太郎' }
});
reactiveで作ったオブジェクトは、どこからインポートしても同じ実体を参照します。値を書き換えれば、それを使っている全画面が自動で更新されます。本格的にやるならPiniaというライブラリがおすすめです。
つまずきポイント
Vue.jsに触り始めたときに、ほぼ誰もが引っかかるポイントをまとめておきます。
1. refで宣言した変数は.valueが必要(JS側だけ)
const count = ref(0);
count.value++; // JS側では .value でアクセスする
console.log(count.value); // 1(中身を見たいときも.value)
console.log(count); // Refオブジェクトそのものが出力される点に注意
<p>{{ count }}</p> <!-- テンプレート側では.value不要 -->
この非対称が最初は気持ち悪いですが、すぐ慣れます。
2. jQuery的に直接DOMを触りたくなる衝動を抑える
document.getElementById('xxx').innerText = '...'と書きたくなったら、まず「それはv-modelやv-if、マスタッシュ構文で済まないか?」と考える癖をつけましょう。9割はVue.jsの機能で済みます。
3. リストの:keyを付け忘れる/indexを使う
v-forを書くと「:keyを付けろ」と警告されます。このkeyは要素を区別する目印なので、配列のindexではなく、データ固有のID(user.idなど)を使うのがベストです。
<!-- いまいち -->
<li v-for="(item, i) in items" :key="i">{{ item.name }}</li>
<!-- 推奨 -->
<li v-for="item in items" :key="item.id">{{ item.name }}</li>
indexをkeyにすると、配列の並び替えや途中挿入で描画がおかしくなることがあります。
まとめ
Vue.jsへの乗り換えで一番大事なのは、主語を「DOM」から「データ」に切り替えることだと思っています。
- jQuery: 「この要素を、こう操作して、こうする」
- Vue.js: 「このデータは、こう表示される。データを変えれば画面も変わる」
この切り替えができると、コードの見通しが劇的に良くなります。ボタンを押したらどこのDOMが書き換わるか、追いかける必要がなくなるからです。
いきなり全面リプレイスする必要はありません。jQueryで動いている画面の中の、特に込み入った部分だけをVue.jsに置き換えていく、という段階的な移行で十分です。CDN1行から始められるのがVue.jsの良いところです。
jQueryで培ったDOMやイベントの知識は全く無駄になりません。むしろ内部で何が起きているかイメージできる分、Vue.jsの挙動もすんなり理解できるはずです。
それでは、良いVue.jsライフを。