Vue.js&Nuxt.js超入門を読んでのアウトプット

# 初めに
[Vue.js&Nuxt.js超入門](https://www.shuwasystem.co.jp/book/9784798056593.html)を読んだためアウトプットの内容を投稿する。


# 簡単な使い方
コンテンツデリバリーネットワークを用いてVueを使用する場合はHTMLに以下のような記述をする。

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

new Vueを使用してvueを使ってみる
```html
<!DOCTYPE html>
<html>
<head>
    <title>My first Vue app</title>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
</head>
<body>
    <div id="app">
    {{ message }}
    </div>
    <script>
    // Vueのインスタンスを作成している。この中に幾つかのオブジェクトを配置する。
    var app = new Vue({
        // appというid属性を指定している
        el: '#app',
        // messageというデータプロパティを定義し、初期値を"Hello Vue!"に設定している
        data: {
            message: 'Hello Vue!'
        }
    })
    </script>
</body>
</html>
```

###### 実際に表示されるHTML
```html
<div id="app">
    Hello Vue!
</div>
```

messageの箇所が定義したHello Vue!という値に変更されていることがわかる。

#### Mustache構文
Mustache構文とは、データとテンプレートを紐づけるための構文であり、`{{}}`(ダブルカーリーブラケット)を使用する。
先ほどの`{{ message }}`が該当する。
Vueでは、データの変更を検知して自動的にテンプレートを更新するため、データが変更されると自動的にビューが更新される。

では、自動的にデータの変更を検知してテンプレートを更新するか確認してみる。
```html
<body>
    <div id="app">
    {{ message }}
    </div>
    <button onclick="doAction();">
    click
    </button>

    <script>
    var data = {
        message: 'Hello Vue!'
    };
    var app = new Vue({
        el: '#app',
        data: data
    })
    function doAction() {
        data.message = "クリックしました";
    }
    </script>
</body>
```

上記では、クリックボタンを押すと`data`オブジェクトのプロパティである`message`に`クリックしました`という文字列が格納されて表示が変更される。
また、`data.message`はVueの変数ではなく、JavaScriptにある値である。
このように、元の変数の値を変更すると自動的に表示が変更される仕組みをリアクティブという。

### テキストの入力で使用
よく使われる例として、以下のように、inputタグ内で入力した値をmessageに格納してリアクティブに表示することができる。

```html
<body>
    <div id="app">
    {{ message }} <!-- 3.  2より値が自動的に変更される -->
    </div>
    <hr>
    
    <input type="text" onInput="doAction(event);"> <!-- 1. 発火 -->
    <script>
    var data = {
        message: ''
    };
    var app = new Vue({
        el: '#app',
        data: data
    })

    // 2. dataオブジェクトのmessageプロパティを変更
    function doAction(event) {
        data.message = event.target.value;
    }
    </script>
</body>
```

### {{}}で式を使用
`{{}}`でJavaScriptの式を使用することもできる。

先ほどのmessageの箇所を`{{ message * 1.08 }} `に変更して数値を入力すると1.08倍の値を表示することも可能である。

## プロジェクトの作成
Vueのプロジェクトを作成して起動してみる
- Vue 3を選択

```
% npm install vue
% vue create hello_app
% cd hello_app
% npm run serve
```

これでVueのプロジェクトを起動することができる。
作成したプロジェクトの主なディレクトリの役割は以下である。

publicディレクトリ・・・HTMLやCSSなどの公開されているファイル類が保管されている。
srcディレクトリ・・・Vue.jsで作成したファイルなどがまとめられている。

### ビルド
実際にプロジェクトを公開する際には以下のコマンドを使用してビルドする必要がある。
```
npm run build
```

ビルドすると`dist`というフォルダが作成される。

## renderメソッド
複雑な表示や、HTMLを組み込みたい場合などにはrenderを使用する。

renderの前に仮想DOMに関して簡易的に知る必要があったので下記にまとめる。
- Vueでは、ブラウザの実際のDOM (Document Object Model) と同じような構造を持つ、仮想的なメモリ上のDOMを使用する。
- 仮想DOMでは、DOMの変更を検知して必要なものだけ実際のDOMへ反映するので効率が良い。

renderメソッドは以下のように記載する。
```javascript
render: function(createElement) {
  // 仮想DOMの生成を行う処理
}
```
引数には`createElement`が格納されている。
これは、仮想DOMの生成関数であり、VueのコンポーネントをHTMLのDOMに変換することができる。

下記は、renderを使用して先ほど記載したHTML同様にボタンをクリックするとHTMLが変更される。
```html
<body>
    <div id="app">
    {{ message }}
    </div>
    <button onclick="doAction();">
    click
    </button>

    <script>
    var data = {
        message: 'Hello Vue!'
    };
    var app = new Vue({
        el: '#app',
        data: data,
        render: (element)=>{
            return element("p", data.message);
        }
    })
    function doAction() {
        data.message = "クリックしました";
    }
    </script>
</body>
```
生成されるHTMLでは、divタグが表示されていない。
`{{}}`を使用する場合とは異なり、renderを用いる場合は`el`で取得しているHTMLも書き換えるようである。
```html
<p>Hello Vue!</p>
```

### createElement
createElement関数に関して詳細に記載する。

```javascript
createElement(tagName, options, children);
```

#### optionsにcssを当てる
optionsは省略可能である。
下記はstyle属性を付与する例である。

```javascript
createElement(
      タグ名,
      {
        style: {
          color: 'red',
        },
      },
      表示する値
    );
```

#### children
配列を使用して子要素を組み込むことができる。
```javascript
createElement(
      'div',
      [
        createElement('p', 'This is a paragraph.'),
        createElement('ul', [
          createElement('li', 'Item 1'),
          createElement('li', 'Item 2'),
          createElement('li', 'Item 3'),
        ]),
      ]
    );
```

### v-html
v-htmlは、`{{}}`のようにテキストを表示するのではなく、 HTMLタグを解釈してHTMLとして表示することができる。
下記は諸々省略している。

```html
<div v-html="htmlContent"></div>
<script>
export default {
  data() {
    return {
      htmlContent: "<strong>This is <em>dynamic</em> HTML content!</strong>"
    };
  }
};
</script>
```

### v-bind
v-bindは、HTMLの属性にVueのデータを割り当てることができる。
以下は`href`属性にurlというVueの変数を割り当てている。
これで動的にHTMLの属性の値を変更することができる。

```html
<a v-bind:href="url">This is a link</a>
// v-bindという記述は省略も可能
<a :href="url">This is a link</a>
```

#### class
v-bindはclassにも使用することができる。
以下の例だと値が`true`だと`red`classが適応される
```html
<p v-bind:class="{red: bool値}"></p>
```

classが複数ある場合などはオブジェクトを使用すると簡潔に記載できる。
※ class以外でもオブジェクトを使用することは可能なのでデータが複数ある場合に使用するとよさそう。
```html
    <div id="app">
        <p v-bind:class="classes"></p>
    </div>

    <script>
    var classObj = {
        red: true,
        blue: false
    };
    var data = {
        classes: classObj
    };
    var app = new Vue({
        el: '#app',
        data: data
    });
```

### v-if
if文に相当するものである。
```html
< タグ v-if"条件"> ・・・表示内容・・・</タグ>
< タグ v-else>・・・表示内容・・・</タグ>
```

例
通常のif文のように`condition1`がtrueであればv-ifのpタグが表示されて、falseであればv-else-ifが評価されていく。
```html
<template>
  <div>
    <p v-if="condition1">one</p>
    <p v-else-if="condition2">two</p>
    <p v-else>three</p>
  </div>
</template>
```

### template
templateタグは、コンポーネントのテンプレートを定義するために使用される。
v-ifで複数要素をレンダリングする際やv-forを使用する場合などに用いる。

### v-for
for文に相当する機能である。
オブジェクトの場合はプロパティを取り出す順番は保証されていないので注意が必要である。
```html
< タグ v-for"変数 in 配列 or オブジェクト">
// インデックス番号を用いる
< タグ v-for"{ 変数, インデックス } in 配列">
< タグ v-for"{ 変数, キー } in オブジェクト">
```

### v-if v-forの組み合わせ

```html
            <tr v-for="(v, index) in arr" v-if="index % 2 == 0">
                <td>{{index}}</td>
            </tr>
            <tr v-else>
                <td>{{index}}</td>
            </tr>
```

この場合はv-forが優先され、偶数の場合は v-if が 奇数の場合は v-else の方が表示される。

## コンポーネント
Vueのコンポーネントは再利用可能なVueのインスタンスである。
基本的な使い方は以下である。
```haml
// 定義
Vue.component( 名前, {設定情報});

// コンポーネントの呼び出し
<コンポーネント名 />
``` 

###### 例
id`app`のdivタグをルートインスタンスとし、その中でhelloコンポーネントを呼び出している。
```html
<body>
    <div id="app">
        <hello/>
    </div>

    <script>
    Vue.component('hello', 
    {
        template: '<p>Hello!</p>'
    })
    var app = new Vue({
        el: '#app',
    });
    </script>
</body>
```

コンポーネントに変数を渡す
```html
<body>
    <div id="app">
        <hello/>
    </div>

    <script>
    Vue.component('hello', 
    {
        data: function() {
            return { 
                message: 'これは新しいメッセージです。'
            };
        },
        template: '<p>{{message}}</p>',
    });

    var app = new Vue({
        el: '#app',
    });
    </script>
</body>
```

propsを使用して値を渡す。
propsとは、コンポーネントに値を渡すプロパティであり、親コンポーネントから子コンポーネントに値を渡す際などに使用される。
propsには、型の指定やデフォルト値の指定などのオプションがある。

以下の例だと、作成したhelloコンポーネントをルートインスタンスの子要素内で呼び出している。
`props:['name']`で値を受け取り`{{}}`で表示している。
今回の例だとpropsの配列の要素は1つだが複数受け取ることも可能である。

```html
<body>
    <div id="app">
        <div><hello name="Taro" /></div>
        <div><hello name="Hanako" /></div>
    </div>


    <script>
    Vue.component('hello', 
    {
        props:['name'],
        template: '<p class="hello">Hello, {{name}}!</p>',
    });
    var app = new Vue({
        el: '#app',
    });
    </script>
</body>
```

### v-model
v-modelは、フォームの入力とVueのデータを双方向バインディングする。
これによって、フォームの値が変更されるとそれに同期してVueのデータも変更される。
`input` `select` `textarea`などで用いることができる。

v-bindと異なる点は、フォームの入力をバインドするために使用されることである。

例としては以下であり、inputタグのmessageが変更されるごとにpタグの表示が変わる。
```html
<div id="app">
  <input type="text" v-model="message">
  <p>{{ message }}</p>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    message: ''
  }
})
</script>
```

### v-on
v-onは以下のイベントリスナーを登録することができる。
以下のイベントを登録することができる。
- click
- submit
- keydown
- keyup
- mouseover

また、v-onは`@`と記載して省略可能である。

###### 例
下記例だとbuttonタグをクリックした際にv-on:clickが発火し、incrementメソッドが呼び出される。
```html
<div id="app">
  <button v-on:click="increment">Increment</button>
  <p>{{ count }}</p>
</div>

<script>
var app = new Vue({
  el: '#app',
  data: {
    count: 0
  },
  methods: {
    increment: function () {
      this.count++;
    }
  }
})
</script>
```

### computed
vueのcomputedプロパティは算術プロパティとも呼ばれ、プロパティの値を算出する手段である。
computedプロパティで定義された関数が依存するデータプロパティの値が変更された場合、自動的に再評価され、新しい値が返される。
そのため、動的な値を評価する際に用いられる。

以下の例だと、`v-model`がバインドしている`num`が変更されると、computedプロパティのincrementが呼び出される。

```html
<body>
    <div id="app">
        <hello/>
    </div>

    <script>
    Vue.component('hello', {
        data:function() {
            return { 
                num:0,
            };
        },
        computed:{
            increment:function(event) {
                return this.num++;
            }
        },
        template: '<div><p>{{increment}}</p><div><input type="number" v-model="num"></div></div>',

    });

    var app = new Vue({
        el: '#app',
    });
    </script>
</body>
```

### ローカルコンポーネント
今まで使用していた`Vue.component`はグローバルなコンポーネントである。
コンポーネントをローカルで使用する場合は以下のようにcomponentsオプションを使用する。
```javascript
new Vue({
  el: '#app',
  components: {
    'my-component': MyComponent
  }
})
```

### 子コンポーネントから親コンポーネントに値を渡す
子コンポーネントから親コンポーネントに値を渡す場合は$emitを使用する。
```javascript
this.$emit('イベント名', データ);
```

イベント名は、発行したいカスタムイベントであり、データにはそのイベントに渡すデータである。

## コンポーネントの詳細に関して

### propsのバリデーション
propsのバリデーションには、型チェック・必須プロパティ・デフォルト値・カスタムバリデーションの4つがある。

```javascript
props: {
  prop1: {
    // 型が `Number` であることを示します
    type: Number,
    // 必須プロパティであることを示します
    required: true,
    // デフォルト値を示します
    default: 'default value',
    // バリデーション関数を示します
    validator: function (value) {
      return value.length > 0
    }
  },
  prop2: {
    // 型が `String` or `Number`であることを示します
    type: [String, Number]
  }
}
```

### ゲッターとセッター
getメソッドは、プロパティが読み取られたときに呼び出され、setメソッドは、プロパティに新しい値が設定されたときに呼び出される。

```javascript
  data:function(){
    return {
      val:0,
    };
  },
  computed: {
    a:{
      get:function(){
        // thisはdataの値を参照することができる
        return this.val * 2;
      },
      set:function(value){
        this.val = Math.floor(value / 2);
      },
    },
  },
```

### watch
watchオプションは、リアクティブなデータの変更を監視し、その変更に応じて任意の処理を実行する機能である。

```javascript
watch: {
  dataPropertyName: function(newVal, oldVal) {
    // カスタムの処理をここに記述
  },
}
```
上記の場合は`dataPropertyName`というデータオブジェクト内にあるプロパティが変更された場合のみ関数が実行される。

watchオプションは、Vueコンポーネントがインスタンス化されたときに実行されるため、初期値に対してもコールバック関数が呼び出されます。
初期値に対してはコールバック関数を呼び出したくない場合は以下のように`immediate: false`を付与する。
```javascript
  dataPropertyName: {
    handler: function(newVal, oldVal) {
      // カスタムの処理をここに記述
    },
    immediate: false // 初期値に対してはコールバック関数を呼び出さない
  }
```

# Vueのイベントに関しての詳細
Vueのイベントは、バブリングとキャプチャリングというDOMイベントの伝播方法が存在する。
バブリングは、子要素から親要素へイベントが伝播していく。
キャプチャリングは、親要素から子要素に向かってイベントが伝播する方法である。

###### バブリングの例

```html
<div id="parent">
  <button id="child">Click me</button>
</div>
```

```javascript
const parent = document.getElementById('parent');
const child = document.getElementById('child');

parent.addEventListener('click', function(event) {
  console.log('parent');
});

child.addEventListener('click', function(event) {
  console.log('child');
});
```

```console
child
parent
```

上記例だと、子要素のaddEventListenerが発火した後に親要素のaddEventListenerが発火する流れになる。
では、イベントの流れを止めたい場合は以下のstopを使用することで可能になる。

```html
<div id="parent">
  <button id="child" @click.stop>Click me</button>
</div>
```
他にもstopのような機能が色々るが、紹介されていたのはsalfだった。
salfはstopと同様に記載し、自身の要素がクリックされた場合にのみイベントをトリガーすることができる。

## slot
Vueのslotはコンポーネントのテンプレート内部で外部からコンテンツを挿入するための場所を定義するものである。
親コンポーネントから子コンポーネントに値を渡す際に用いられるため、外部のコンポーネントを渡す際に値を柔軟に変更することができ、コンポーネントの再利用も可能である。
slotには名前なしと名前ありのものがあ、名前ありは複数のslotを用いる場合などに使用する。

###### 名前なし
```javascript
<Child>
    <p>slot</p>
</Child>

<div><slot /></div>
```
###### 名前あり
```javascript
<Child>
    <p slot="a">slot a</p>
    <p slot="b">slot b</p>
</Child>

<slot name="a" />
<slot name="b" />
```

# nuxt.js
Nuxt.jsは、Vue.jsの上に構築されたフレームワークで、サーバーサイドレンダリング・ルーティングの自動生成などが提供される。

### ルーティング
router-linkを使用すると、ユーザーがクリックすることで他のページに移動できる。
```javascript
<router-link to="/other">Go to Other</router-link>
```
$route変数のparamsでパラメーターを取得できる。
```javascript
$route.params.id
```
validate プロパティという動的なルートパラメータの検証を行うためのメソッドがある。
```javascript
    validate ({ params }) {
      if (params.id == undefined || params.pass == undefined){
        return false;
      } else {
        return true;
      }
    },
```

## Vuex
Vuexは、Vue.jsアプリケーションのための状態管理ライブラリであり、各コンポーネントで状態を共有することができる。
vueでは、コンポーネントが複数あることがあるため、データの共有ができるようにする。
ストアの値はページをリロードした際に初期化されるので注意が必要。

```store/index.js
import Vuex from 'vuex'

const createStore = () => {
  return new Vuex.Store({
    state: function(){
        return {
            message: 'This is store message!',
        };
    },
  })
}

export default createStore
```
下記でアクセス可能になる。

```pages/index.vue
$store.state.message
```
## ミューテーション
実際にストアの値は全てのコンポーネントで使用されるため、コンポーネントで更新することは推奨されていない。
そのため、ミューテーションというストア内のメソッドを使用してストアの値を変更する。

mutationsにメソッドを定義して、`$store.commit('ミューテーション名')`でミューテーションを呼び出すことができる。
```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  }
})

$store.commit('increment')
console.log(store.state.count) // 1
```

ミューテーションは第二引数にペイロード（任意のデータ）を受け取ることができる。
```javascript
  mutations: {
    increment (state, n) {
      state.count = n
    }
  }

$store.commit('increment', 1)
```
複数の値を渡したい場合はオブジェクトを使用する。

## アクション
アクションはミューテーションを呼び出すことができる。
複数のミューテーションを呼び出したり、アクションで非同期処理を行い、それが完了したらミューテーションを呼び出すなどの使い方がされる。
アクションは、commitメソッドを使用してミューテーションを呼び出す。
```javascript
const store = new Vuex.Store({
  state: {
    count: 0
  },
  mutations: {
    increment (state) {
      state.count++
    }
  },
  actions: {
    incrementAsync ({ commit }) {
      setTimeout(() => {
        commit('increment')
      }, 1000)
    }
  }
})
```