Vue.js + ES6: 公式サイトTodoMVCの例をいろいろな手法でつくってみる

[Vue.js公式サイト](https://jp.vuejs.org)には、学習のために「例」のコーナーが設けられています。「[TodoMVC の例](https://jp.vuejs.org/v2/examples/todomvc.html)」は、そのサンプルのひとつです。SPA(シングルページアプリケーション)のチュートリアルとして、Todoリストはよく扱われます。

「TodoMVC の例」が備える基本的な機能は、それらと大きく変わりません。けれど、やたらとボタンが並ぶことなく、必要に応じて表示されたり、確定や取り消しは[enter]/[esc]といったキー入力で済ますことによる、洗練されたインタフェースが特長です(図001)。

#### 図001■TodoMVCの画面表示
[![FN1707004_002.png](https://qiita-image-store.s3.amazonaws.com/0/128949/54242261-84bf-434d-2b93-af10a3ccdac2.png)
\>\> 公式サイト「TodoMVC の例」へ](https://jp.vuejs.org/v2/examples/todomvc.html)

ちなみに、筆者が初心者向けに書いたチュートリアル「[Vue.js + ES6入門](http://www.fumiononaka.com/Business/index.html#Vue)」01-12は、ボタンだらけのダサダサなデザインを採用しました。その方が、コードはわかりやすくなるからです(サンプル001)。Vue.jsがはじめての人は、こちらから読まれた方がよいでしょう。このチュートリアルをテキストにしたハンズオンの「[Vue.js入門講座](https://67.org/ws/workshop/detail/0136javascript.html)」が開講されています。興味がありましたら、ご検討ください。

#### サンプル001■Vue.js + ES6: props validation and key attribute
<p class="codepen" data-height="360" data-theme-id="default" data-default-tab="js,result" data-user="FumioNonaka" data-slug-hash="QeGGKZ" style="height: 360px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js + ES6: props validation and key attribute">
  <span>See the Pen <a href="https://codepen.io/FumioNonaka/pen/QeGGKZ">
  Vue.js + ES6: props validation and key attribute</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>

Vue.jsは初心者から商用向けの規模の大きな開発まで、知識や技術に応じたコーディングができます。その手法や技術を解説したチュートリアルなどの記事がそれなりの本数書きたまりました。本稿はそれらのテーマと内容の簡単なご紹介です。実際に動きが確かめられるコードサンプルも交えています。詳しい解説は、それぞれに引用した記事をお読みください。

# 01 公式サイトの例をECMAScript 2015の構文にしてみる
「[Vue.js + ES6: TodoMVCをつくる](http://www.fumiononaka.com/Business/index.html#Vue)」01-05は、コードの組み立ては公式の例にもとづき、構文をECMAScript 2015(ES6)に改めたチュートリアルです(サンプル002)。

#### サンプル002■Vue.js + ES6: Vue TodoMVC 05
<p class="codepen" data-height="360" data-theme-id="default" data-default-tab="js,result" data-user="FumioNonaka" data-slug-hash="bGGmXqW" style="height: 360px; box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 2px solid; margin: 1em 0; padding: 1em;" data-pen-title="Vue.js + ES6: Vue TodoMVC 05">
  <span>See the Pen <a href="https://codepen.io/FumioNonaka/pen/bGGmXqW">
  Vue.js + ES6: Vue TodoMVC 05</a> by Fumio Nonaka (<a href="https://codepen.io/FumioNonaka">@FumioNonaka</a>)
  on <a href="https://codepen.io">CodePen</a>.</span>
</p>
<script async src="https://static.codepen.io/assets/embed/ei.js"></script>


公式サイトの例は日本語環境で入力するとき、正しく動かない問題がありました。キーイベントの選択によっては、変換確定と単独で入力した[enter]キーが区別されないのです(図002)。そこで、本チュートリアルには「[Vue.js: TodoMVCの例で日本語の項目が正しく入力できるようにする](https://qiita.com/FumioNonaka/items/2dfb272156e48cfcf824)」にもとづく対処が加えてあります。

#### 図002■日本語変換確定の[enter]キーで項目が追加されてしまう
![1812001_001.png](https://qiita-image-store.s3.amazonaws.com/0/128949/ba3b5eeb-5924-5534-71d9-2845a9cbc467.png)


# 02 Vue CLI 3のひな形から単一ファイルコンポーネントでつくる

「[単一ファイルコンポーネント](https://jp.vuejs.org/v2/guide/single-file-components.html)」は、コンポーネントごとにテンプレートのHTML記述とJavaScriptコード、そしてCSSスタイルをひとつのファイル(vue)にまとめる開発手法です。コンポーネントに分けて開発することにより、管理やテスト、使い回しもしやすくなります。ただし、開発環境を整えなければなりません。

```vue:単一ファイルコンポーネント.vue
<template>
	<!-- HTML記述 -->
</template>

<script>
	// JavaScriptコード
</script>

<style>
	/* CSSスタイル */
</style>
```

[Vue CLI](https://cli.vuejs.org)を使えば、開発環境とともに単一ファイルコンポーネントのひな形プロジェクトが簡単にできます(「[Vue CLI 3入門](http://www.fumiononaka.com/Business/index.html#Vue_CLI)」01-06参照)。このひな形からTodoMVCアプリケーションをつくるのが「[Vue.js + CLI入門](http://www.fumiononaka.com/Business/index.html#Vue_with_CLI)」01-08です。でき上がったアプリケーションの見た目や動きは、前2項の作例と変わりません(サンプル003)。

#### サンプル003■vue-todo-mvc-07
[![FN1707004_003.png](http://www.fumiononaka.com/TechNotes/Images/FN1707004_003.png)
\>\> CodeSandboxへ](https://codesandbox.io/s/vue-todo-mvc-07-rdx8f)

けれど、アプリケーションのプロジェクトファイル構成をみれば、単一ファイルコンポーネント(vue)の組み合わせでつくられていることがわかります(図003)。

#### 図003■単一ファイルコンポーネントによるプロジェクトファイル構成
![qiita_1911001_01.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/5078c807-bd57-d88f-2610-7979111ea1db.png)

Vue.jsにはアニメーションの機能も備わっています。公式の例にはないリスト項目のアニメーションを試したのが、「[Vue.js + CLI入門 08: 要素にアニメーションを加える](http://www.fumiononaka.com/Business/html5/FN1910001.html)」です。実際の動きは、[CodeSandboxサンプル](https://codesandbox.io/s/vue-todo-mvc-08-ccosp)でお確かめください。


# 03 `v-for`ディレクティブの定めには一意の`key`属性が必要
`v-for`は、複数データから要素を動的につくってページに差し込むディレクティブです。使う際の注意を解説した「[Vue.js: v-forで項目インデックスをkey属性にしていいのか](https://qiita.com/FumioNonaka/items/d1d9c9335116426a8316)」がよく参照されています。

ディレクティブで加える各要素には、一意の`key`属性を定めなければなりません。正しい属性値を与えないとき、不具合の起こる典型が前述のアニメーションです。この記事では、TodoMVCのアニメーションで、具体的にどのような動きになってしまうのかをわかりやすく示しました。


# 04 Vue I18nで多言語に対応させる
Vue I18nは、Vueアプリケーションを国際化(多言語対応)させるためのプラグインです(「[Vue.js: Vue I18nでアプリケーションを多言語に対応させる](https://qiita.com/FumioNonaka/items/bb994ab520bb0b268f65)」参照)。「[Vue.js + Vue I18n: アプリケーションを多言語に対応(国際化)させる](http://www.fumiononaka.com/Business/html5/FN1910002.html)」は、前掲「Vue.js + CLI入門 08」でつくったアニメーションの作例を、日英2ヶ国語対応にします。

各国語の翻訳はロケールにわけて、変数(プロパティ)でもたせます(JSON形式に対応)。

```js
const messages = {
	en: {
		message: {
			archive: 'Clear completed'
		}
	},
	ja: {
		message: {
			archive: '断捨離'
		}
	}
};

```

すると、`VueI18n`インスタンスのロケール(`locale`)を変えるだけで、テンプレートに定めた変数のテキストが切り替わるのです(図004)。実際の結果は、[CodeSandboxサンプル](https://codesandbox.io/s/vue-i18n-todo-mvc-7pnti)でご覧ください。

```js
const i18n = new VueI18n({
	locale: 'ja',
	messages,
});
new Vue({
	i18n,

}).$mount('#app');
```

#### 図004■変数値の差し込まれた翻訳テキストが表示される
[![FN1910002_007.png](http://www.fumiononaka.com/TechNotes/Images/FN1910002_007.png)
\>\> CodeSandboxへ](https://codesandbox.io/s/vue-i18n-todo-mvc-7pnti)


# 05 単一ファイルコンポーネントにVuexを使う
VuexはVueアプリケーションに状態管理の仕組みを提供するライブラリです。アプリケーションにひとつだけつくられるStoreが状態を一手に引き受け、決まったやり方でしか変えられません。

単一ファイルコンポーネントのTodoMVCアプリケーションにVueを組み合わせたチュートリアルが「[Vue.js + Vuex入門](http://www.fumiononaka.com/Business/index.html#Vuex)」01-08です。でき上がった[CodeSandboxサンプル](https://codesandbox.io/s/vue-vuex-todo-mvc-08-c4381)の見た目、動きとも、もちろん前掲の作例と変わりません。注目していただきたいのは、VuexのStoreモジュール(`store.jp`)が加わっていることです(図005)。

#### 図005■VuexのStoreモジュールが加わったプロジェクトファイル構成
<img width="980" alt="qiita_1911001_02.png" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/128949/4d2e95ec-7bcc-305a-076f-ee27024fb0b9.png">

コンポーネントの組み立ては前掲02の単一ファイルコンポーネントのプロジェクトと変わりません。「Vue.js + CLI入門 02」02「[項目をボタンで削除する](http://www.fumiononaka.com/Business/html5/FN1812001.html#sec02)」では、孫の項目(`TodoItem`)から子のリスト(`TodoList`)にイベント(`remove-todo`)を送り、さらに親(`App`)にバケツリレーして、項目が削除されました。

`src/components/TodoItem.vue`
[`@click="removeTodo"` → `this.$emit('remove-todo', this.todo)`]
　　　　　↓
`src/components/TodoList.vue`
[`@remove-todo="removeTodo"` → `this.$emit('remove-todo', todo)`]
　　　　　↓
`src/App.vue`
[`@remove-todo="removeTodo"` → `removeTodo(todo)`]

けれど、Vuexを使えば、データの参照・変更はStoreが一手に引き受けます。「Vue.js + Vuex入門 02」02「[項目ごとにボタンで削除する](http://www.fumiononaka.com/Business/html5/FN1909003.html#sec02)」では、孫(`TodoItem`)からでも、Storeに直に削除を頼めば済むのです。データの変更については、Storeのモジュールがいわばハブになると捉えればよいでしょう。

`src/components/TodoItem.vue`
[`@click="removeTodo"` → `this.$store.commit('removeTodo'`]
　　　　　↓
`src/store.js`
[`removeTodo(state, todo)`]

Vueのコードのさまざまな書き方は、知識や技術、あるいはつくるプロジェクトによって選択が変えられます。長短を比べて評価したうえで、もっとも適した手法をお選びください。