🎉

【初心者向け】Vue.jsをToDoアプリを作りながら学ぼう

2020/11/23に公開

この記事では、Todoアプリを作りながら学んだVue.jsの基本をまとめています。
超初心者向け、超入門です。難しいことは書けないのでご容赦ください。。

筆者の学習時の技術レベル

  • プログラミング学習開始して5ヶ月、エンジニア実務経験 (Rails) 3ヶ月
  • バックエンド Rubyを中心に勉強しており、今後もバックエンドがメインのつもり
  • フロントエンドはJavaScript、jQueryの基本的な読み書きはできる程度

必要な前提知識

  • HTML/CSS/JavaScriptの基本的な知識

どうやってVue.jsを勉強したの?

超Vue JS 2 完全パック - もう他の教材は買わなくてOK! (Vue Router, Vuex含む)

僕は動画のほうが入りやすいので、まずUdemyですね。

タイトルにもあるように、他の教材を買わなくてもいいくらい充実した内容です。
Todoアプリを作るくらいなら全部やらなくても大丈夫ですが、
「Vue Routerを使ったSPA(single page application)を作りたい」
「Vue.jsで大規模開発を経験したい」といった人の導入にもおすすめです。

基礎から学ぶ Vue.js

こちらは定番のVue.js入門書です。

公式サポートサイトも充実しており、
今回のTodoアプリ作成もこのサイトをかなり参考しました。

公式ドキュメント(日本語)

Vue.jsは日本語の公式ドキュメントも充実しています。
上記のような教材で学びつつ公式情報を確認するのが、個人的にはいいかなと思いました。

Vue.jsってなにがいいの?

Vue.jsは、JavaScriptフレームワークの1つです。
要は「JavaScriptを簡単に使いやすくしたもの」で、他にはjQueryやReactがあります。

※フレームワークとライブラリの違いは割愛→こちらを参考にしてください

Vue.jsの特徴は主に下記3つです。(他にも色々とありますが、、)

1. 学習コストが低い

  • 日本語ドキュメント情報が充実している
  • 書籍や動画などの教材もわりと多い
  • 構造がシンプルで記述量も少ないため、比較的早く習得することができる

2. MVVMモデルが採用されている

  • MVVMモデルはModel(M)-View(V)-ViewModel(VM)の設計思想です。
  • なんか難しそうなので超簡単にイメージだけ説明します。

mvvm.png
(公式ガイドより抜粋)

  • ViewはDOM(Document Object Model)のことで、JavaScriptで扱うHTMLの要素です。
  • ModelはJavaScriptオブジェクトのこと。
  • ViewModelが重要で、ModelViewを同期するオブジェクト、Vueインスタンスです。ここで双方向データバインディングを実現しています。

双方向データバインディングとは、データと描写(View)を同期させる仕組みのことで、上記の場合View側・Model側どちらからでもデータを変更すれば同期されるようになっています。
Vue.jsは少ないコード量でこの仕組を実現しています。

設計パターンの難しい説明は割愛しますが、画面上に表示されるViewとJavaScriptを簡単につなげてくれるのがVueです。
主にここをゴニョゴニョ書いていきます。

3. SPA(シングルページアプリケーション)の作成に向いている

SPAは、単一のWebページからなるWebアプリケーションです。
画面遷移がほぼなく、動作が速い使い勝手の良いWEBアプリで、例えばWeb版のSlackやFacebookメッセンジャー、GoogleMapなどですね。

Vue.jsプロジェクトで、SPAは比較的簡単に作ることができます。

実際にVue.jsでToDoアプリを作ろう

下記サイトを参考にしたので、試してみてください。

今回はVue CLIのような開発ツールやVue Routerは使わず、シンプルにindex.htmlstyle.cssmain.jsの3ファイルで作成します。

まず完成形から共有

Image from Gyazo

1. HTML、CSSをざっくり組み立てます

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.js TODO APP</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="to-do"> <!-- Vue.jsで扱うDOM要素を指定するためidを付与 -->
    <p>
      NewTask:
      <input type="text">
      <button>Add</button>
    </p>
    <hr>
    <ul>
      <li>
        <input type="checkbox">
        <span>Rubyの勉強をする</span>
        <button>Delete</button>
      </li>
      <li>
        <input type="checkbox">
        <span>Vue.jsのアプリを作る</span>
        <button>Delete</button>
      </li>
      <li>
        <input type="checkbox">
        <span>Youtubeをみる</span>
        <button>Delete</button>
      </li>
    </ul>
  </div>
</body>
</html>
ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

ul > li {
  margin: 5px;
  text-indent: 0;
}

#to-do {
  width: 800px;
  margin: 0 auto;
}

2. Vue.jsを導入していきます

今回は簡単にscriptタグでHTMLに直接埋め込むCDNを使用します。

  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
  <script src="./main.js"></script>

そしてjsファイルにまずVueインスタンスを作成し、Vue.jsで扱う要素を指定します。
今回はid="to-do"以下の要素を扱います。
elはviewと紐付ける要素を指定します。classの場合、.to-doのようになります。

new Vue({
  el: '#to-do'
});

3. 初期値を設定しよう

HTMLにタスクが3つありますが、それらを初期値としてVueで設定します。

new Vue({
  el: '#to-do',
  data: {
    todos: [
      { task: 'Rubyの勉強をする', isCompleted: false },
      { task: 'Vue.jsのアプリを作る', isCompleted: false },
      { task: 'Youtubeをみる', isCompleted: false }
    ]
  }
});

isCompleted: falseは後ほど説明します。
dataは使用するデータの初期値を定義するオプションです。オブジェクトは配列も登録できます。

今回はtodosにタスクを3つ配列で、中身は連想配列で定義しています。

4. 登録したTodoを表示する

ここで登場するのが「ディレクティブ」です。
ディレクティブはviewに動きをつけるための特別な属性で、v-〇〇といった形をしています。

今回は配列で指定したTodoを繰り返し処理で表示したいので、v-forを使用します。

<li v-for="todo in todos">
  <input type="checkbox">
  {{ todo.task }}
  <button>Delete</button>
</li>

v-for

v-forの値はtodo in todosですが、
形式は(個々の要素) in (繰り返したいオブジェクト)といった具合です。

vueの値は二重中括弧で表示できるので、
タスク名の取得は{{ todo.task }}となります。
(連想配列のキーから取得しています)

5. チェックするとタスク完了にする(取り消し線を入れる)

次にチェックするとタスク名に取り消し線が入るようにします。

<li v-for="todo in todos">
  <input type="checkbox" v-model="todo.isCompleted">
  <span :class="{ 'complete': todo.isCompleted }">{{ todo.task }}</span>
  <button>Delete</button>
</li>
ul > li > .complete {
  text-decoration: line-through;
  color: #ddd;
}

v-model

まずv-model="todo.isCompleted"ですが、
v-modelは「双方向データバインディング」を実現するディレクティブです。

値には同期させたいデータを指定するので、今回の場合checkboxvalueとなります。
checkboxの場合、v-modelの値はboolean値(true/false)をとります。
初期値(data)設定の際、各タスクにisCompleted: falseと定義していましたが、
これはチェックボックスにチェックを入れていない状態を指します。

チェックON・OFFでtrue/falseと変化するので、v-modelの値は
v-model="todo.isCompleted"とします。

v-bind

次に:class="{ 'complete': todo.isCompleted }"ですが、
これはv-bind:class="{ 'complete': todo.isCompleted }"の省略記法です。

v-bindはhtmlの属性値をバインドする(結びつける)ためのディレクティブです。
ここではtodo.isCompletedの値(true/false)を受け取り、
classにcompleteをつけるかつけないを、チェックボックスのON・OFFで変更できるようにしています。

つまり、

  1. 初期値:チェックボックスOFF、isCompleted: false
  2. チェックボックスにチェックを入れる
  3. isCompleted: trueとなる
  4. v-bindで値を受け取り、completeクラスが付与されcssに定義したデザインが適用される

このような流れになります。

6. タスクを追加できるようにする

まず、入力フォームからタスクを追加するために、受け皿としてdataを追加します。

<p>
  NewTask:
  <input type="text" v-model="newTask">
  <button>Add</button>
</p>
new Vue({
  el: '#to-do',
  data: {
    newTask: '',

次にボタンを押したらタスクが追加できるように、クリックイベントを作成します。

<button v-on:click="addTodo()">Add</button>
methods: {
  addTodo: function() {
    if (this.newTask == '') return;
    this.todos.push(
      { task: this.newTask, isCompleted: false }
    );
    this.newTask = '';
  }
}

v-on

v-onはDOMイベントの際に使用するディレクティブで、クリックイベントの場合v-on:clickとなります。

そしてvue側でクリックイベントに対応するメソッドを定義します。

まず最初のif (this.newTask == '') return;ですが、入力したタスクはthis.newTaskで取得できます。
未入力の場合にこれ以降の処理を行わない(returnする)ということです。

そしてTodoリストであるthis.todosにタスクを追加し、newTaskは初期化しておきます。

7. タスクを削除できるようにする

<button v-on:click="deleteTodo(todo)">Delete</button>
methods: {
  addTodo: function() {
    if (this.newTask == '') return;
    this.todos.push(
      { task: this.newTask, isCompleted: false }
    );
    this.newTask = '';
  },
  deleteTodo: function(todo) {
    var index = this.todos.indexOf(todo)
    this.todos.splice(index, 1)
  }
}

最後にdeleteボタンでタスクを削除します。

deleteTodo(todo)では、引数で該当する要素を取得し、
methodにてtodosの配列がらindexを取得、タスクを削除します。

v-on:click@clickと省略できます。

完成!

<!DOCTYPE html>
<html lang="ja">
<head>
  <meta charset="UTF-8">
  <title>Vue.js TODO APP</title>
  <link rel="stylesheet" href="./style.css">
</head>
<body>
  <div id="to-do">
    <p>
      NewTask:
      <input type="text" v-model="newTask">
      <button @click="addTodo()">Add</button>
    </p>
    <hr>
    <ul>
      <li v-for="todo in todos">
        <input type="checkbox" v-model="todo.isCompleted">
        <span :class="{ 'complete': todo.isCompleted }">{{ todo.task }}</span>
        <button @click="deleteTodo(todo)">Delete</button>
      </li>
    </ul>
  </div>

  <!-- Vue.js -->
  <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11"></script>
  <script src="./main.js"></script>
</body>
</html>
new Vue({
  el: '#to-do',
  data: {
    newTask: '',
    todos: [
      { task: 'Rubyの勉強をする', isCompleted: false },
      { task: 'Vue.jsのアプリを作る', isCompleted: false },
      { task: 'Youtubeをみる', isCompleted: false }
    ]
  },
  methods: {
    addTodo: function() {
      if (this.newTask == '') return;
      this.todos.push(
        { task: this.newTask, isCompleted: false }
      );
      this.newTask = '';
    },
    deleteTodo: function (todo) {
      var index = this.todos.indexOf(todo)
      this.todos.splice(index, 1)
    }
  }
});
ul {
  margin: 0;
  padding: 0;
  list-style-type: none;
}

ul > li {
  margin: 5px;
  text-indent: 0;
}

#to-do {
  width: 800px;
  margin: 0 auto;
}

ul > li > .complete {
  text-decoration: line-through;
  color: #ddd;
}

※参考サイト再掲
下記サイトも参考にしたので、試してみてください。

Discussion