Vue.js最新版「Vue 3」を試してみた

f:id:tamb_kgn:20201007180846p:plain

はじめに

初登場のタンバリン大阪開発チームの加减です。よろしくお願いします。

今回は9月19日に正式リリースされたばかりのVue.jsの最新版「Vue 3」を触ってみたいと思います。

実行環境 バージョン
OS MacOS 10.15.6
Node.js 11.15.0
Vue CLI 4.5.6

インストール

とりあえず公式ドキュメントに沿ってやってみましょう。

v3.vuejs.org

いくつか方法がありますが、今回はVue CLIを使っていきます。

npm install -g @vue/cli
vue --version
@vue/cli 4.5.6

Vue CLIの準備ができたのでプロジェクトを作っていきます

vue create vue3-sample
Vue CLI v4.5.6
? Please pick a preset: Default (Vue 3 Preview) ([Vue 3] babel, eslint)

完了するまでちょっと時間がかかるのでコーヒーでも飲みながら待ちましょう

f:id:tamb_kgn:20201001004129p:plain

おっ!どうやら成功したようですね。指示通りに開発用のローカルサーバーを立ち上げてみましょう

cd vue3-sample
npm run serve

f:id:tamb_kgn:20201001004414p:plain

Oops... cannot find module 'vue-loader-v16/package.json' ok...

npm i --save-dev vue-loader-v16
+ vue-loader-v16@16.0.0-beta.5.4
added 9 packages from 43 contributors, updated 8 packages and audited 1326 packages in 7.039s
found 0 vulnerabilities

これでどやあ!

f:id:tamb_kgn:20201001005107p:plain

はい、お待たせしました。インストール完了です。

Composition API

Vue3に追加された機能の中でも、今回は「Composition API」にフォーカスしたいと思います。ちなみにVue.js 2系(以下Vue2)の「Options API」も引き続きサポートされています。 Composition APIの概要について、公式ドキュメントによると↓

Introduction | Vue.js

どうやらOptions APIだとdata, methods, computed, watchといったオプション単位でロジックを書く必要があり、コンポーネントが大きくなるにつれ複数機能の実装がコードの各所に散らばります。

結果、非常に読みづらいのに加え、ロジックの再利用性も低くなってしまいます。Composition APIではこの問題を解決してくれる、ということのようです。

ま、とりあえず触ってみましょう。

今回はサンプルとして、Vue2で書いた簡単なToDoリストアプリをVue3にリプレイスしてみたいと思います。全体のコードは以下から。

github.com

こんな感じの超シンプルなアプリです。

f:id:tamb_kgn:20201006190123g:plain

setup

大前提として、Composition APIではコンポーネントの処理をまとめてsetupメソッド内に定義します。 Vue2ではリアクティブデータはdataメソッド内で扱っていましたが、Vue3では新たに追加されたメソッドrefreactiveを利用して定義します。 それぞれ、reactiveはobject型、refはそれ以外のプリミティブ値に用います。使う際は忘れずimportしてください。

さらに、定義したリアクティブデータをtemplate内で使用するには最後にreturnしてあげる必要があります。

<script>
import { reactive, ref } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    //data() {
    //  return {
    //      input: 'Add task here',
    //      items: [],
    //      id: 0
    //  }
    //},
    setup() {
        let input = ref('');
        let items = reactive([]);
        let id = ref(0);
        return {
            input,
            items,
        }
    }
}
</script>

methods

続いてmethodsの定義ですが、非常にシンプルです。ES6そのままな感じ。 注意点として、リアクティブデータの値にアクセスするには変数名の後ろに.valueを付ける必要があります。 ちなみにtemplate側では.valueなしで大丈夫です。

また、Vue2ではthisでVueインスタンスを参照した上でプロパティにアクセスしていましたが、setup内ではthisは不要です。そもそもアクセスできません。

理由は、setupが実行されるのがVueインスタンスが生成される前のbeforeCreateおよびcreatedのタイミングだからです。

<script>
import { reactive, ref } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // methods: {
    //     addListItem() {
    //         this.id++;
    //         const item = {
    //             id: this.id,
    //             text: this.input,
    //             isDone: false,
    //         }
    //         this.items.push(item);
    //         localStorage.setItem('taskList', JSON.stringify(this.items));
    //         localStorage.setItem('lastID', JSON.stringify(item.id));
    //         this.input = '';
    //     },
    //},
    setup() {
        // 省略
        const addListItem = () => {
            if (input.value !== '') {
                id.value++;
                const item = {
                    id: id.value,
                    text: input.value,
                    isDone: false
                }
                items.push(item)
                localStorage.setItem('taskList', JSON.stringify(items));
                localStorage.setItem('lastID', JSON.stringify(item.id));
                input.value = '';
            }
        }
        return {
            input,
            items,
            addListItem,
        }
    }
}
</script>

computed

次はcomputedです。算出プロパティと呼んだりもしますね。

こちらもimportした上でcomputedメソッドを定義していきます。引数にコールバック関数で処理を追加します。

<script>
import { reactive, ref, computed } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // computed: {
    //     doingNum: function() {
    //         return this.items.filter(item => item.isDone === false).length;
    //     }
    // },
    setup() {
        // 省略
        const doingNum = computed(() => {
            return items.filter(item => item.isDone === false).length;
        });
        return {
            input,
            items,
            addListItem,
            doingNum
        }
    }
}
</script>

mounted

Vue3でもライフサイクルフックは健在です。 ただ呼び出し方が少し変更されています。

hook名の頭にonを付けたメソッドとして定義し、引数にコールバック関数を指定します。

<script>
import { reactive, ref, computed, onMounted } from 'vue';
export default {
    name: 'Sample',
    props: {
        inputText: String
    },
    // mounted: function() {
    //         const tasks = JSON.parse(localStorage.getItem('taskList'));
    //         if (tasks != null && tasks.length > 0) {
    //             this.id = JSON.parse(localStorage.getItem('lastID'));
    //             tasks.forEach(task => {
    //                 this.items.push(task);
    //             });
    //         }
    //     }
    setup() {
        // 省略
        const setListItem = () => {
            const tasks = JSON.parse(localStorage.getItem('taskList'));
            if (tasks != null && tasks.length > 0) {
                id.value = JSON.parse(localStorage.getItem('lastID'));
                tasks.forEach(task => {
                    items.push(task);
                });
            }
        }
        onMounted(setListItem);
        return {
            input,
            items,
            addListItem,
            doingNum
        }
    }
}
</script>

onMounted以外のhookについては公式ドキュメントを参照してみてください。

Lifecycle Hooks | Vue.js

Vue.js devtools

chrome.google.com

Chrome等でVue.jsアプリを開発する際に便利な拡張機能「Vue devtool」ですが、残念なことに2020年10月時点ではVue3に未対応のようです。

ですが、ありました解決策が↓

chrome.google.com

β版ですが問題なく使えましたので、開発のお供にどうぞ。

まとめ

Vue.jsを久々に触りましたが、Vue3のComposition APIでだいぶ書きやすくなりましたね。

今回ご紹介したComposition API以外にも、便利な機能がいくつか追加されているようですし、TypeScriptとの親和性アップや、パフォーマンス向上、バンドルサイズの圧縮等々、全体的にパワーアップしているようです。

ただ、現時点ではIE11をサポートしていないようですので、実際のプロジェクトで使う場合はサポートブラウザの要件確認が必須です。将来的にはサポートを予定しているようです。