九保すこひです(フリーランスのITコンサルタント、エンジニア)
さてさて、前回「Bootstrap v5がどうなるのか調査してみた」という記事を公開しましたが、実は今後のリリースでもうひとつ気になるものがあります。
それは、JavaScript開発の強力な味方、
「Vue.js」の新バージョン(v3)
です。
Vue 3については、以前からたびたび情報が出てましたがついにアルファ版が利用できるようになりました。(2020.3.26現在)
そこで❗
今回は、近い将来リリースされるであろう Vue 3の使い方をまとめてみることにしました。
ぜひ参考にしていただけると嬉しいです😊✨
【追記:2020.09.20】2020年9月18日に正式リリースされ、変更点があったので、記事を加筆・修正しました。
【追記:2020.10.30】「refの使い方が変わった」を追加しました。
「ついに来ました Vue 3 🎉」
目次
- 1 Vueインスタンスの作り方が変わった
- 2 data変数の設定が関数のみになった
- 3 グローバル・コンポーネントの設定方法も変わった
- 4 配列やオブジェクトのバインディングが改善された
- 5 Portalで別の場所にコンテンツを表示できるようになった
- 6 コンポーネントへの「v-model」が複数対応になった
- 7 コンポーネントは1つのタグで囲まなくてよくなった
- 8 Composition APIで大規模なコンポーネントがつくりやすくなった
- 9 refの使い方が変わった
- 10 Suspenseでコンポーネント準備中は代替表示ができるようになった
- 11 filterはなくなった
- 12 TypeScriptをサポートした
- 13 おまけ:新しいVueを試す方法
- 14 おわりに
Vueインスタンスの作り方が変わった
これまで、Vueアプリをつくる場合このような書き方をしていました。
(HTML)
<div id="app">
<!-- ここに Vueのコンテンツ -->
</div>
(JavaScript)
new Vue({
el: '#app'
});
それが、今回のアップデートからは、createApp()を使うようになりました。
import { createApp } from 'vue'
createApp({
// ここに各種設定
}).mount('#app');
なお、CDNを使う場合はこうなります。
(HTML)
<script src="https://unpkg.com/vue@next"></script>
(JavaScript)
Vue.createApp({
// ここに各種設定
}).mount('#app');
data変数の設定が関数のみになった
これまでdata変数は以下のようにオブジェクトでの指定ができていました。
// ⚠ これはVue 3ではうまく行かない例です
data: {
text: 'test'
},
しかし、Vue 3では必ず関数として指定する必要があります。
// 関数として指定します
data() {
return {
text: 'test'
}
},
もともとコンポーネントやミックスインではこの形にする必要があったので、統一したのかもしれません。
グローバル・コンポーネントの設定方法も変わった
これまでVueコンポーネントは、特に設定なしでも有効になる「グローバルな」コンポーネント指定は以下のようにしていました。
// ⚠ これはVue 3では間違った例です。
Vue.component('v-user', {
props: ['id', 'email', 'password'],
template: `
<div>
<div v-text="id"></div>
<div v-text="email"></div>
<div v-text="password"></div>
</div>
`
});
しかし、Vue 3からは以下のような設定方法に変更になっています。
// Vue 3ではこう書きます
Vue.createApp({
// 省略
})
.component('v-user', {
props: ['id', 'email', 'password'],
template: `
<div v-text="id"></div>
<div v-text="email"></div>
<div v-text="password"></div>
`
})
.mount('#app');
もしくは、ドットを分けて以下のようにすることもできます。
// ドット部分を分割してもOK❗
const app = Vue.createApp({
// 省略
});
app.component('v-user', {
// 省略
});
app.mount('#app');
なお、ローカルなコンポーネントはこれまでと同じです。
// ローカルなコンポーネントは同じです
const userComponent = {
props: ['id', 'email', 'password'],
template: `
<div>
<div v-text="id"></div>
<div v-text="email"></div>
<div v-text="password"></div>
</div>
`
};
const app = Vue.createApp({
components: {
'v-user': userComponent
},
// 省略
}).mount('#app');
配列やオブジェクトのバインディングが改善された
これまでのちょっとした弱点だったのですが、data変数に存在していないキーを追加してもリアルタイムでHTMLに反映されませんでした。
<html>
<body>
<div id="app">
{{ user }}
</div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
<script>
new Vue({
el: '#app',
data: {
user: {
id: 1,
email: 'user@example.com',
password: 'secret'
}
},
mounted() {
this.user.age = 25; // 👈 ageは存在しないので、Vue 2では反映されない
}
});
</script>
</body>
</html>
しかし、これがVue 3では問題なく動くように改善されました。
⚠ ご注意: ただし、存在していないdata変数を追加することはできません。(上のケースで言うと、 this.customer = { .... }; としても、最初にdata変数として定義していなのでダメです・・・😫ということになります。 )
また、配列の場合もより直感的なコードを書くことができます。
Vue.createApp({
el: '#app',
data: {
names: ['山田太郎', '佐藤次郎']
},
mounted() {
this.names.push('田中三郎'); // 👈 Vue 3ならリアルタイムに反映される
}
}).mount('#app');
そして、データの削除も簡単になります。
delete this.user.email; // 👈 emailを削除
なお、Vue 2までのやりかたは次のとおりでした。
Vue.set(this.user, 'age', 25); // Vue 2の追加 Vue.delete(this.user, 'email'); // Vue 2の削除
Portalで別の場所にコンテンツを表示できるようになった
Portalは新しく追加された機能で、簡単に言うと「別の場所で表示できる」機能です。
例えば、次の例を見てください。
<div id="app">
<!-- Vueのエリア内 -->
<Portal target="#footer">フッターです!</Portal>
</div>
<div id="footer"></div>
実はこの例では、<Portal> .. </Portal>の場所には何も表示されず、一番下の<div id="footer"> .. </div>に表示されることになります。
つまり、Vueが管理するエリア外にコンテンツを「転送」できるというわけです。
しかも、その転送コンテンツにもVueの動きを加えることができるので、例えば、閉じるボタンで表示を消すことも可能です。
そして、Portalの使い道として考えられるのが、モーダルやアラート通知です。
コンポーネントへの「v-model」が複数対応になった
これまでは「v-model」はひとつの値だけしかセットできませんでしたが、次のように複数同時にセットできるようになりました。
<UserForm
v-model:id="user.id"
v-model:email="user.email"
v-model:password="user.password"></UserForm>
なお、v-model:に続く名前はコンポーネントのpropsに一致します。
props: ['id', 'email', 'password']
コンポーネントは1つのタグで囲まなくてよくなった
これまでのVueコンポーネントの制約のひとつに「必ず特定のタグで囲んで1つの要素だけにしないといけない」というものがありましたが、これは気にしなくてよくなりました。
<!-- 👇 Vue 3 ではOK -->
<template>
<div>1つ目</div>
<div>2つ目</div>
<div>3つ目</div>
</template>
Composition APIで大規模なコンポーネントがつくりやすくなった
Composition APIは、大規模なコンポーネントをつくる場合や、多人数での開発に向いています。
なぜなら、Composition APIはコードが大量にあっても管理しやすいからです。(逆に言うと、小さなコンポーネントの場合はあまりメリットはありません)
実際の例をみてみましょう❗
読み込み時にデータ100件を取得し、その中から1ページ10件ずつ表示するコンポーネントです。

<template>
<div v-for="p in filteredPosts">
<div>
ID: {{ p.id }}、タイトル: {{ p.title }}
</div>
</div>
<div>
<button type="button" @click="prevPage">前へ</button>
{{ pagination.page }}ページ目
<button type="button" @click="nextPage">次へ</button>
</div>
</template>
<script>
import { ref, reactive, computed, watch, onMounted } from 'vue'
export default {
setup() {
// data
const posts = ref([]);
const pagination = reactive({
page: 1, // 現在のページ
limit: 10 // 10件ずつ表示
});
// methods
const prevPage = () => {
return pagination.page--;
};
const nextPage = () => {
return pagination.page++;
};
const getPosts = () => {
fetch('https://jsonplaceholder.typicode.com/posts')
.then(response => response.json())
.then(data => {
posts.value = data;
});
};
// computed
const filteredPosts = computed(() => {
let filteredPosts = [];
const start = (pagination.page - 1) * pagination.limit;
const end = start + pagination.limit;
for(let i = start ; i < end ; i++) {
if(posts.value[i] !== undefined) {
filteredPosts.push(posts.value[i])
}
}
return filteredPosts;
});
// watch
watch(posts, (value, oldValue) => {
console.log('postsが変更されました');
});
// mounted
onMounted(() => {
getPosts();
});
return {
posts,
pagination,
prevPage,
nextPage,
filteredPosts
}
}
}
</script>
もうお気づきかもしれませんが、Composition APIといっても以前の書き方が変わっているだけで、Vue 2に慣れていればそれほど難しい変更ではないかと思います。
では、メリットは何かというと「コードの分割(共通化)」がしやすいということです。
例えば、paginationは別のコンポーネントでも使うなら、外部化しておいた方がいいですよね。そんな場合は次のようすることができます。
UserPosts.vue
import pagination from './pagination'
pagination.js
import { reactive } from 'vue'
export default reactive({
page: 1, // 現在のページ
limit: 10 // 10件ずつ表示
});
このようにしておけば、別のコンポーネントで使う場合にすぐ呼び出せますし、なんなら別の開発者も使うことができます。
refの使い方が変わった
前項目の話の細かい部分になるのですが、Vue 2のrefは個人的によく使う機能だったので詳しくご紹介します。
まず、refとはVueの中で特定の要素(<div>とか<input>とかですね)を簡単に取得できる機能で、以前はthis.$refs['*****']という使い方をしていました。
そして、Vue 3からはこのrefを使うにはsetup()内で指定をするように変更になりました。
Vue.createApp({
// 省略
setup() {
return {
emailInput: Vue.ref(null) // 👈 カッコの中身は初期値です。
};
}
}).mount('#app');
※こちらはcdnを使った場合です。
そして、ターゲットにしたい要素にref="*****"をつければOKです。
<input type="text" ref="emailInput">
これで、Vue内でthis.emailInputが使えるようになります。
this.emailInput.classList = 'test-class'; // <input> タグのCSSクラス名を変更しています
Suspenseでコンポーネント準備中は代替表示ができるようになった
Suspenseを使うと、コンポーネントの準備中、代替コンテンツを表示できるようになりました。
実際の使い方はこちらです。
<Suspense>
<template #default>
<!-- このコンポーネントの準備を待つ -->
<UserPost></UserPost>
</template>
<template #fallback>
⏳ 読み込み中...
</template>
</Suspense>
この例では、UserPostの準備ができらたら自動的に表示が切り替わります。
ちなみに、#fallbackの部分が表示されるのはコンポーネントのasyncが解決されるまでの間で、次のようなコードが必要です。
UserPost.vue
<template>
{{ post }}
</template>
<script>
export default {
async setup () {
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve({
id: 1,
title: 'Postタイトル'
});
}, 3000); // 3秒後に実行
});
const post = await promise;
return {
post
}
}
}
</script>
実際にブラウザで確認すると、まず次のような表示になります。
![]()
そして3秒後、表示は自動的にこうなります。
![]()
filterはなくなった
次のようなfilter機能はなくなったようです。(ちなみに私はcomputed派でなのでノーダメージでした😉)
{{ message | filterA | filterB }}
TypeScriptをサポートした
TypeScriptは、開発者の間で人気のテクノロジーですので、その流れを受けてVueでも採用されたようです。
おまけ:新しいVueを試す方法
2020.3.26現在、Vue 3はまだ正式にリリースしていませんが、アルファ版を先取りして試してみることができます。
【追記:2020.9.20】Vue 3は、2020年9月18日に正式リリースされました。
やり方は、まず適当なフォルダで以下のコマンドを実行します。
git clone https://github.com/vuejs/vue-next-webpack-preview.git
そして、/vue-next-webpack-previewフォルダに移動しパッケージをインストールします。
npm install
これで準備は完了です。
後は、同じ場所で次のコマンドを実行すればブラウザで「http://localhost:8080/」にアクセスできるようになります。
npm run dev
おわりに
ということで、今回はVueの次期バージョン3の変更点をまとめてみました。
ちなみに、たくさん追加機能や変更点を紹介したのでVue 2から相当変わっているような感じるかもしれませんが、カンファレンスの動画でも言及されているとおり(13:49あたりです)Vue 3になってもそれほど書き換えはしなくてもよさそうです。
また、Vue 3はファイルサイズが小さくなり高速化もしているので利用者にとってもメリットがあるんじゃないでしょうか。
私も根っからのVueファンとして、ドシドシこれからの開発に役立てたいと思います❗
ぜひ皆さんもチェックしてみてくださいね。(もしかすると、変更点があるかもないので、リリース後をおすすめします😉)
ではでは〜!

「うーん、正式リリースが待ち遠しい・・・
(この記事を書いてから半年後に正式リリースとなりました😉👍)」





