サーバーサイドJavaエンジニアがVue.jsの基本を学習したときのメモ

# 概要

この記事は、私がVue.jsの基本的な仕様や使い方をユーザーガイドとサンプルアプリケーション、Qiitaなどの詳しい解説ページから学んだときのメモです。ふだんはサーバーサイドのWebアプリケーション開発(Java)を行っているので、フロントエンド開発の経験・知識に乏しくNode.jsやES6(ECMAScript2015)については初歩的なことをなんとなく知っている程度です。
そのためVue.jsと直接関係のないところで疑問に思ったり躓いた点がたくさんあり難儀しましたが、そのあたりのことは"(余談)"というかたちで記事に加えています。

学習の流れは

1. vue initコマンドでアプリケーションのひな型を生成し、プロジェクトの構造を理解する
1. 生成されたソースコードを読んで意味を理解する
1. ソースコードに簡単な変更を加えてアプリケーションの動作を確認する

です。

**環境**

* Windows 10 Professional
* Node.js
* Vue.js
* Bootstrap-vue

**参考**

* [Vue.js] (https://jp.vuejs.org/)


# 環境作り

## Node.js

Nodistでインストールしました。

```text
> node -v
v8.11.1
```

```text
> npm -v
4.0.5
```

## Vue.js

```text
> npm install --global vue
```

```text
> npm install --global vue-cli
```

```text
> vue --version
2.9.3
```

<details><summary>(余談 : npm) installのオプション</summary><div>

package.jsonがあるディレクトリで`npm install`を実行すると、package.jsonの`dependencies`、`devDependencies`に定義されている依存モジュールをインストールする。

|long            |short  |description |
|:---------------|:------|:--------------------|
|--global        |-g     |グローバルインストール |
|--save          |       |ローカルインストール<br/> package.jsonの`dependencies`へ記録<br/> アプリケーションの開発、実行時に必要な依存モジュール |
|--save-dev      |-D     |ローカルインストール<br/> package.jsonの`devDependencies`へ記録<br/> アプリケーションの開発時に必要な依存モジュール |

</div></details>

## Bootstrap-vue

https://bootstrap-vue.js.org/

Vue.jsでBootstrapが利用できるようになるプラグインです。
インストールはプロジェクトのひな型を作成したあとに、プロジェクトのディレクトリで行います。

```text
> npm install --save bootstrap-vue
```

## vue-devtools

https://github.com/vuejs/vue-devtools

Vue.jsのデバッグツールです。Chromeの拡張機能なのでウェブストアからインストールします。


# プロジェクトのひな型の作成

モジュールバンドラーにwebpackを利用するプロジェクトのひな型を生成します。
下記のように生成の途中でプロジェクトの設定を選択する場面がありますが、Vue.jsの基本的な使い方を学ぶことが目的なのでVue-routerはインストールし、ESLintとテスト関係のモジュールは今回の学習の範囲外なのでインストールしませんでした。

<details><summary>(余談 : webpack) モジュールバンドラー</summary><div>

モジュールバンドラーは複数のモジュールを単一ファイルにまとめるという機能があります。
有名なものにはwebpack、browserifyなどがあります。この記事ではwebpackを利用していますが、Vue.jsはbrowserifyにも対応しているようです。

```text
> vue init browserify exercise-vue
```

</div>
</details>

```text
> vue init webpack exercise-vue

? Project name exercise-vue
? Project description A Vue.js project
? Author rubytomato <mail address>
? Vue build standalone
? Install vue-router? Yes
? Use ESLint to lint your code? No
? Set up unit tests No
? Setup e2e tests with Nightwatch? No
? Should we run `npm install` for you after the project has been created? (recommended) npm

   vue-cli · Generated "exercise-vue".


# Installing project dependencies ...
# ========================

// 省略...

# Project initialization finished!
# ========================

To get started:

  cd exercise-vue
  npm run dev

Documentation can be found at https://vuejs-templates.github.io/webpack
```

<details><summary>(余談 : npm) モジュールの依存関係を確認する</summary><div>

プロジェクトのモジュールの依存関係はlsコマンドで確認できます。

```text
> npm ls
```

</div></details>

<details><summary>(余談 : npm) node_modules\\.binディレクトリ</summary><div>

開発で使用するローカルコマンド（webpackやwebpack-dev-serverなど）は.binディレクトリに存在します。

```text
> node_modules\.bin\webpack -v
3.11.0
```

```text
> node_modules\.bin\webpack-dev-server -v
webpack-dev-server 2.11.2
webpack 3.11.0
```

binディレクトリの場所を確認するには

```text
> npm bin --save
C:\path\to\exercise-vue\node_modules\.bin
```

グローバルインストールしたbinディレクトリは

```text
> npm bin -g
C:\Program Files (x86)\Nodist\bin
```

</div></details>

## プロジェクトの構造

ひな型を生成した直後のディレクトリ構造とファイルの抜粋です。（ドットファイルなどは省略しています。）

```text
/exercise-vue
  |
  +--- /build
  |      |
  |      +--- build.js
  |      +--- check-versions.js
  |      +--- logo.png
  |      +--- utils.js
  |      +--- vue-loader.conf.js
  |      +--- webpack.base.conf.js
  |      +--- webpack.dev.conf.js
  |      +--- webpack.prod.conf.js
  |
  +--- /config
  |      |
  |      +--- dev.env.js
  |      +--- index.js
  |      +--- prod.env.js
  |
  +--- /node_modules
  |      |
  |      +--- /依存モジュール
  |
  +--- /src
  |
  +--- /static
  |
  +--- index.html
  |
  +--- package.json
```

<details><summary>(余談 : webpack) staticディレクトリ</summary><div>

ファイルをstaticディレクトリに配置するとビルド時にdist/staticディレクトリへコピーされます。
なお、アプリケーションで使用する静的（アセット）ファイルは、通常はsrc/assetsディレクトリに配置するようです。

</div></details>

## アプリケーションの実行

生成したアプリケーションのひな型をそのまま動かしてみます。

### 開発環境

開発環境ではwebpack-dev-serverを起動してアプリケーションを実行します。

```text
> npm run dev
```

or

```text
> npm start
```

アプリケーションが起動すると、コンソールに以下のようにURLが表示されます。このURLにアクセスすると下の画面が表示されます。

```text
DONE  Compiled successfully in 4623ms                                                                                                                                                          

I  Your application is running here: http://localhost:8080
```

ちなみに画面右側に表示されているのはインストールしたvue-devtoolsの画面です。ここでコンポーネントが持つデータを書き換えたり、コンポーネントの階層を確認できます。

![v1.png](https://qiita-image-store.s3.amazonaws.com/0/22772/c5ff3140-afaa-af23-689e-621afa3b966d.png)

アプリケーションの実行中にソースコードを変更するとページが自動的にリフレッシュされて変更内容が反映されます。(vue-devtoolsがなくても)
たとえば、HelloWorld.vueファイルのmsgプロパティを"Welcome to Your Vue.js App"から"Welcome to Exercise Vue"に変更して保存すると、ほぼ同じタイミングでブラウザ上の文字列が変わります。

```vue:HelloWorld.vue
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'  // → 'Welcome to Exercise Vue'に変える
    }
  }
}
</script>
```

* わかりにくいですが、文字列を変えた瞬間のgifアニメです。

![v2.gif](https://qiita-image-store.s3.amazonaws.com/0/22772/5f901ab6-dcc0-1445-94a4-458158f28770.gif)

### 本番環境

本番環境向けにビルドしたアプリケーションを実行するにはdistディレクトリに出力されたファイルを使用します。

```text
> npm run build

Hash: adba64b01b402cbc0207
Version: webpack 3.11.0
Time: 9679ms
                                                  Asset       Size  Chunks             Chunk Names
               static/js/vendor.7fed9fa7b7ba482410b7.js     112 kB       0  [emitted]  vendor
                  static/js/app.f16ac5c624284d30f5af.js    11.6 kB       1  [emitted]  app
             static/js/manifest.2ae2e69a05c33dfc65f8.js  857 bytes       2  [emitted]  manifest
    static/css/app.cca059254702f9ed953b7df749673cf4.css  432 bytes       1  [emitted]  app
static/css/app.cca059254702f9ed953b7df749673cf4.css.map  828 bytes          [emitted]
           static/js/vendor.7fed9fa7b7ba482410b7.js.map     553 kB       0  [emitted]  vendor
              static/js/app.f16ac5c624284d30f5af.js.map    22.2 kB       1  [emitted]  app
         static/js/manifest.2ae2e69a05c33dfc65f8.js.map    4.97 kB       2  [emitted]  manifest
                                             index.html  514 bytes          [emitted]

  Build complete.

  Tip: built files are meant to be served over an HTTP server.
  Opening index.html over file:// won't work.
```

#### distディレクトリ

ビルドするとdistディレクトリが作成され、バンドル結果が出力されます。
htmlファイル以外のファイルにはファイル名の一部にハッシュ値が付加されています。

```text
/dist
  |
  +--- /static
  |      |
  |      +--- /css
  |      |      |
  |      |      +--- app.cca059254702f9ed953b7df749673cf4.css
  |      |      +--- app.cca059254702f9ed953b7df749673cf4.css.map
  |      |
  |      +--- /js
  |             |
  |             +--- app.f16ac5c624284d30f5af.js
  |             +--- app.f16ac5c624284d30f5af.js.map
  |             +--- manifest.2ae2e69a05c33dfc65f8.js
  |             +--- manifest.2ae2e69a05c33dfc65f8.js.map
  |             +--- vendor.7fed9fa7b7ba482410b7.js
  |             +--- vendor.7fed9fa7b7ba482410b7.js.map
  |
  +--- index.html
```

<details><summary>(余談 : webpack) ファイルのハッシュ値</summary><div>

ブラウザで静的ファイルがキャッシュされている場合に備えて、ファイルを更新したら強制的に新しいファイルを使用するようにするための工夫です。
ハッシュ値はファイルの内容から計算されるのでファイルの内容が変わらなければハッシュ値も変わりません。

</div>
</details>

#### Web Server for Chromeで実行する

Web Server for Chromeという拡張機能を使うとdistディレクトリに出力されたファイルで動作確認ができます。

![w.png](https://qiita-image-store.s3.amazonaws.com/0/22772/2a2091d1-aa8c-7e6f-4c74-350e273bc06e.png)

#### nginxで実行する

distディレクトリに出力されたファイル(index.htmlとstaticディレクトリ)をnginxのドキュメントルートへコピーします。
あとはnginxを起動して、以下のURLにアクセスするとアプリケーションの実行を確認できます。

```
http://localhost
```

<details><summary>(余談 : webpack) assets/logo.pngの行方</summary><div>

画面にVueのロゴ画像が表示されていますがビルド先のdistディレクトリ下には出力されていません。
ブラウザ上でロゴ画像のsrcを調べると以下のようにBase64エンコードされていました。

```html
<img src="data:image/png;base64,iVBORw0KGgoAAAA ... SuQmCC">
```

この挙動はwebpackの設定で画像のバンドル処理にurl-loaderが使われているためです。
この設定はwebpack.base.conf.jsにあり、下記のようにファイルサイズがlimitで指定したサイズより小さい場合にBase64エンコードされるようです。
画像ファイルをファイルサイズに関わらずBase64エンコードしないようにするには、limitに-1を指定する、またはローダーをfile-loaderに変えるなどの方法があります。

```js:webpack.base.conf.js
{
  test: /\.(png|jpe?g|gif|svg)(\?.*)?$/,
  loader: 'url-loader',
  options: {
    limit: 10000,
    name: utils.assetsPath('img/[name].[hash:7].[ext]')
  }
}
```

Base64エンコードをしないようにするとlogo.pngは、dist/static/imgへコピーされます。
またsrcもwebpackが適切に書き換えてくれます。

```html
<img src="/static/img/logo.82b9c7a.png">
```

</div></details>


## ソースコードを読む

### buildディレクトリ

buildディレクトリには、build.jsやwebpack.base.conf.jsなどのwebpackが利用するjsが格納されています。
バンドルのルールや設定などが記述されているようですが、詳しくはわかりません。

### configディレクトリ

configディレクトリには、index.jsの他に`NODE_ENV`を設定するprod.env.jsとdev.env.jsが格納されています。
環境別に処理するためのconfigファイルだと思いますが、こちらも詳しくはわかりません。

### srcディレクトリ

```text
/src
  |
  +--- /assets
  |      |
  |      +--- logo.png
  |
  +--- /components
  |      |
  |      +--- HelloWorld.vue
  |
  +--- /router
  |      |
  |      +--- index.js
  |
  +--- App.vue
  +--- main.js

index.html
```

#### main.js

アプリケーションのエントリポイントになるファイルです。
ここで生成しているVueインスタンスを”ルートVueインスタンス (root Vue instance)”と呼びます。
このインスタンス生成時のオプションに、このアプリケーションを構成するAppというコンポーネントと画面遷移を制御するルーターを設定しています。

オプションのelに指定しているのはこのインスタンスが管理するDOMセレクタです。このアプリケーションではindex.htmlのdiv#appになります。

```js:main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
```

> productionTip
> これをfalseに設定すると、Vueの起動時のプロダクションのヒントが表示されなくなります。

<details><summary>(余談 : ES6) importのfromにディレクトリを指定する</summary><div>

import/exportはES6で定義されているモジュールを扱う仕組みです。
fromにディレクトリを指定するとそのディレクトリのindex.jsが評価されます。

```js
import router from './router'
```

なお、require/exportsはNode.jsのモジュールを扱う仕組みです。

Vue.jsのソースコードではimport/exportが使用されており、npmでインストールしたモジュールが使用するコード（たとえば、webpackが使用するbuild.jsやwebpack.base.conf.js）ではrequire/exportsが使用されているようです。

</div></details>

<details><summary>(余談 : webpack) importのfromにファイルを指定する</summary><div>

このimportはApp.vueファイルを対象としていますが、拡張子を省略した場合の拡張子の解決はwebpackのresolve.extensionsの設定が関係します。

```js
import App from './App'
```

webpack.base.conf.jsの設定は以下のようになっているので、拡張子を省略した場合は.js、.vue、.jsonから解決されます。

```text:webpack.base.conf.js
resolve: {
  extensions: ['.js', '.vue', '.json'],
  // ...省略...
}
```

</div></details>

<details><summary>(余談 : ESLint) eslint-disableというコメント行</summary><div>

Vueインスタンスを生成している行の上にある`/* eslint-disable no-new */`というコメントは、ESLintのルールを部分的に無効（または有効）にするためのコメントです。
このようにESLintにはコメントによってルールを無効／有効にする機能があり、このno-newというルールは生成したオブジェクトを変数に保存しないコードをエラーにします。

* [Disallow new For Side Effects (no-new)] (https://eslint.org/docs/rules/no-new)

</div></details>

#### App.vue

拡張子がvueというファイルは単一ファイルコンポーネント(Single file component)と呼ばれ、このファイルにはコンポーネントの見た目や振る舞いを定義するtemplate、script、styleという要素があります。
このコンポーネントはAppという名前が付けられ、このアプリケーションの一番外側のコンポーネントになります。
templateの\<router-view\>の位置に、ルーターで定義したコンポーネントが差し込まれます。

```vue:App.vue
<template>
  <div id="app">
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'App'
}
</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>
```

![v3.png](https://qiita-image-store.s3.amazonaws.com/0/22772/6c8faa13-8782-7f77-3667-e747a53d18d1.png)

#### components/HelloWorld.vue

コンポーネントはページだったりページの部品(ヘッダーやフッター、フォームなど)だったりと規模はさまざまあるようです。
このHelloWorldコンポーネントがいつどこで使われるかは、次で説明するルーターで定義されています。

```vue:HelloWorld.vue
<template>
  <div class="hello">
    <h1>{{ msg }}</h1>
    <h2>Essential Links</h2>
    <ul>
      <li>
        <a
          href="https://vuejs.org"
          target="_blank"
        >
          Core Docs
        </a>
      </li>
      <li>
        <a
          href="https://forum.vuejs.org"
          target="_blank"
        >
          Forum
        </a>
      </li>

      //... 省略 

    </ul>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>

<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
h1, h2 {
  font-weight: normal;
}
ul {
  list-style-type: none;
  padding: 0;
}
li {
  display: inline-block;
  margin: 0 10px;
}
a {
  color: #42b983;
}
</style>
```

##### templateタグ

コンポーネントのビュー(html)を記述します。
ビュー内で`{{ プロパティ名 }}`と記述すると、scriptで定義したプロパティの値を表示します。

この例ではh1タグに表示する文字列にmsgプロパティを使用しています。

```html
<h1>{{ msg }}</h1>
```

**スタイルガイドについて**

JavaScriptにはコーディングルールを決めたスタイルガイドというものがあり、有名なものには

* [Google JavaScript Style Guide] (https://google.github.io/styleguide/jsguide.html)
* [Airbnb JavaScript Style Guide] (https://github.com/airbnb/javascript)
* [JavaScript Standard Style] (https://standardjs.com/)

などがあります。

Vue.jsにもVue.js固有のコーディングについて[公式のスタイルガイド] (https://jp.vuejs.org/v2/style-guide/)があり、下記のhtmlの記述の仕方は”複数の属性をもつ要素”というスタイルガイドに則ったものになります。

> 複数の属性をもつ要素は、1 行に 1 要素ずつ、複数の行にわたって書くべきです。

```html
<a
  href="https://vuejs.org"
  target="_blank"
>
  Core Docs
</a>
```

<details><summary>(余談 : html5) templateタグとWeb Components</summary><div>

templateはHTML5より実装された標準のタグです（[\<template\>: コンテンツテンプレート要素] (https://developer.mozilla.org/ja/docs/Web/HTML/Element/template)）。
そしてtemplateで定義した要素を、開発者が名前付けしたタグ（カスタムタグ）に差し込むといった使い方は、[Web Components] (http://w3c.github.io/webcomponents/explainer/)で決められているAPIの仕様に似ています。

このように、Vue.jsはなるべくWeb標準の仕様に準拠するように設計されています。

</div></details>


##### scriptタグ

このコンポーネントの振る舞いを記述します。
dataプロパティは関数でなければなりません。また、この関数が返すデータオブジェクトはインスタンスが監視していて変更はビューへ通知されます。
たとえば、このデータオブジェクトのmsgプロパティを書き換えると、このプロパティを使用している\<h1\>のテキストが連動して変わります。

```vue
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  }
}
</script>
```

<details><summary>(余談 : ES6) export default</summary><div>

import/exportはES6で定義されているモジュールを扱う仕組みです。
exportの方法には名前付きエクスポートとデフォルトエクスポートがあります。Vue.jsのコンポーネントではデフォルトエクスポートを使用するようです。

* [export] (https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/export)

**名前付きエクスポート**

> 名前付きエクスポートは、さまざまな値をエクスポートするのに役立ちます。インポートするときに、同じ名前で対応する値を参照できます。

**デフォルトエクスポート**

> デフォルトエクスポートについては、モジュールごとにひとつだけです。関数、クラス、オブジェクトなどをデフォルトエクスポートすることができます。

</div></details>

<details><summary>(余談 : ES6) 見慣れないdata () { ... }という記法</summary><div>

このdataプロパティの定義の仕方は、ES6で導入されたメソッド定義です。

```vue
{
  name: 'HelloWorld',
  data () {
    //...
  }
}
```

> [メソッド定義] (https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Functions_and_function_scope/Method_definitions)
> ECMAScript 2015 (ES6) より、オブジェクトイニシャライザのメソッド定義のための短い構文が導入されています。これは、メソッドの名前に割り当てられた関数の省略形です。

ES6以前は次のように記述していたと思います。

```vue
{
  name: 'HelloWorld',
  data: function () {
    //...
  }
}
```

</div></details>

###### その他のプロパティ

dataプロパティの他にもいろいろなプロパティがあります。以下はその一部です。

```vue
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  // 算出プロパティ
  computed: {
    hoge: function () {
      // 何らかの処理
      // thisはこのインスタンスを指す
    }
  },
  // メソッド
  methods: {
    fuga: function () {
      // 何らかの処理
      // thisはこのインスタンスを指す
    }
  },
  // 監視プロパティ
  watch: {
    msg: function (val, oldVal) {
      // msgプロパティを監視する処理
    }
  }
}
</script>
```

* 算出プロパティ (computed)
    * 算出プロパティは依存関係にもとづきキャッシュされる
* メソッド (methods)
* 監視プロパティ (watch)
    * 監視するプロパティが変更されたときに呼び出されます。

###### インスタンスライフサイクルフック

コンポーネントにはライフサイクルイベントがあり、イベントに対応したフック処理を実装することができます。

フック処理の一部

```vue
<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  created: function () {
    // インスタンスが作成されたときに行う何かの処理
  },
  mounted: function () {
    // インスタンスがマウントされたあとに行う何かの処理
  },
  updated: function () {
    // データが更新されDOMが再描画されたときに行う何かの処理
  },
  destroyed: function () {
    // インスタンスが破棄されたあとに行う何かの処理
  }
}
</script>
```

##### styleタグ

template要素で記述したビュー(html)のスタイル(CSS)を記述します。
style要素にscoped属性を付けるとこのコンポーネント固有のスタイルになります。

#### router/index.js

index.jsにはルーターの設定が記述されています。
この例では`/`に上記のHelloWorldコンポーネントを割り当てているので、`localhost:8080`にアクセスすると、App.vueの\<router-view\>の位置にHelloWorldコンポーネントが表示されます。

```js:index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'

Vue.use(Router)

export default new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld
    }
  ]
})
```

![v4.png](https://qiita-image-store.s3.amazonaws.com/0/22772/32578fce-108a-fd77-f67a-7d1ba2abdd03.png)

#### index.html

このファイルはdistディレクトリに出力されるindex.htmlのテンプレートのような役割を担っています。
このファイルに行う変更はそのままビルドに反映されるので、metaタグなどをカスタマイズしたい場合はこのファイルを修正すればよいようです。

```html
<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0">
    <title>exercise-vue</title>
  </head>
  <body>
    <div id="app"></div>
    <!-- built files will be auto injected -->
  </body>
</html>
```

アプリケーションの一番外側のAppコンポーネントはこのファイルの`div#app`に差し込まれます。
その処理はmain.jsのVueのインスタンスを生成するところで行われています。

```js:main.js
/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
```

# 子コンポーネントを追加する変更

CommonHeaderというコンポーネントを作成し、Appコンポーネントに差し込む変更を行います。

## CommonHeaderコンポーネントを作成

下記のビューしか持たない簡単なコンポーネントをcomponents/CommonHeader.vueに作成しました。

```vue:CommonHeader.vue
<template>
  <header id="header">
    <h1>common header</h1>
  </header>
</template>
```

## Appコンポーネントに組み込み

CommonHeaderをインポートしcommonHeaderコンポーネントを表示したい場所にタグを記述します。
コンポーネント名はキャメルケースですが、タグはケバブケース(単語をハイフンで区切る表記)になっています。

```vue:App.vue
<template>
  <div id="app">
    <!-- ここにヘッダーを表示する -->
    <common-header />
    <img src="./assets/logo.png">
    <router-view/>
  </div>
</template>

<script>
// コンポーネントを利用できるようにインポートする
import commonHeader from '@/components/CommonHeader'

export default {
  name: 'App',
  components : {
    'commonHeader': commonHeader
  }
}
</script>
```

アプリケーションを起動しブラウザでページを開いた状態で改修すると、変更がほぼリアルタイムに反映されます。

![v5.png](https://qiita-image-store.s3.amazonaws.com/0/22772/b2c2a374-ff6a-5b95-1ebd-62985a1ede36.png)

# ページを追加する変更

新しいページを追加し画面遷移ができるようにルーターを修正します。
また画面のデザイン（の一部）にBootstrapを利用します。

## Bootstrapを有効にする

インストールしたBootstrap-vueプラグインを有効にするため、main.jsを次のように修正します。

```js:main.js
// The Vue build version to load with the `import` command
// (runtime-only or standalone) has been set in webpack.base.conf with an alias.
import Vue from 'vue'
import App from './App'
import router from './router'

// add
import BootstrapVue from 'bootstrap-vue'
import 'bootstrap/dist/css/bootstrap.css'
import 'bootstrap-vue/dist/bootstrap-vue.css'
Vue.use(BootstrapVue)
// add

Vue.config.productionTip = false

/* eslint-disable no-new */
new Vue({
  el: '#app',
  router,
  components: { App },
  template: '<App/>'
})
```

## メモリスト

メモリストはメモを一覧表示するコンポーネントです。components/MemoList.vueを以下のように作成しました。
memosがメモデータを保持するプロパティですが、データはAPIサーバーから取得するという想定なので初期化処理はcreatedというフック処理で行っています。

```vue:MemoList.vue
<template>
  <div class="memo">
    <h1>{{ title }}</h1>
    <div>
      <b-table striped hover :items="memos" :fields="fields" />
    </div>
  </div>
</template>

<script>
export default {
  name: 'MemoList',
  data () {
    return {
      title: 'Memo List',
      fields: [ 'id', 'title', 'description', 'done' ],
      memos: [ ]
    }
  },
  created: function() {
    this.memos = [
      {id: 1, title: 'memo1', description: 'desc1', done: true},
      {id: 2, title: 'memo2', description: 'desc2', done: false},
      {id: 3, title: 'memo3', description: 'desc3', done: false},
      {id: 4, title: 'memo4', description: 'desc4', done: false}
    ]
  }
}
</script>
```

メモデータの一覧（テーブル）表示はBootstrap-vueというプラグインを利用しているため、下記の1行で済んでいます。

```html
<b-table striped hover :items="memos" :fields="fields" />
```

## ルーターを修正する

router/index.jsを修正してメモリストへ遷移（正確には遷移ではなくコンポーネントの切り替えだとおもいました）できるようにします。
router.afterEachはページ事にタイトルを変えるようにするための処理です。このあたりの処理は[かゆいところに手が届くvue-routerの機能] (https://qiita.com/noplan1989/items/9ce7d6a04ffb82755ae1)という記事を参考にさせて頂きました。

```js:index.js
import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import MemoList from '@/components/MemoList'

Vue.use(Router)

const router = new Router({
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld,
      meta: {
        title: 'hello world'
      }
    },
    {
      path: '/memo',
      name: 'MemoList',
      component: MemoList,
      meta: {
        title: 'memo list'
      }
    }
  ]
})

router.afterEach((to, from) => {
  if (to.meta && to.meta.title) {
    document.title = to.meta.title
  }
})

export default router
```

<details><summary>(余談 : webpack) @で始まるimportのpath</summary><div>

[ES6 import using at ('@') sign in path in a vue.js project using Webpack] (https://stackoverflow.com/questions/42749973/es6-import-using-at-sign-in-path-in-a-vue-js-project-using-webpack)

この例のようにpathが@で始まる記述がありました。

```js
import HelloWorld from '@/components/HelloWorld'
```

この記法はVue.js固有のものではなく、webpackのものでビルドすると@がsrcに置換されるようになっています。
webpack.base.conf.jsのresolveにその設定が記述されています。

```js
resolve: {
  extensions: ['.js', '.vue', '.json'],
  alias: {
    'vue$': 'vue/dist/vue.esm.js',
    '@': resolve('src'),
  }
}
```

</div></details>

## CommonHeaderにリンクを追加

リンクは\<router-link\>で記述します。\<a\>では正しくリンクが作成されませんでした。
また、この例の実装に特に意味はないですがビュー内から$routeでルーターの情報を参照することができます。

```vue:CommonHeader.vue
<template>
  <header id="header">
    <h1>common header</h1>
    <div>
      <router-link to="/" class="badge badge-primary">Go to Home</router-link>
      <router-link to="/memo" class="badge badge-primary">Go to Memo</router-link>
    </div>
    <p>{{ $route.name }} : {{ $route.path }}</p>
  </header>
</template>
```

![v6.gif](https://qiita-image-store.s3.amazonaws.com/0/22772/e221c591-fb46-a91c-7e21-280b0c50fe51.gif)

ちなみに、scriptで$routeを参照するには以下のようにthisで修飾する必要があります。

```vue
<script>
export default {
  name: 'CommonHader',
  computed: {
    getRouteInfo () {
      return `${this.$route.name} : ${this.$route.path}`
    }
  }
}
</script>
```

<details><summary>(余談 : ES6) \`....\`(バッククォート)で囲んだ文字列</summary><div>

ES6で標準化されたテンプレート文字列(Template literal)という機能です。(ES6以前でも使えた環境はあったようです。）
テンプレート文字列では複数行の文字列を扱ったり、`${}`で表現するプレースフォルダで文字列に式を埋め込むことができます。

```js:例
`1 + 1 = ${1+1}`
// 1 + 1 = 2

var a = "aaa"
`${a.toUpperCase()}`
// AAA
```

</div>
</details>

# 子コンポーネントへデータを伝達する変更

親コンポーネントのデータを子コンポーネントへ伝達する変更を行います。
メモリストのテーブル部分を子コンポーネント化して、親コンポーネントからメモデータを渡して表示するようにします。

## テーブルを表示するコンポーネントを作成

以下の内容でcomponents/MemoTable.vueを作成しました。
親コンポーネントから受け取るデータはpropsプロパティに定義します。

```vue:MemoTable.vue
<template>
  <div class="memo-table">
    <b-table striped hover :items="items" :fields="fields" />
  </div>
</template>

<script>
export default {
  name: 'MemoTable',
  props: ['items', 'fields']
}
</script>
```

propsプロパティの定義の仕方は、以下のようにtypeやrequiredを指定してより厳密にする方法もあります。

```vue
props: {
  items: {
    type: Array,
    required: true
  },
  fields: {
    type: Array,
    required: true
  }
}
```

typeに指定できるデータタイプは以下のものがあります。

* String
* Number
* Boolean
* Function
* Object
* Array
* Symbol

## 親コンポーネントの修正

データを渡す側のMemoList.vueの修正は以下のようになりました。

```vue:MemoList.vue
<template>
  <div class="memo">
    <h1>{{ title }}</h1>
    <memo-table v-bind:items="memos" v-bind:fields="fields" />
  </div>
</template>

<script>
import memoTable from '@/components/MemoTable'

export default {
  name: 'MemoList',
  components : {
    'memoTable': memoTable
  },
  data () {
    return {
      title: 'Memo List',
      fields: [ 'id', 'title', 'description', 'done' ],
      memos: [ ]
    }
  },
  created: function() {
    this.memos = [
      {id: 1, title: 'memo1', description: 'desc1', done: true},
      {id: 2, title: 'memo2', description: 'desc2', done: false},
      {id: 3, title: 'memo3', description: 'desc3', done: false},
      {id: 4, title: 'memo4', description: 'desc4', done: false}
    ]
  }
}
</script>
```

データを渡す処理はmemoTableコンポーネントのv-bindで記述します。
`v-bind:items`のitemsは子コンポーネントのpropsプロパティで定義した名前です。そして"memos"という一見文字列を渡しているような部分が子コンポーネントに渡すプロパティの名前です。

```vue
<memo-table v-bind:items="memos" v-bind:fields="fields" />
```

v-bindは省略できるので以下のようにも記述できます。

```vue
<memo-table :items="memos" :fields="fields" />
```

ちなみに文字列リテラルを渡す場合はv-bindは使わず以下のように記述します。v-bindを使うとダブルクォーテーション内の文字列がプロパティ名として扱われてしまいます。

```vue
<child-component message="なんらかのメッセージ" />
```

![v7.png](https://qiita-image-store.s3.amazonaws.com/0/22772/2784a785-928b-b645-04a1-da9755509c58.png)

# 補足

## Vue.jsのキャッチアップについて

[Vue.jsのキャッチアップ] (https://qiita.com/rubytomato@github/private/8924d6f590adc410bd21)にまとめています。

## 学習の成果

1. [Vue.jsで簡単なメモアプリを実装する] (https://qiita.com/rubytomato@github/items/294ca52abc2be4ae6131)
2. [Cloud Firestore with Vue.jsで簡単なメモアプリを実装する] (https://qiita.com/rubytomato@github/items/78087a2c69389f642760)
3. [アイテム一覧へアイテムを追加したときにシンプルなアニメーションを適用する] (https://qiita.com/rubytomato@github/items/9d627b16828be8f62915)

## Nodistの使い方

[marcelklehr/nodist] (https://github.com/marcelklehr/nodist)

インストール可能なNode.jsのバージョンを確認

```
> nodist dist
```

Node.jsのインストール

```
> nodist + <version>
or
> nodist add <version>
```

ローカルにインストールされているNode.jsの確認

```
> nodist list
```

Node.jsのアンインストール

```
> nodist - <version>
or
> nodist rm <version>
```

利用するNode.jsのバージョンを指定

```
> nodist <version>
or
> nodist global <version>
```

ローカルで利用するNode.jsのバージョンを指定

```
> nodist local <version>
```

ローカルにインストールされているnpmの確認

```
> nodist npm
```

利用するnpmのバージョンを指定

```
> nodist npm <version>
```

現在のNode.jsがバンドルするnpmを指定

```
> nodist npm match
```

## [WIP] フロントエンド開発の技術スタック(Front-End Development Technology Stack)のキャッチアップについて

更新中です。まずアウトラインを書き出した後にサマリを追記していく予定です。

* 多数のライブラリ、フレームワーク名を記載することになり、Google検索などのノイズになりかねないので、別の限定共有公開ページへ切り出しました。

[フロントエンド開発の技術スタックのキャッチアップ] (https://qiita.com/rubytomato@github/private/5eea9500d91cef7da0c2)