# Vue.js を触ってみよう

# 事前準備

  • HTML, javascriptを書くためのエディタを用意してください。
  • 好きなブラウザを開き、開発者ツールが使える状態にしてください。

# Vue.js とは

公式サイト: https://jp.vuejs.org/ (opens new window)

2014年頃に登場したJavaScriptフレームワークです。軽量さと導入しやすさが主眼に置かれており、他のライブラリと組み合わせたり既存のアプリケーションに追加で導入するのも容易に作られています。

開発者のエヴァンさんはAngularJSの開発に関わった人で、Angularの本当に好きだった部分を抽出したかったそうです。

特徴としては

  • view(JavaScriptとHTMLの紐付け)部分中心のフレームワーク
    • routerや状態管理など、SPAを構築するのに必須な機能は公式が出している周辺ライブラリとして導入する
    • HTTPリクエストなども外部ライブラリ(今ではfetch APIで良さそう)
  • SPAみたいな大規模なだけでなく、HTMLを少し動的にしたいような用途でも導入できる
    • 今回のハンズオンでやるのはこれ
  • ファイルサイズが軽量
  • VirtualDOM による高速なレンダリング
  • component 指向
  • 公式ドキュメント (opens new window)が親切で充実している
  • vue-routerやvue-cliなど準公式ライブラリが充実している

「公式ドキュメントが親切」というとあいまいな言い方ですが、かなり初期から日本語ドキュメントが充実していたり、書き方も全体的にとっつきやすく作られています。

https://jp.vuejs.org/v2/guide/index.html (opens new window)

導入可能な幅が広く、小さなWebページにとりあえず導入するような使い方から、vue-cliなどを用いた本格的なSPAの構築まで幅広く利用可能です。

反面大規模なアプリケーションを構築する場合は、きちんと設計を行わないと生産性が低くなりがちです。

またSSR(サーバサイドレンダリング)用のNuxtJS (opens new window)も人気です。

# First Step...の前に

vueを使ってみる前に、まずはvueなしのJavaScriptでHTMLを操作するとどんな感じなのか、体験してみましょう。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>iij-bootcample Vue.js</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
  ここにメッセージ↓
  <div id="app">
  </div>

  <script type="text/javascript">
    const message = 'Welcome to iij-bootcamp!!';

    const elem = document.getElementById('app');
    elem.innerText = message;
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

「ここにメッセージ↓」の下にJavaScriptで文字列を追加している簡単なコードです。 document.getElementById('app') はhtmlの中からidappは要素を取得するJavaScriptの機能です。

この例にあるelemのようなオブジェクトをDOM要素(element)だったり、HTML要素などと呼びます。

次はもう少し複雑な例をやってみましょう。

<!doctype html>
<html lang="ja">

<head>
  <meta charset="utf-8">
  <title>iij-bootcample Vue.js</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
</head>

<body>
  ここにメッセージ↓
  <div id="app">{{ message }}</div>

  <table id="people_table" border="1">
    <tr>
      <th>id</th>
      <th>name</th>
    </tr>
  </table>

  <script type="text/javascript">
    const message = "Welcome to iij-bootcamp!!";

    const elem = document.getElementById("app");
    elem.innerText = message;

    const peoples = [
      { id: 1, name: 'taro' },
      { id: 2, name: 'jiro' },
      { id: 3, name: 'saburo' }
    ];
    const table = document.getElementById("people_table");
    peoples.forEach(people => {
      const tr = document.createElement("tr");
      const idTd = document.createElement("td");
      const nameTd = document.createElement("td");

      idTd.innerText = people.id;
      nameTd.innerText = people.name;

      tr.appendChild(idTd);
      tr.appendChild(nameTd);

      table.appendChild(tr);
    });
  </script>
</body>

</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51

これはpeoplesという配列の中の値をJavaScriptでテーブルに追加する例です。ちょっと面倒になってきましたね。 document.createElement("tr")はHTMLのtr要素を作成します。tdも同様に作成し、appendChildで子要素として追加します。

このようにhtmlのDOMにJavaScriptからアクセスし、DOM要素を操作することでhtmlを書き換えるのが基本になります。

# First Step

JavaScriptでDOM操作がなんとなくできたところで、Vue.jsを導入するとどうなるかやってみましょう。 以下のようなファイルを作ってブラウザでアクセスしてみてください。

<!doctype html>
<html lang="ja">
<head>
  <meta charset="utf-8">
  <title>iij-bootcample Vue.js</title>
  <base href="/">

  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
  <div id="app">
    {{ message }}
  </div>

  <script type="text/javascript">
    const app = new Vue({
      el: '#app',
      data: {
        message: 'Welcome to iij-bootcamp!!'
      }
    })
  </script>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

Welcome to iij-bootcamp!!と表示されたでしょうか。これは一番初めの例と同じことをvueで行っています。

vueではnew Vue()の宣言時にdataの変数やelで紐付けるHTML要素、その他メソッドなどを宣言的に記述していきます。ここでは単に文字列を表示しているだけですが、表示内容はリアルタイム(リアクティブ)に変更可能です。JavaScript部分を以下のように変更してみてください。

const app = new Vue({
  el: '#app',
  data: {
    message: 'Welcome to iij-bootcamp!!'
  }
})

setTimeout(function() {
  app.message = "message changed"
}, 5000)
1
2
3
4
5
6
7
8
9
10

これは画面が表示されてから5秒後にapp.messageの内容を変更するコードです。JavaScriptの変数に代入するだけで画面が自動的に更新されたのが分かると思います。このようにVueはVue Objectの中の変数の変更を検知して、それを自動的に画面に反映してくれます。

# ディレクティブ

ディレクティブとは、HTML要素の中に書けるvue独自の機能です。具体例を見てみましょう。 body タグの中身を以下のようにしてみてください。

<div id="app">
  <span v-bind:style="{color: activeColor}">{{ message }}</span>

  <div v-if="activeColor === 'red'">赤です</div>
  <div v-if="activeColor === 'green'">緑です</div>
  <div v-if="activeColor === 'blue'">青です</div>
</div>

<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!',
      activeColor: 'black'
    }
  })

  setTimeout(() => {
    app.activeColor = 'red';
  }, 5000)

  setTimeout(() => {
    app.activeColor = 'green';
  }, 8000)

  setTimeout(() => {
    app.activeColor = 'blue';
  }, 11000)
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

指定した秒数ごとに色が変わりつつ、メッセージも変化したと思います。

上のコードではv-bindv-ifの2種類、ディレクティブを使っています。

v-bind:の後にHTMLの属性名を書くことで、その属性を動的に変更できるディレクティブです (試しにv-bindを使わずstyle="color: activeColor"のようにstyle属性に直接指定するとどうなるか試してみてください)。

v-ifは非常によく使うディレクティブで、条件式の中身がtrueな場合のみ要素を表示してくれます。

このようにディレクティブを使うことで、リアルタイムな画面の表示切り替えをシンプルなコードで実現できます。

他に非常によく使うディレクティブとして、繰り返しを行うv-forがあります。

<div id="app">
  <span>{{ message }}</span>

  <table border="1">
    <tr>
      <th>id</th>
      <th>name</th>
    </tr>
    <tr v-for="people in peoples">
      <td>{{ people.id }}</td>
      <td>{{ people.name }}</td>
    </tr>
  </table>
</div>

<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!',
      peoples: [
        { id: 1, name: 'taro' },
        { id: 2, name: 'jiro' },
        { id: 3, name: 'saburo' }
      ]
    }
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28

これは最初にやったテーブルの例と同じことをvueでやってます。 このコードではtr要素とその中身がpeoplesの長さだけ繰り返されます。先ほどと違ってJavaScriptでforを回さなくても、HTML側にわかりやすく記述できます。 (shiroやgoroも足してあげてみてください)

# 入力を処理する

ユーザーがボタンを押したり、入力欄に何かを入力した時の処理を書いてみましょう。まずはボタンを押した時です。

<div id="app">
  <p>{{ message }}</p>
  <button v-on:click="reverseMessage">Reverse Message</button>
</div>

<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!'
    },
    methods: {
      reverseMessage: function () {
        this.message = this.message.split('').reverse().join('')
      }
    }
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

methodsという新しいプロパティが出てきました。Vue ではこのようにインスタンス(app)内で使用するメソッドを定義できます。

v-onは要素にイベントハンドラを追加するディレクティブです。つまりこのbutton要素をクリックした時にreverseMessageメソッドが呼ばれるように定義しています。reverseMessageが呼ばれると関数がmessage変数を書き換えます。message変数を書き換えると、Vue が勝手に変更を検知してDOMを書き換えてくれます。

ユーザーの入力を変数に反映するにはv-modelディレクティブが便利です。

<div id="app">
  <p>{{ message }}</p>
  <input v-model="message">
</div>

<script type="text/javascript">
  var app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!'
    }
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13

入力欄を変更すると内容がリアルタイムに反映されます。ちなみにHTML => 変数変数 => HTMLの両方の変更が反映される機能を「双方向バインディング」と呼びます。

# サーバからデータを取得する

Vue の使い方がだいたい分かったところで、サーバからデータを取得して画面に反映してみましょう。Vue 自体にはajax通信をサポートする機能は含まれていないため、fetch APIを使ってみましょう。

取得するデータは以下のURLにあるjsonです。

https://raw.githubusercontent.com/iij/bootcamp/master/test.json (opens new window)

<div id="app">
  <div>{{ message }}</div>

  <button v-on:click="loadData">load data</button>

  <table border="1">
    <tr>
      <th>id</th>
      <th>name</th>
    </tr>
    <tr v-for="people in peoples">
      <td>{{ people.id }}</td>
      <td>{{ people.name }}</td>
    </tr>
  </table>
</div>

<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!',
      peoples: []
    },
    methods: {
      loadData: function () {
        const self = this;

        fetch('https://raw.githubusercontent.com/iij/bootcamp/master/test.json')
          .then(res => {
            return res.json();
          })
          .then(data => {
            console.log(data);
            self.peoples = data;
          });
      }
    }
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

これまで出てきた要素が色々使われています。これで無事ボタンを押すとデータをロードして表示するアプリが作れました。

(データを読み込む前からテーブルのヘッダーが表示されているのがカッコ悪いですね。データを読み込むまではヘッダーを表示しないように修正してみてください。)

# コンポーネントを使う

これまで作ってきたVueアプリはごく小さいものであれば十分便利に使えます。しかしある程度の規模のアプリになってくるとコードのメンテナンスが大変になってきます。

そこでVueではコンポーネントと呼ばれる単位でアプリケーションを作れる機能があります。試しに上のコードからテーブル部分を分離するコンポーネントを作ってみましょう。bodyの中身を以下のように変更してください。

<div id="app">
  <div>{{ message }}</div>

  <button v-on:click="loadData">load data</button>

  <people-table v-bind:peoples="peoples"></people-table>
</div>

<script type="text/javascript">
  Vue.component('people-table', {
    props: ['peoples'],
    template: `
<table border="1">
  <tr>
    <th>id</th>
    <th>name</th>
  </tr>
  <tr v-for="people in peoples">
    <td>{{ people.id }}</td>
    <td>{{ people.name }}</td>
  </tr>
</table>
`
  });
</script>

<script type="text/javascript">
  const app = new Vue({
    el: '#app',
    data: {
      message: 'Welcome to iij-bootcamp!!',
      peoples: []
    },
    methods: {
      loadData: function () {
        const self = this;

        fetch('https://raw.githubusercontent.com/iij/bootcamp/master/test.json')
          .then(res => {
            return res.json();
          })
          .then(data => {
            console.log(data);
            self.peoples = data;
          });
      }
    }
  })
</script>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

ここでは1個目のscriptタグでpeople-tableというコンポーネントを定義しています。コンポーネントはtemplateというプロパティがあるのが特徴で、ここにコンポーネント内に表示したいHTMLを記述します。

さらにpropsでこのコンポーネントのインプットを定義しています。今回のpeople-tableは、peoplesという変数で配列を受け取り、それをテーブルとして表示するコンポーネントです。

このようにアプリケーションをコンポーネントと呼ばれる小さな単位に分割していくとで、1つ1つのコンポーネントをシンプルに保ちつつ、アプリケーション全体を構築していくことが可能です。


CC BY-SA Licensed | Copyright (c) 2020, Internet Initiative Japan Inc.