Udemy 「Vue.js + Firebaseで作るSPA」(Section1-10)をやってみた

# 前書き
自分はバックエンドエンジニアであるが一度フロントエンドの思考に触れたいと考えていたため、冬休みを利用してUdemyのコースを受講した。本投稿ではその前半(全12章中1−10章)の個人的記録である。前半はVueの基礎のハンズオンレクチャーである。

# 2. Vue.jsの学習環境の構築

* JSFiddleでbrowser上でVueを使ったHTML, CSS. JSの実行行動を確認できる。実際の開発では使わない。
* CDNからVueのライブラリを１行でインポート。Vueインスタンスを生成し、vueというグローバル変数に設定する。

# 3. Vue.jsの基礎

* Reactive System = Vue.jsではデータへの参照と代入を常に監視しており、新しい値が代入されたことを検知したら自動的に値を書き換える。
* consoleからvueインスタンスにアクセスするには、JSに`window.vm = vueインスタンス`。これでconsoleで`vm`。`vm.$data.message`とかで参照や代入。

* Vue.jsの環境では、jqueryで直接DOM操作することは避けるべき。これはVue.jsでは仮想DOMツリーを形成し、適応したい変化の差分だけを高速で書き換える仕組みになっているから。


* Vueインスタンス初期化時のオプションに関して
    * `elオプション`では'セレクタ名'`か`DOM要素のオブジェクト`を与えることでそのvueインスタンスをマウントするhtml要素を指定できる。(vueインスタンス(vm)を初期化した後でも、`vm.$mount('セレクタ名'or DOM要素オブジェクト)`でマウントされうる。)
    * `dataオプション`はhtml内のマスタッシュ記法における{{ xxx }}`に対応するdata: { xxx: '111' }`の'111'を代入する。もちろん無名関数出会っても代入される。
    * `watchオプション`を使って、データの値を監視して、値が変わったタイミングでcallback関数を呼び出せる。Vueインスタンスの初期化の際、dataに含まれる変数の名前をもつメソッドをwatchオプションに定義することで、その変数が変わったら同メソッドが発動される。このVueインスタンスを初期化後、`window.vm = Vueインスタンス`すればok
    * `computedオプション`を使って、特定のデータを加工したものをVueインスタンスのプロパティとして表示する。Vueインスタンスの初期化時にcomputedオプションとして無名関数を定義し、htmlからはその関数を呼ぶ。computedプロパティの関数は関数内で使用しているデータが書き換えられた場合のみ再実行され、それ以外の場合はキャッシュされたデータが再実行されます。=>つまりデータに関係ない関数(e.g. Date())を呼び出した場合はそれが再計算されることはないので注意する。
    * `filteredオプション`で定義した関数は、htmlの{{}}マスタッシュ記法内でパイプののちに呼び出せて、表示するテキストデータを加工できる。
    * `templateオプション`ではel要素に代入されるhtmlテンプレートを定義できる。この中に{{}}やディレクティブを入れることも可能。
    * `componentsオプション`



* htmlの`ディレクティブ`に関して
    * `v-bindディレクティブ`でそのhttpのタグの属性値にデータの値を設定できる。例)`<v-bind:属性名="Vueインスタンスのデータのプロパティ名">`や`<:属性名="Vueインスタンスのデータのプロパティ名">`
    * `v-onディレクティブ` はそのhtml要素で発生したイベントを検知し、特定のメソッドを呼び出す。v-onディレクティブに設定したイベントをトリガーとしてmethods内のメソッドを実行する。このメソッドにはイベントオブジェクトが引数として渡される。これによってどの要素で発生したイベントかが取得できる。例)`<button v-on:click='clickLog'>`や`<button @click='clickLog'>`
    * `v-showディレクティブ`は＝に続く要素がtrueであれば表示、falseになれば非表示にする。非表示にするために対象要素に`style="display: none;"`を設定している。=> 表示切り替えがほとんど発生しない場合に使う
    * `v-ifディレクティブ`も同様。ただし、非表示にするために対象要素をコメントアウトしている。=>　頻繁に表示切り替えが生じる時に使う(DOM要素をつけたり消したりしているためレンダリングのコストが高い)
    * `v-ifディレクティブ`と`v-else-ifディレクティブ`と`v-elseディレクティブ`を使い、要素の出し分けを行うことができる。なお、そもそもv-if=で指定されたデータ(変数)が存在する場合のみkの分岐に入る。
    * `v-forディレクティブ`は、配列のデータをVueオブジェクトから引っ張ってきて、それでループを回す。(この場合に使われない要素は、cssの非表示でもコメントアウトではなく、全く表示されない)。keyには一意なkeyを与えることで、要素の削除、追加、並び替えに有効に役立つ。例)`<li v-for "todo in todos" :key="todo.id" >`
    * `v-modelディレクティブ`は双方向バインディングによってフォームのinput要素のフォームの値をdataと連動させる。

# 4. Vueインスタンスのライフサイクル

# 5. コンポーネントの基礎
* Vueコンポーネントの登録は、Vue.component(~~~)。~~~の各オプションは関数で定義する。コンポーネント登録->Vueインスタンス生成の順に定義。なお、Vueコンポーネントではtemplateの直下には一つの要素しかかけない。
* コンポーネントの親子構造は最初に生成したnew Vueをルートとしてその下にコンポーネントがぶら下がっている構造である。
* グローバルコンポーネントとローカルコンポーネント(推奨)の登録
    * グローバル登録はVue.component(~~~)で登録。どこからでも読み込める。
    * ローカル登録されたコンポーネントのオブジェクト(constで定義とか?)を呼び出すには、呼び出し元のコンポーネントあるいはVueインスタンスで`componentsオプション`でそのコンポーネントを呼び出すよう指定する(ローカル登録する)。登録先コンポーネント内のテンプレートからしか呼び出すことはできない。どこから呼び出されているかがわかりやすいため、不要なものを削除しやすい。
* コンポーネント間のデータの伝搬。基本的にはコンポーネント内で定義したデータは他のコンポーネントから参照、書き換えできない。
    * `親->子`は**親が子を呼び出した際**、子が、子コンポーネントの登録時に`propsオプション`で指定されている変数を親から引っ張ってくる、親は子を呼び出すタグにおいて、その渡したいデータを(bindディレクティブなりで)属性値として指定する。(もちろん、子がglobalでないならば、親コンポーネント登録時に子をcomponentsに登録してやる必要がある)。propsで定義された変数もdataと同様に子コンポーネントのtemplate内からアクセス可能であるが、propsの値はdataと違って変更できない。(syncで双方向バインディングにすれば可能)ただし、propsに定義されているのがオブジェクトの場合、そのプロパティの書き換えはok。
    * `子->親`。例えば、子コンポーネントでの(フォームなどでの)データの書き換えを親コンポーネントに反映する場合。子コンポーネントのmethod内で`this.$emit(親で定義されたイベント名,  親に渡す値)`を定義することで、子でその`this.$emit`が踏まれると、親の指定されたイベントが発動される。その際、親は子から`$event`で値を受け取る。
    * `子<->親の双方向バインディング`で、子でpropsが書き換えられた時にそれが親に同期されるようにするには、子で`this.$emit('update:userName(props内の変数)', this.user_name)`とし、親で`<user-form :user-name.sync='user_name'></user-form>`とする。これで、子でuserNameが書き換えられると、親のuser-nameも書き換えられる(逆伝搬される)。(注)JavaScriptではキャメルケースで書いていても、HTML内ではケバブケースの記法で書かないといけない。
* componentsでtagに代入する際、代入されるtemplateに`<slot></slot>`を仕込むとそこに、代入元のtagが代入される。
    * 代入元(親)で`<tag slot='xxx'>`=>component(代入先、子)内の<slot name='xxx'>を置き換える。component内に<tag>要素が入る。
    * 代入元(親)で`<template slot='xxx'>`=>component(代入先、子)内の<slot name='xxx'>を置き換える。component内にはtemplate要素の中身のみが入る。
    * 代入元(親)で`<span>にも<templates>にも囲まれないコンテンツ`=>component(代入先、子)の<slot>(name属性なしのslot要素)を置き換える。

# 7. エディターの紹介
# 8. Vue CLIによる開発環境構築

## セットアップ
vueコマンドで`トランスコンパイラ`,`Vue.js関連のライブラリ`(routerやvue-x?など),
`リントツール`,`テストツール`をセットアップできる。

```
$ npm install -g @vue/cli@3.5.0`でvueコマンドをインストール。
$ vue create プロジェクト名
$ npm run serve
```

## ファイルが表示されるまで
**読み込み順は、index.html -> (main.jsでマウント指定) -> App.vue -> someComponent.vue**(この[ページ](https://swallow-incubate.com/archives/blog/20190416/)がダントツに詳しいので参考にすべし)

* `public/index.html`がシングルページアプリケーションの起点になるファイルだ。
* `index.html`内の`body`タグ内の`id=app`のdiv要素、こいつにVueインスタンスをマウントすることでアプリケーションが動作している。このVueインスタンスは`src/main.js`で初期化され、`$mount('#app')`によって前述のdiv要素にマウントされている。
    * 初期化時のrenderのh関数は`createElement関数`のエイリアスである。`render: h => h(App)`におけるAppコンポーネントとはApp.vueのことで、これはサイトの全ページで共有される大元のtemplateである。(グローバルメニューなどを入れる)
* このAppコンポーネントは`src/App.vue`からインストールした単一ファイルコンポーネント。templateとスタイルが定義されており、vue-routerの機能を使ってアクセスしてきたurlに応じて表示するコンポーネント(homeコンポーネント、aboutコンポーネントなど)を切り替えている。
* Appコンポーネントは`<router-link>`を使って要求されるページに合わせたコンポーネントをとってくる。
* そこではimportを使ってパーツのコンポーネントを取ってくる。。。
 
# 9. 単一ファイルコンポーネントの解説

**１つのファイルに１つのコンポーネント(=`template`+`JavaScript`+`css`)を書く**

* `src/components`内にAddresses.vue(ページ内で使用するコンポーネント)
* `src/views`内にHome.vue(ページのコンポーネント)。ここで、`import Addresses from @/components/Addresses.vue`する。(なおApp.vueはサイトの全ページで共有される大元のtemplateである)

* `src/main.js`にrouterをインポートし、Vueインスタンスの初期化時に引数として渡している。
* router自体は、`src/router/index.js`で定義されている。ここでは`Vue.user(VueRouter)`でVueRouterプラグインを有効化し、VueRouterインスタンスを初期化している。この際、mode, base, routesオプションを指定する。
    * `mode`は`history`,`hash`,`abstract`から選択。abstractは主にandroid, iOS開発で使うっぽい。
    * `routes`は各routeのオブジェクトの配列である。このオブジェクトのcomponentプロパティに、無名関数を指定した際には、(初回時ではなく)対応するページが踏まれた際にそのcomponentをサーバから取ってくることになる。(例)`component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')`だと、webpackの仕業でabout.jsというファイルが切り出される。コンポーネントが増えてきて画面の表示に時間がかかるという場合に取り入れてみよう。
* ルートごとにcomponentを切り替えて表示する`<router-view>タグ`とリンクの作成に利用する`<router-link>タグ`<=router.jsのファイル内でrouterモジュールを読み込んだから使える。
    * `<router-vue>`タグを書いた場所にルートに合わせたコンポーネント、つまりHomeコンポーネントやAboutコンポーネントが代入される。
    * `<router-link>`タグではto属性にリンク先のpathを指定する。レンダリングの際にはaタグに置き換えられる。[例]`<router-link to="/">Home</router-link>`や`<router-link to="{ name: 'hoge' }">Home</router-link>`