Vue 3の新しい機能と変更点・全11件

こんにちは❗フリーランス・エンジニアの 九保すこひ です。

さてさて、前回「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 🎉」

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 2refは個人的によく使う機能だったので詳しくご紹介します。

まず、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ファンとして、ドシドシこれからの開発に役立てたいと思います❗

ぜひ皆さんもチェックしてみてくださいね。(もしかすると、変更点があるかもないので、リリース後をおすすめします😉)

ではでは〜!

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

この記事が役立ちましたらシェアお願いします😊✨ by 九保すこひ
また、わかりにくい部分がありましたらお問い合わせからお気軽にご連絡ください。
(また、個人レッスンも承ってます👍)
このエントリーをはてなブックマークに追加       follow us in feedly