Vue 3.0.0-betaのお試し環境をVue CLIで作ってみた


2020/04/17 に Vue 3.0.0-beta.1 がリリースされました!
お試し用の環境を作って Vue Composition APIProxy によるリアクティブといった待望の機能を試してみましょう。

動作環境

  • macOS Catalina
  • Node.js 12.16.2
    • npm 6.14.4
  • Vue CLI v4.3.1
  • Chrome 81

Vue CLI のインストール

npm i -g @vue/cli

Vue CLI をグローバルインストールしたくない方は、以下の手順の vue コマンド部分を npx @vue/cli に読み替えていただいても大丈夫です。

環境構築

プロジェクトの作成

vue-3-beta-trial というプロジェクト名で環境を構築していきます:

vue create vue-3-beta-trial

設定は以下のようにしました:

Vue CLI v4.3.1
? Please pick a preset: Manually select features
? Check the features needed for your project: Babel, TS, Linter
? Use class-style component syntax? No
? Use Babel alongside TypeScript (required for modern mode, auto-detected polyfills, transpiling JSX)? No
? Pick a linter / formatter config: Basic
? Pick additional lint features: Lint on save
? Where do you prefer placing config for Babel, ESLint, etc.? In dedicated config files
? Save this as a preset for future projects? No

プラグインの導入

プロジェクトディレクトリが出来上がったら、vue-cli-plugin-vue-next を導入します:

cd vue-3-beta-trial
vue add vue-next

ファイルの修正

vue-cli-plugin-vue-next を導入した直後に npm run serve でビルドしようとしてもエラーが出てしまうので、 *.d.ts ファイルと *.vue ファイルを修正します:

  1. src/shims-tsx.d.ts の内容をすべてコメントアウトする
  2. src/App.vue, src/components/HelloWorld.vue の内容を以下のように修正:
src/App.vue
 <script lang="ts">
-import Vue from 'vue';
+import { defineComponent } from 'vue';
 import HelloWorld from './components/HelloWorld.vue';
src/App.vue
-export default Vue.extend({
+export default defineComponent({
   name: 'App',
   components: {
     HelloWorld

以上で npm run serve でビルドができるようになりました

ファイルの差分をみてみる

vue create コマンドで作成したプロジェクトは自動で git の初期コミットが済んだ状態のため、 git statusvue add vue-next コマンド以降に差分が出たファイルを確認できます:

$ git status
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git restore <file>..." to discard changes in working directory)
        modified:   .eslintrc.js
        modified:   package-lock.json
        modified:   package.json
        modified:   src/App.vue
        modified:   src/components/HelloWorld.vue
        modified:   src/main.ts
        modified:   src/shims-tsx.d.ts

package.json の差分

package.json
@@ -8,7 +8,7 @@
     "lint": "vue-cli-service lint"
   },
   "dependencies": {
-    "vue": "^2.6.11"
+    "vue": "^3.0.0-beta.1"
   },
   "devDependencies": {
     "@typescript-eslint/eslint-plugin": "^2.26.0",
@@ -16,10 +16,11 @@
     "@vue/cli-plugin-eslint": "~4.3.0",
     "@vue/cli-plugin-typescript": "~4.3.0",
     "@vue/cli-service": "~4.3.0",
+    "@vue/compiler-sfc": "^3.0.0-beta.1",
     "@vue/eslint-config-typescript": "^5.0.2",
     "eslint": "^6.7.2",
-    "eslint-plugin-vue": "^6.2.2",
+    "eslint-plugin-vue": "^7.0.0-alpha.0",
     "typescript": "~3.8.3",
-    "vue-template-compiler": "^2.6.11"
+    "vue-cli-plugin-vue-next": "~0.1.2"
   }
 }

src/main.ts の差分

src/main.ts
@@ -1,8 +1,4 @@
-import Vue from 'vue'
+import { createApp } from 'vue';
 import App from './App.vue'

-Vue.config.productionTip = false
-
-new Vue({
-  render: h => h(App),
-}).$mount('#app')
+createApp(App).mount('#app')

vuejs/rfcs0009-global-api-change.md に書いてある通り、 Vue 2系 におけるプラグイン導入(Vue.use())によるグローバル汚染問題の解消のためこのような書き方に変更となったようです。

お試し編

Vue Composition API を試してみる

https://composition-api.vuejs.org/ にあった Basic example をコピーしてビルドしてみました:

App.vue
<template>
  <button @click="increment">
    Count is: {{ state.count }}, double is: {{ state.double }}
  </button>
</template>

<script>
import { reactive, computed } from 'vue'

export default {
  setup() {
    const state = reactive({
      count: 0,
      double: computed(() => state.count * 2)
    })

    function increment() {
      state.count++
    }

    return {
      state,
      increment
    }
  }
}
</script>

シンプルなカウンターが動作しました。
(なお、上記のコードであれば Vue 2.6.11 に @vue/composition-api (Vue 2 向けのプラグイン)を導入して試すことができます)

Proxy な reactive を試してみる

Vue 3.0 では Proxy ベースのリアクティブ実装となる1ため、従来のリアクティブ実装2では実現できなかった配列のインデックスと一緒にアイテムを直接セットする場合についても変更を検知可能になるようです。

App.vue
<template>
  <button @click="increment1">
    counts[0] is: {{ state.counts[0] }}
  </button>
  <button @click="increment2">
    counts[1] is: {{ state.counts[1] }}
  </button>
</template>

<script>
import { reactive } from 'vue'

export default {
  setup() {
    const state = reactive({
      counts: [100, 200],
    })

    function increment1() {
      state.counts[0]++
    }

    function increment2() {
      state.counts[1]++
    }

    return {
      state,
      increment1,
      increment2,
    }
  }
}
</script>

Vue 2系では変更の検知ができなかった state.counts[0]++ といった操作でも、画面上に値が反映されました。

また、 <button> 要素が <template> 直下に2個並んでいることから、ルート要素は必ず1個という掟についてもなくなっていることが分かります。


  1. 現在(2020/04/19)のところ提供されていないIE11互換ビルドでは、従来のリアクティブ実装に近いものが用いられると思われます 

  2. Object.defineProperty() で getter/setter に変換する仕組みなどが用いられています。詳細は公式ドキュメントのリアクティブの探求を参照してください