Vue.jsチートシート(基礎編)

公式チュートリアルからの抜粋になります。
Vueに入門したての人(私)が開発するためのカンペなので、これからVueを学習したい方は公式のほうを読まれることをおすすめします。

つづき: [Vue.jsチートシート(コンポーネントと構成編)](https://qiita.com/morrr/items/2642135a927d6a3e7584)


参照
------------------------------------------------------------
- [Vue.js公式](https://jp.vuejs.org/)
    - [ガイド](https://jp.vuejs.org/v2/guide/)
    - [APIリファレンス](https://jp.vuejs.org/v2/api/)

概要
------------------------------------------------------------
- Vue = Webフロントエンドフレームワーク
    - Progressive Framework = 非モノリシックな作りで少しづつ作っていけるようになっている
    - (MVVMでいうところの)View部分を中心にサポートする
    - 開発コミュニティは中国語圏


環境構築
------------------------------------------------------------
### インストール
CDNで直接利用するか、npmによりインストールできる。

- CDN:

    ```html
    <!-- dev -->
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    
    <!-- prod -->
    <script src="https://cdn.jsdelivr.net/npm/vue"></script>
    ```

- npm

    ```sh
    $ npm install vue
    ```


### ビルド
- Vueのnpmパッケージは、用途別の複数のビルドを含む

    ```sh
    $ ls node_modules/vue/dist/
    ./                          vue.common.dev.js           vue.esm.browser.js          vue.js                      vue.runtime.common.js       vue.runtime.js
    ../                         vue.common.js               vue.esm.browser.min.js      vue.min.js                  vue.runtime.common.prod.js  vue.runtime.min.js
    README.md                   vue.common.prod.js          vue.esm.js                  vue.runtime.common.dev.js   vue.runtime.esm.js
    ```

- 利用するバンドルツールと用途により利用するファイルが違ってくる
    - UMD(ブラウザが直接利用できる), CommonJS, ESM(odule)
    - コンパイラ, ランタイム, 完全(両方)
    - 開発モード, 本番モード
        - UMDの場合はそれぞれ別ファイル
        - CommonJS/ESMでは設定により切り替わる(`process.env.NODE_ENV`)

### はろわ

```html:somestaticfile.html
<div id="app">
{{ message }}
</div>

<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
<script>
var app = new Vue({
  el: '#app',
  data: {
    message: 'Hello world'
  }
})
</script>
```

Vueインスタンス
------------------------------------------------------------
### 基礎
- Vueアプリケーションは `Vue` クラスのinstanciateにより起動する
    - MVVMでいうところのView Modelに相当する(Vue自体はMVVMではない)
    - ルートとなるVueインスタンスに対してツリー状にコンポーネントが連なるかたちでアプリケーションが構成される
    - コンポーネントもまたVueインスタンスである
    - Vueインスタンスはイニシャライザの引数としてオプションをとる

#### data: リアクティブなデータ
- `data` オプションのプロパティはVueインスタンス自体のプロパティとしても利用できるようになる

    ```js
    var data = { foo: 'bar' }
    var vm = new Vue({data: data})
    vm.foo == data.foo      // true. データの更新時にも両者は同期される
    ```

- `data` に設定されたオブジェクトは `リアクティブ` な状態となる
    - つまり `data` が更新されるとビュー(DOM)の再描画がトリガされる

    ```js
    var vm = new Vue({data: {}})
    vm.some = 'foo'     // 最初に渡されたプロパティ以外をあとで追加しても再描画は発生しない
    ```

#### $: ビルトインプロパティ
- Vueインスタンスは便利なプロパティ/メソッドを備えている
    - ビルトインのプロパティは接頭辞 `$` を持つ

    ```js
    var data = { a: 1 }
    var vm = new Vue({
      el: '#app',
      data: {
        foo: 'bar',
      }
    })

    vm.$data === data                           // true
    vm.$el === document.getElementById('app')   // true

    vm.$watch('foo', function(newValue, oldValue){
        // data.fooの変更時のイベントハンドラ
    })
    ```

#### ライフサイクル
- Vueアプリケーションのライフサイクルに対応するイベントを設定できる

    ```js
    var vm = new Vue({
      data: {
      },
      created: function(){
        // ...
      },
      updated: function(){
        // ...
      },
    })
    ```

    - イベントは `beforeCreate→created→beforeMount→mounted→beforeupdate→updated→beforeDestroy→destroyed` の順番

- ライフルサイクルイベント内の `this` はVueインスタンスを指す
    - アロー関数(`()=>`)を使ってしまうとthisを使えないので `function(){}` が推奨される

#### {}: Mustache

- `{{ }}` (Mustache) によりバインドしたdataプロパティを展開できる

    ```html
    <p>{{ message }}</p>
    ```

- `v-once` を指定すると描画は一度だけ行われる

    ```html
    <!-- data.messageが更新されても再描画されない -->
    <p v-once>{{ message }}</p>
    ```

- `v-html` を使うとHTML要素を含むテキストを解釈したうえで表示させられる

    ```html
    <!-- data.htmltextがタグを含んでいても正しく解釈される -->
    <p v-html="htmltext'></p>

    <!-- MustacheだとHTMLタグがそのまま(エスケープされて)表示される -->
    <p>{{ htmltext }}</p>
    ```

- Mustacheはinner html以外で使うことができない

    ```html
    <!-- こういう書き方はダメ -->
    <p {{ attributes }}>Message</p>

    <!-- 代わりにv-bindを使う -->
    <p v-bind:attr="value">Message</p>

    ```

- MustacheはJavascriptの式を評価できる

    ```html
    <p>{{ foo + '/' + bar }}</p>
    ```

#### v-\*: ディレクティブ
- ディレクティブ = `v-*` の形式のHTML属性
    - 属性値はjsの式になる
    - 属性値が変化するたびに、その変更をDOMに反映させることができる

    例 | - | -
    --- | --- | ---
    `v-if` | `v-if="true"` | 値の真偽に応じて表示の可否を切り替え
    `v-for` | `v-for="item in list"` | 値の数だけDOMを複製して表示する
    `v-bind:ATTR` | `v-bind:href="url"` | 値の変更時にHTML属性に適用する


- 動的引数 = バインドする属性を動的に選択できる
    - `v-bind:[attr]` とすると動的なHTML属性に対して値をバインドする

- 修飾子 = ディテクティブの特殊なバインドの指定
    - `v-on:submit.prevent` とするとsubmitイベントがトリガされたときに `event.preventDefault()` が呼ばれる

### computed: 算出プロパティ
- 算出プロパティ = テンプレートに反映する値を算出するメソッド(を格納したVueインスタンスのプロパティ)
    - テンプレートに複雑な式を直接書いてしまうことを避けるために使う

- 算出プロパティはVueインスタンスの `computed` プロパティ以下に含める:

    ```js
    var vm = new Vue({
      el: '#app',
      data: {
        message: 'Foo!'
      }
      computed: {
        reverseMessage: function(){
          return this.message.split('').reverse().join('')
        },
      }
    })
    ```
    
    ```html
    <p>{{ message }}</p>
    <p>{{ reverseMessage }}</p>
    ```

- 算出プロパティは、依存するdataプロパティに連動してデータバインドされる
    - 算出プロパティをバインドすると、その中で使っているdataプロパティが変わった場合に表示も自動更新される

- 算出プロパティは、その中で使っているdataプロパティが変更されない限り、呼び出しても再評価されない
    - 算出プロパティがdataを含まない場合、そのプロパティは一度しか評価されない

- 算出プロパティはデフォルトではgetterだが、setterも定義できる:

    ```js
    var vm = new Vue({
      data: {
        foo: 'bar',
      },
      computed: {
        fooLarge: {
          get: function(){
            return this.foo.toUpperCase()
          },
          set: function(newValue){
            this.foo = newValue.toLowerCase()
          },
        },
      },
    })
    ```

### watch: 監視プロパティ
- 監視プロパティ = Vueインスタンスの変更に反応するイベントを定義できる

```js
// fooが更新されたらfooLargeも更新する
// 算出プロパティでも同機能が実現できる
// (この場合は算出のほうが宣言的になるので望ましい)
var vm = new Vue({
  data: {
    foo: 'bar',
    fooLarge: 'BAR',
  },
  watch: {
    foo: {
      fooLarge = this.foo.toUpperCase()
    }
  }
})
```

- 変更をトリガにして非同期的に高コストな処理を実装したい場合に使う


### methods: メソッド
- Vueインスタンス内に任意のメソッドを定義できる

```js
var vm = new Vue({
  methods: {
    foo_method: function(){
      return 'foo'
    }
  }
})
```

```html
<p>{{ foo_method() }}</p>
```

- 算出プロパティと異なり、メソッドは呼び出すたびに再評価される


### 省略記法

### :/@: v-bind/v-onの省略
- `v-bind` と `v-on` は頻出するため省略できる
    - `v-bind:href` → `:href`
    - `v-bind:[key]` → `:[key]`
    - `v-on:click` → `@click`
    - `v-on:[event]` → `@[event]`


#### classのバインド
- htmlのclassやstyle属性は `v-bind` によってバインドできるが、属性値が複雑な場合もある
    - `v-bind` はこれらの属性をバインドする特殊な書き方を提供している

- オブジェクトを渡すことで、そのキーをクラスとして指定できる

    ```js
    var vm = new Vue({
      data: {
        isFoo: false,
        isFoo: true,
      }
    })
    ```

    ```html
    <!-- <div class="bar baz"> となる -->
    <div v-bind:class="{ foo: isFoo, bar: isBar}" class="baz"></div>
    ```

- オブジェクトごとバインドしたり、算出プロパティを指定することもできる

    ```js
    var vm = new Vue({
      computed: {
        someClasses: function(){
          return {
            foo: true,
            bar: false,
          }
        },
      },
    })
    ```

    ```html
    <!-- <div class="foo"> となる -->
    <div v-bind:class="someClasses"></div>
    ```

- 配列を渡してクラスを複数指定できる

    ```js
    var vm = new Vue({
      data: {
        foo: 'hoge'
        bar: 'piyo'
      }
    })
    ```

    ```html
    <!-- <div class="hoge piyo"> となる -->
    <div v-bind:class="[foo, bar]"></div>
    ```


- 配列とオブジェクトを組み合わせることもできる

    ```html
    <!-- 三項演算子で切り替える書き方 -->
    <div v-bind:class="[isFoo ? 'foo' : '', bar]"></div>

    <!-- 配列とオブジェクトを組み合わせる -->
    <div v-bind:class="[{ foo: isFoo }, bar]"></div>
    ```


#### styleのバインド
- `v-bind:style` に渡したオブジェクトのkey/valがそのままスタイルとなる

    ```html
    <!-- <div style="color:#FF0000;margin:10px;"></div> とかになる -->
    <div v-bind:style="{ color: textColor, margin: marginVal+'px'}"></div>
    ```

- オブジェクトごと渡すこともできる

    ```html
    <div v-bind:style="someStyles"></div>
    ```

- 配列を使うと複数のスタイル(のオブジェクト)をバインドできる

    ```html
    <div v-bind:style="[fooStyles, barStyles]"></div>
    ```

- `v-bind:style` は *ベンダープリフィクスを自動的に付加してくれる*


### v-if/v-show: 条件付きレンダリング
#### v-if/else/else-if
- `v-if` で条件付きのレンダリングができる

    ```html
    <div v-if="isFoo">foo</div>
    ```

- `v-else` で偽の場合の表示を指定できる
    - v-ifの設定された要素の直後の要素でないと動作しない

    ```html
    <div v-if="isFoo">foo</div>
    <div v-else>bar</div>
    ```

- `v-else-if` もある

    ```html
    <div v-if="isFoo">foo</div>
    <div v-else-if="isBar">bar</div>
    <div v-else>baz</div>
    ```

- `<template>` を使うと複数のタグをifの対象にできる

    ```html
    <!-- レンダリング結果にtemplateタグは含まれない -->
    <template v-if="visible">
      <div>foo</div>
      <div>bar</div>
      <div>baz</div>
    </template>
    ```

- `key` を指定すると、切り替えに伴って異なるタグが使い回されてしまうことを防げる

    ```html
    <!-- keyを指定しないと、表示が切り替わっても入力された値がそのままになる(== 同じタグが使い回される) -->
    <input type="text" v-if="isFoo" key="input-foo"></input>
    <input type="text" v-else" key="input-bar"></input>
    ```

#### v-show
- v-showは非表示の場合もDOM要素は存在し続ける
    - つまりスタイルの `display` を単に切り替える

- v-ifは非表示の場合はDOM要素そのものが消える
    - ifとshowのどちらを使うかは、初期描画と切り替え描画のコストのトレードオフになる


### v-for: リストレンダリング
- v-forとv-ifは描画のパフォーマンスを著しく下げるため、併用すべきでない
    - [スタイルガイド - v-for と一緒に v-if を使うのを避ける](https://jp.vuejs.org/v2/style-guide/#v-for-と一緒に-v-if-を使うのを避ける-必須)

    ```html
    <!-- こう書くとよろしくない -->
    <ul>
     <li v-for="item in list" v-if="item.isVisible"></li>
    </ul>

    <!-- 算出プロパティを変わりに使うといい -->
    <ul>
     <li v-for="item in visibleList"></li>
    </ul>
    ```

- アイテムと同時にインデックスを渡すことができる

    ```html
    <ul>
      <li v-for="(item, index) in list">
        {{ index }}
      </li>
    </ul>
    ```

- `in` のかわりに `of` が使える

    ```html
    <ul>
      <li v-for="item of list"></li>
    </ul>
    ```

- forはオブジェクトに対しても使える
    - オブジェクトに対するイテレーション順序は保証されていない

    ```js
    var vm = new Vue({
      data: {
        object: {
          foo: 'FOO',
          bar: 'BAR',
        },
      },
    })
    ```

    ```html
    <ul>
      <!-- key/indexは省略できる -->
      <li v-for="value, key, index in object">({{ index }}) {{ key }} : {{ value }}</li>
    </ul>
    ```

- forを使う場合はkeyも指定してあげることが推奨される
    - key指定がないほうが、局所的な再描画の効率は向上するが、keyを指定したほうがVueがDOM要素を追跡しやすくなる(== 全体的なパフォーマンスが上がる？)

    ```html
    <ul>
      <li v-for="item in list" v-bind:key="item.id"></li>
    </ul>
    ```

- forに指定した配列に対してメソッドで操作を行った場合も、その更新が検出される
    - `push()`, `pop()`, `shift()`, `unshift()`, `splice()`, `sort()`, `reverse()`

- 配列自体を新しいものに置き換えた場合も、Vueはフルではなく部分的な再描画をしてくれる


- *配列の値を直接変更しても、Vueはその変更を検出できない*
    - 代わりに `Vue.set()` あるいは `$set()` を使う

    ```js
    // listをバインドしたv-forがあったとしても、この変更は検知できない
    vm.list[index] = newValue;

    // 配列長についても同様
    vm.list.length = newLength;

    // Vue.setなら変更を検出できる
    Vue.set(vm.list, index, newValue)

    // インスタンスメソッドも使える
    vm.$set(vm.list, index, newValue)
    ```

- forに数値を指定して繰り返すこともできる

    ```html
    <!-- indexは1~10となる -->
    <ul>
      <li v-for="i in 10>{{ i }}</li>
    </ul>
    ```


- ifと同じく `<template>` が使える

    ```html
    <ul>
      <template v-for="item in list">
        <li>{{ item.id }}</li>
        <li>{{ item.text }}</li>
      </template>
    </ul>
    ```

### v-on: イベントハンドリング
- 特定のDOMに対するイベントの監視ができる

    ```js
    <!-- 単に式を評価する -->
    <button v-on:click="alert('Foo!')">foo</button>

    <!-- dataプロパティを変更する -->
    <button v-on:click="label = 'clicked'">click!</button>
    <label>{{ text }}</label>

    <!-- メソッドを呼ぶ -->
    <button v-on:click="someMethod">call method</button>
    ```

- `$event` を渡すことでDOM自体のイベントを参照できる

    ```html
    <button v-on:click="someMethod($event)">Foo</button>
    ```

    ```js
    var vm = new Vue({
      methods: {
        someMethod: function(event){
          // ...
        },
      },
    })
    ```

- イベント修飾子を使って挙動を細かく指定できる

    - `stop` = イベントをチェーンさせない
    - `prevent` = デフォルトの(DOM自体の)挙動をさせない
    - `capture` = DOM内部の要素によるイベントをキャプチャする
    - `self` = 子要素をtargetとするイベントの場合に発火しない
    - `once` = 1回だけ発火する
    - `passive` = イベントハンドラの完了を待たずにデフォルトの挙動をさせる(パフォーマンス改善目的)

    ```html
    <!-- デフォルトの挙動を抑止する  -->
    <form v-on:submit.prevent="onSubmit"></form>

    <!-- イベントハンドラを指定しなくてもいい  -->
    <form v-on:submit.prevent></form>
    ```

- キーボードイベントを指定できる

    ```html
    <!-- Enterキーの押下時のみ発火 -->
    <input v-on:keyup.enter="onKeyEnter"></input>

    <!-- 修飾キーも指定できる -->
    <div v-on:ctrl="onHoldCtrl"></div>

    <!-- exactを指定すると他の修飾キーが押されてない場合に発火する -->
    <div v-on:ctrl.exact="onHoldCtrlOnly"></div>

    <!-- マウスクリックも検出できる -->
    <div v-on:click.right="onRightButtonClicked"></div>
    ```

### v-model: フォームのバインド
- `v-model` によってフォーム内の要素をかんたんにバインドできる

    ```
    <!-- テキストボックスとdata.textをバインドする -->
    <input v-model="text"></input>


    <!-- 要素が複数ある場合は配列に対してバインドできる -->
    <div>
      <input type="checkbox" v-model="abc"></input><label>Foo</label>
      <input type="checkbox" v-model="abc"></input><label>Bar</label>
      <input type="checkbox" v-model="abc"></input><label>Baz</label>
    </div>
    ```

- 修飾子によってバインドの挙動を細かく指定できる
    - `lazy` = 入力中ではなく入力の完了後(on changed)にバインドされる
    - `number` = 入力が自動的にintにキャストされる
    - `trim` = 空白を自動的に除去する

    ```html
    <input v-model.lazy="text"></input>
    ```