カテゴリー: FrontEnd

もうすぐ登場!Vue 3の変更点まとめ

はじめに

いよいよ、待望の Vue 3 のリリースが近づいてきています。今回は Vue 3 での変更点について、まとめてみました。目玉機能である Composition API については、こちらをご覧ください。

仕様変更

複数のv-modelが定義可能に

これまでは、コンポーネントに渡す 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直下に複数のタグを記述可能に

これまでは、 <template> タグの直下に記述できるのは、1つのタグのみだったので、 <div> タグで全体を囲っていたりしていましたが、 Vue 3 では、 Fragment が利用できるようになるため、複数のタグを記述できるようになります。

例えば、親のコンポーネントに <ul> タグがあり、その中に <li> タグが複数定義されているコンポーネントを入れる、といったことができるようになります。

<template>
  <li>list 1</li>
  <li>list 2</li>
  <li>list 3</li>
</template>

開始処理がcreateAppに

従来のアプリの開始処理は、以下のように定義していましたが、

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の仕様変更

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の仕様変更

$attrs が拡張され、 $listeners で参照していたイベントや、別で管理されていた、 class や style も$attrs から取得できるようになります。

廃止

filterを廃止

filter には、学習コストの増加や、公文解析を複雑にするデメリットがあり、メソッドで代用可能なため、削除されます。

Functional Componentを廃止

通常のコンポーネントと Functional Component との性能差が大幅に縮小されたため、削除されます。

一部のEvent Emitter APIを廃止

$on 、 $off 、 $once が削除されます。 Event の管理には、 Mitt などのライブラリの使用が推奨されています。

新機能

コンポーネントを移動させるTeleport

<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 は、コンポーネントの準備中に別の表示をしたい場合に利用します。

<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サポートの強化

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 が出ています。正式リリースされるのが楽しみです。

おすすめ書籍

   

Hiroki Ono

シェア
執筆者:
Hiroki Ono
タグ: Vuejs

最近の投稿

フロントエンドで動画デコレーション&レンダリング

はじめに 今回は、以下のように…

3週間 前

Goのクエリビルダー goqu を使ってみる

はじめに 最近携わっているとあ…

4週間 前

【Xcode15】プライバシーマニフェスト対応に備えて

はじめに こんにちは、suzu…

2か月 前

FSMを使った状態管理をGoで実装する

はじめに 一般的なアプリケーシ…

3か月 前