はじめに
いよいよ、待望の Vue 3 のリリースが近づいてきています。今回は Vue 3 での変更点について、まとめてみました。目玉機能である Composition API については、こちらをご覧ください。
仕様変更
複数のv-modelが定義可能に
これまでは、コンポーネントに渡す v-model は一つだけでしたが、複数の v-model を定義できるようになります。
v-model を2つ定義したい場合、呼び出し元では、 v-model:xxx=”yyy” というように定義します。
1 | <sample-component v-model:firstname="firstname" v-model:lastname="lastname"> |
コンポーネント側のコードは、このようになります。 setup 関数については、 こちらをご覧ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 | <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直下に複数のタグを記述可能に
これまでは、 <template> タグの直下に記述できるのは、1つのタグのみだったので、 <div> タグで全体を囲っていたりしていましたが、 Vue 3 では、 Fragment が利用できるようになるため、複数のタグを記述できるようになります。
例えば、親のコンポーネントに <ul> タグがあり、その中に <li> タグが複数定義されているコンポーネントを入れる、といったことができるようになります。
1 2 3 4 5 | <template> <li>list 1</li> <li>list 2</li> <li>list 3</li> </template> |
開始処理がcreateAppに
従来のアプリの開始処理は、以下のように定義していましたが、
1 2 3 4 5 6 | import Vue from 'vue'; import App from './App.vue'; new Vue({ render: h => h(App), }).$mount('#app'); |
Vue 3 では、 createApp 関数を使って、定義するように変わりました。
1 2 3 4 | import { createApp } from 'vue'; import App from './App.vue'; createApp(App).mount('#app'); |
ちなみに、 vue-router は、 vue-router-next を使用するようになります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | // 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; |
1 2 3 4 5 | import { createApp } from 'vue' import App from './App.vue' import router from './router' createApp(App).use(router).mount('#app') |
scoped cssの仕様変更
scoped CSS で使える擬似クラスとして、 ::v-deep() 、 ::v-slotted() 、 ::v-global が追加されました。
::v-deep() は、子のコンポーネントにスタイルを適用するための擬似クラスで、今までは、 /deep/ として設定することができました。
1 | ::v-deep(.hoge) {} |
::v-slotted() は、slot で受け取った親のコンポーネントの要素に、スタイルを適用するためのものです。
1 | ::v-slotted(.hoge) {} |
::v-global は、 scoped CSS 内で、グローバルなスタイルを定義するためのものです。
1 | ::v-global(.hoge) {} |
$attrsの仕様変更
$attrs が拡張され、 $listeners で参照していたイベントや、別で管理されていた、 class や style も$attrs から取得できるようになります。
廃止
filterを廃止
filter には、学習コストの増加や、公文解析を複雑にするデメリットがあり、メソッドで代用可能なため、削除されます。
Functional Componentを廃止
通常のコンポーネントと Functional Component との性能差が大幅に縮小されたため、削除されます。
一部のEvent Emitter APIを廃止
$on 、 $off 、 $once が削除されます。 Event の管理には、 Mitt などのライブラリの使用が推奨されています。
新機能
コンポーネントを移動させるTeleport
<Teleport> タグで囲った部分を、 to属性で指定した先に移動させることができます。モーダルやポップアップなど、コンポーネントの外部に記述された領域に、コンポーネントの内容を反映する際に便利な機能です。
1 2 3 4 5 6 7 8 9 | <template> <div> <!-- ↓を移動 --> <teleport to="#teleport-id"> <p>This is child component.</p> <p>My name is {{ name }}.</p> </teleport> </div> </template> |
たとえば、子のコンポーネントの内容を
1 2 3 4 5 6 7 8 9 | <template> <div> <div> <p>This is parent component.</p> </div> <!-- ↓に移動する --> <div id="teleport-id"></div> </div> </template> |
親のコンポーネントに移動させます。
準備中を表すSuspense
Suspense は、コンポーネントの準備中に別の表示をしたい場合に利用します。
<suspense> タグの中に2つの <template> タグを記述します。 #default が指定されている方の <template> タグに含まれる、すべての非同期コンポーネントの準備が完了するまでは、 #fallback が指定されいる方の <template> タグの内容が表示されます。
1 2 3 4 5 6 7 8 9 10 11 12 | <template> <suspense> <template #default> <async-component1/> <async-component2/> <async-component3/> </template> <template #fallback> <div>ロード中です。</div> </template> </suspense> </template> |
ちなみに、非同期コンポーネントを作成するには、 setup() 関数を async/await にする必要があります。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | <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 メソッドで操作する必要がありました。
1 2 3 4 5 6 7 | 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サポートの強化
TypeScript 環境で Composition API を利用する場合、 defineComponent メソッドを利用すると、型推論が有効になります。
1 2 3 4 5 6 7 | import { defineComponent } from "vue"; export default defineComponent({ setup() { // 省略 } }) |
また、 reactive や ref メソッドで記述する変数の型を、指定できます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | // 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 が出ています。正式リリースされるのが楽しみです。