いよいよ、待望の Vue 3 のリリースが近づいてきています。今回は Vue 3 での変更点について、まとめてみました。目玉機能である Composition API については、こちらをご覧ください。
これまでは、コンポーネントに渡す v-model は一つだけでしたが、複数の v-model を定義できるようになります。
v-model を2つ定義したい場合、呼び出し元では、 v-model:xxx=”yyy” というように定義します。
<sample-component v-model:firstname="firstname" v-model:lastname="lastname">
コンポーネント側のコードは、このようになります。 setup 関数については、 こちらをご覧ください。
<template> <div> <div> 姓:<input v-bind:value="lastname" v-on:input="updateLastname($event.target.value)"> </div> <div> 名:<input v-bind:value="firstname" v-on:input="updateFirstname($event.target.value)"> </div> </div> </template> <script> export default { props: { firstname: String, lastname: String }, setup(props, context) { function updateFirstname(value) { context.emit('update:firstname', value); } function updateLastname(value) { context.emit('update:lastname', value); } return { updateFirstname, updateLastname } } } </script>
これまでは、 <template> タグの直下に記述できるのは、1つのタグのみだったので、 <div> タグで全体を囲っていたりしていましたが、 Vue 3 では、 Fragment が利用できるようになるため、複数のタグを記述できるようになります。
例えば、親のコンポーネントに <ul> タグがあり、その中に <li> タグが複数定義されているコンポーネントを入れる、といったことができるようになります。
<template> <li>list 1</li> <li>list 2</li> <li>list 3</li> </template>
従来のアプリの開始処理は、以下のように定義していましたが、
import Vue from 'vue'; import App from './App.vue'; new Vue({ render: h => h(App), }).$mount('#app');
Vue 3 では、 createApp 関数を使って、定義するように変わりました。
import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app');
ちなみに、 vue-router は、 vue-router-next を使用するようになります。
// router/index.js import { createWebHistory, createRouter } from "vue-router"; import Home from "@/views/Home.vue"; import Hoge from "@/views/Hoge.vue"; const routes = [ { path: "/", name: "Home", component: Home, }, { path: "/hoge", name: "Hoge", component: Hoge, }, ]; const router = createRouter({ history: createWebHistory(), routes, }); export default router;
import { createApp } from 'vue' import App from './App.vue' import router from './router' createApp(App).use(router).mount('#app')
scoped CSS で使える擬似クラスとして、 ::v-deep() 、 ::v-slotted() 、 ::v-global が追加されました。
::v-deep() は、子のコンポーネントにスタイルを適用するための擬似クラスで、今までは、 /deep/ として設定することができました。
::v-deep(.hoge) {}
::v-slotted() は、slot で受け取った親のコンポーネントの要素に、スタイルを適用するためのものです。
::v-slotted(.hoge) {}
::v-global は、 scoped CSS 内で、グローバルなスタイルを定義するためのものです。
::v-global(.hoge) {}
$attrs が拡張され、 $listeners で参照していたイベントや、別で管理されていた、 class や style も$attrs から取得できるようになります。
filter には、学習コストの増加や、公文解析を複雑にするデメリットがあり、メソッドで代用可能なため、削除されます。
通常のコンポーネントと Functional Component との性能差が大幅に縮小されたため、削除されます。
$on 、 $off 、 $once が削除されます。 Event の管理には、 Mitt などのライブラリの使用が推奨されています。
<Teleport> タグで囲った部分を、 to属性で指定した先に移動させることができます。モーダルやポップアップなど、コンポーネントの外部に記述された領域に、コンポーネントの内容を反映する際に便利な機能です。
<template> <div> <!-- ↓を移動 --> <teleport to="#teleport-id"> <p>This is child component.</p> <p>My name is {{ name }}.</p> </teleport> </div> </template>
たとえば、子のコンポーネントの内容を
<template> <div> <div> <p>This is parent component.</p> </div> <!-- ↓に移動する --> <div id="teleport-id"></div> </div> </template>
親のコンポーネントに移動させます。
Suspense は、コンポーネントの準備中に別の表示をしたい場合に利用します。
<suspense> タグの中に2つの <template> タグを記述します。 #default が指定されている方の <template> タグに含まれる、すべての非同期コンポーネントの準備が完了するまでは、 #fallback が指定されいる方の <template> タグの内容が表示されます。
<template> <suspense> <template #default> <async-component1/> <async-component2/> <async-component3/> </template> <template #fallback> <div>ロード中です。</div> </template> </suspense> </template>
ちなみに、非同期コンポーネントを作成するには、 setup() 関数を async/await にする必要があります。
<template> <div>{{ item.name }}</div> <div>{{ item.price }}</div> </template> <script> export default { async setup() { const fetchItem = () => { return new Promise( resolve => { setTimeout(() => { resolve({ name: 'hoge', price: 100 }) }, 1000) }); } const item = await fetchItem(); return { item } } } </script>
これまでは、配列の要素の更新や、オブジェクトのプロパティの追加、削除を画面に反映させるには、以下のように Vue.set や Vue.delete メソッドで操作する必要がありました。
this.dataArr = [1,2,3]; // Vue.set(dataArr, 0, 2); Vue 2 ではこちら this.dataArr[0] = 2; // Vue 3 ではこちら this.dataObj = { name: 'hoge', age: 20 }; // Vue.delete(this.dataObj, 'age'); Vue 2 ではこちら delete this.dataObj['age']; // Vue 3 ではこちら
Vue 3 では、これらの変更が直接画面に反映されるようになります。
TypeScript 環境で Composition API を利用する場合、 defineComponent メソッドを利用すると、型推論が有効になります。
import { defineComponent } from "vue"; export default defineComponent({ setup() { // 省略 } })
また、 reactive や ref メソッドで記述する変数の型を、指定できます。
// reactive の場合 const state = reactive<{ firstname: string; lastname: string; age: number; }>({ firstname: 'Taro', lastname: 'Yamada', age: 20 }) // ref の場合 const firstname = ref<string>('Taro'); const age = ref<string>('Yamada'); const lastname = ref<number>(20);
Composition API については、くわしくはこちらをご覧ください。
まもなくリリースされる、 Vue 3 での変更点をまとめました。今は、 v-3.0.0-rc.10 が出ています。正式リリースされるのが楽しみです。