もふもふ技術部

IT技術系mofmofメディア

Vue3 Composition APIのreactiveに触れる

Vueの新バージョン、3.0のbeta版が出たらしいので、みんな大好きTODOリストを作って書き味を試してみようと思います。

なお、完成したコードはこちらにおいてあります。よければ参考にしてください。 https://github.com/yubachiri/vue3-todo-sample

CLIのインストール

僕のCLIはだいぶ古かったです。過去にVueを利用してしばらく触ってない場合は、念のため最新のものを入れておきましょう

$ npm uninstall vue-cli -g
$ npm install @vue/cli@4.4.1 -g
$ vue version

バージョン指定をしない場合、2020/5/29時点では3.0.1が入りました。が、4.0.1で修正されているバグに遭遇したり、vue-nextが入らなかったため、最新版を指定しています。参照: Create new project - TypeError: api.assertCliVersion is not a function

Vue3を触れる環境構築

プロジェクト作成

$ vue create sample-project

選択肢が出ますが、defaultでOKです。プロジェクトが作成できたら、試しに起動してみましょう。

$ cd sample-project
$ npm run serve

http://localhost:8080/ でこの画面が表示されればOKです。

vue-default

Vue3 ベータ版の導入

公式プラグインを入れます。CLIのバージョンが低いとエラーが出るので、確認しておきましょう。

$ vue add vue-next

これで準備完了です。実装していきましょう。

実装

HelloWorldする

まずは挨拶から。これを App.vueに丸々コピペしてください。今回はApp.vueしか触りません。

<template>
  <div id="app">
    <p>
      {{state.greet}}, {{state.version}}
    </p>
  </div>
</template>

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

export default defineComponent({
  setup() {
    const state = reactive({
      greet: "Hello",
      version: "Vue 3"
    })

    return {
      state
    }
  }
})
</script>

<style>
#app {
  font-family: Avenir, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-align: center;
  color: #2c3e50;
  margin-top: 60px;
}
</style>

今までのVueとは一味違った構文ですね。従来 data()で書いていたstateは、reactiveで定義できるようになっています。なお、defineComponentはts採用時に効果を発揮するものだそうです。コンポーネント内での型推論が可能になるとのこと。今回はプロジェクトの作成をデフォルト設定で行ったので、恩恵を受けられません。 参考 composition-api#typescript

表示される画面はこちら。

vue-hello

いいですね。ではTODOしていきましょう。

TODOアプリ作る

まずはタスクの登録。

<template>
  <div id="app">
    <input type="text" v-model="state.input" />
    <button @click="addTodo">
      Add TODO
    </button>

    <p v-for="todo in state.todos" :key="todo">
      {{todo}}
    </p>
  </div>
</template>

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

export default defineComponent({
  setup() {
    const state = reactive({
      input: "",
      todos: [],
    })

    function addTodo() {
      state.todos = [...state.todos, state.input]
      state.input = ""
    }

    return {
      state,
      addTodo,
    }
  }
})
</script>

templateには以下の変更を行いました。 - textをバインドしたinputを設置 - 入力された値を配列に追加するためのボタンを設置

scriptの方は、 - todoを格納する配列を定義 - todoを追加する関数を定義

関数は従来 methodsで定義していましたね。自由度が上がっているように見えます。やりようによっては以前より見通しの良いコードになりそうです。 関数は定義したらreturnするようにしましょう。

あとは、todoの完了管理ができるようになったら完成です。簡単ですね。

<template>
  <div id="app">
    <input type="text" v-model="state.input" />
    <button @click="addTodo">
      Add TODO
    </button>

    <p v-for="(todo, index) in state.todos" :key="index">
      {{todo}}
      <button @click="completeTodo(index)">
        Complete
      </button>
    </p>

    <p>Completed</p>
    <p v-for="(todo, index) in state.completedTodos" :key="index">
      {{todo}}
    </p>
  </div>
</template>

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

export default defineComponent({
  setup() {
    const state = reactive({
      input: "",
      todos: [],
      completedTodos: [],
    })

    function addTodo() {
      state.todos = [...state.todos, state.input]
      state.input = ""
    }

    function completeTodo(index) {
      state.completedTodos = [...state.completedTodos, state.todos[index]]

      state.todos.splice(index, 1)
      state.todos = [...state.todos]
    }

    return {
      state,
      addTodo,
      completeTodo
    }
  }
})
</script>

templateの変更 - 各todoに完了ボタンを設置 - 完了したtodoを表示

scriptの変更 - 完了したtodoを格納する配列を定義 - todoを完了する関数を定義

です。雑ですが十分でしょう。

まとめ

構文がガラッと変わりますが、stateや関数の定義あたりはReactのfunction componentっぽさを感じて割と好みです。型推論が真価だと思うので、これはts入れるべきでしたね。vue createで手動設定を選び、ちゃっちゃと入れちゃうのが吉。