Vue.js+Nuxt.js+TypeScript+Pug+Sass

Vue.js、Nuxt.js、TypeScript、Pug、Sassを案件で使用したので、覚え書き。

# ⭐️Vue.js

Vue.jsの特徴と概要。

- JavaScriptのフレームワーク
- MVVM(Model(ロジック)、View(見た目)、ViewModel(データのやり取りの橋渡し部分))という設計パターンを採用
- 双方向データバインディング(データの変更→UIの表示を更新、UIの変更→データの更新を自動的に行う機能)
- ディレクティブ
- コンポーネント指向
- 学習コストが低い

## 必要な知識

Vue.jsをはじめるにあたって、あらかじめ学習が必要な知識をまとめた。
Nuxt.jsやTypeScriptとの相性が良い。

個人的に、ES2015(ES6)以降の構文理解は必須。
vue-property-decorator(Nuxt.jsはnuxt-property-decorator)を使用する場合は、デコレーターの書き方をマスターしておく。
また、最近Vue.jsをはじめとするJSのフレームワークとTypeScriptをセットで使用されることが多いので、TypeScriptの型や構文の理解も必要。

Vue.jsとNuxt.jsの違いがあまりわかってないので、時間があるときにそれぞれの機能をまとめていきたい。

- HTML
 - [Pug](https://qiita.com/uecyan0826/items/5ed78baeb6d8dc235358#pug)
- CSS
 - [Saas or Scss](https://qiita.com/uecyan0826/items/5ed78baeb6d8dc235358#sassscss)
- JavaScript
 - JavaScript基本構文
 - ⭐️ES2015(ES6)以降(必須)
 - ⭐️[Nuxt.js](https://qiita.com/uecyan0826/items/5ed78baeb6d8dc235358#nuxtjs)
 - [⭐️TypeScript(必須)](https://qiita.com/uecyan0826/items/5ed78baeb6d8dc235358#typescript)

## 目標
- 各用語の意味がわかる
- JsvaScript、ES2015(ES6)以降の基本構文がわかる
- Vue.js、Nuxt.js、TypeScriptの基本構文がわかる
- ライフスタイルにそった記載ができる
- Vue.jsとNuxt.jsでどちらの機能を使用しているかわかる
- APIで受け取ったデータと連携できる
- VueXで状態管理が行える
- Vue RouterでSPAのページを作成できる

## 参考

- [やわらかVue.js - 迷わないVueの学び方](https://scrapbox.io/vue-yawaraka/%E8%BF%B7%E3%82%8F%E3%81%AA%E3%81%84Vue%E3%81%AE%E5%AD%A6%E3%81%B3%E6%96%B9)
- [Vue.jsを100時間勉強して分かったこと](https://qiita.com/kskinaba/items/3e8887d45b11f9132012)
- [はじめてのvue-property-decorator](https://qiita.com/simochee/items/e5b77af4aa36bd0f32e5)
- [2019年版Vue.jsを使ってる人には必ず知っていてほしいVue.jsの武器とドキュメントに書かれていないコンポーネントやメンテナンスの際に役立つTips](https://qiita.com/kahirokunn/items/6b4834b9a13406535f32)
- [Vue.js + Vuexでデータが循環する全体像を図解してみた](https://qiita.com/m_mitsuhide/items/f16d988ec491b7800ace)


## ディレクティブ
- `v-`からはじまるvue.js特有の属性
- `単一のJavaScript式`として認識される
  - 複数式は不可なので注意
  - 逆に`v-`がついてないものは数値や文字列として認識

```vuejs
🙆‍♂️ <p v-show="hoge && fuga"></p>
🙅‍♂️ <p v-show="hoge === 1; onChange()"</p>
```

### {{テキスト}}
`{{}}` で囲むことで、Vueのプロパティの値を呼び出せる。

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

### v-bind
HTML要素を`動的`に指定したい場合は、v-bindを使用
`v-bind:`は`:`に省略できる

```vuejs
<p v-bind:属性名="データを展開した属性値"></p>
<p v-bind:href="link"></p>

// 省略
<p :href="link"></p>
```

### v-for
要素を繰り返し描画したい場合は、v-forを使用

```vuejs
<li v-for="item in list">{{ item }}</li>
```

### v-on
イベントハンドリング
式が長くなったり、複数になった場合は関数化するといい
`v-on`は`@`に省略できる

```vuejs
<p v-on:イベント名="式として実行したい属性値"></p>
<p v-on:click="alert('hoge')"></p>

// 省略
<p @click="alert('hoge')"></p>
```

### v-show

trueの場合に表示。
falseの場合は、`display: none;` となるので、HTMLコードは`残る`。

```vuejs
<p v-show="isShow">ほげ</p>
```

### v-if

trueの場合に表示。
v-if、v-else-if、v-elseは同列に記載。
falseの場合は、HTML要素とその子要素をDOMから`取り除く`ので注意。
templateタグを使ってグループ化ができる。`v-showではできない`ので注意。

```vuejs
<template>
  <p v-if="status === hogeA">Aパターン</p>
  <p v-else-if="status === hogeB">Bパターン</p>
  <p v-else>それ以外</p>
</template>
```

### v-style & v-class

trueならstyle、classを付与。
クラス名にハイフンを含む場合は、`''`で囲む。

```vuejs
<p :style="{ height: `${headerHeight}px` }">スタイル</p>
<p :class="{ child: isChild, 'is-active': isActive }">クラス</p>
```

### 複数のデータ

```vuejs
<script>
new Vue({
  el: '#app',
  data: {
    item: {
      id: 001,
      src: 'item001.jpg',
      alt: '商品画像1',
      width: 200,
      height: 200
    }
  }
})
</script>
```

プロパティを全てバインドしようとすると冗長になるので、`オブジェクト`を渡して必要な要素にのみ変更を加える。

```vuejs
🙅‍♂️<img :src="item.src" :alt="item.alt ...">
🙆‍♂️<img :item :id="'thumbnail' + item.id">
```


### v-model
- [Vue.js：v-modelと$emitを使ってデータを読み書きする子コンポーネントをつくる](https://qiita.com/ozone/items/b75efe5c449cbc469b1e)
- フォーム入力バインディング
- 双方向データバインディング

1行で済んでしまうが、コンポーネントが親と子の関係の時に使用できる。3世代の場合は使用できないので注意。

```vuejs
<p v-on:input="item.name = $event.target.value"
   v-bind:value="item.name"></p>
↓
<p v-model="item.name"></p>
```

## ライフサイクル

ライフサイクルの流れ。

- インスタンス作成時
 - beforeCreate
 - Created
- マウント時
 - beforeMount
 - mounted ここでDOMにアクセスできる
- データ更新時
 - beforeUpdate
 - updated
- インスタンスの削除時
 - beforeDestroy
 - Destroyed
- エラー時
 - errorCaptured ver2.5.0以降

[参考：Vueのライフサイクルを完全に理解した](https://qiita.com/chan_kaku/items/7f3233053b0e209ef355)

### mounted
elementへのマウントがされた後に実行される。
mountedでは、子コンポーネント全てをマウントしたことは保証しない。
全てのコンポーネントがマウントされるまで、ロードを待つには以下のように記載。

要素の高さや幅を取りたいときに使うかな。

```vue:mounted.vue
<script>
mounted() {
  this.$nextTick(function () {
    // ビュー全体がレンダリングされた後にのみ実行される
    this.onResize();
    this.setContentHeight();
    this.setBannerWidth();
  })
}
</script>
```

## Vue CLI
Vue.jsでアプリ開発する上で、利用するテンプレートを提供してくれるツール。

## ⭐️vue-property-decorator
Vue.jsをTypeScript特有のクラス構文で書くためのツール
Nuxt.jsを使用する場合は、[nuxt-property-decorator](https://github.com/nuxt-community/nuxt-property-decorator)を使用
→vue-property-decoratorと多分だいたい同じ

- [TypeScriptでVue.jsを書く](https://mae.chab.in/archives/60167)
- vue-property-decorator
 - [公式](https://github.com/kaorun343/vue-property-decorator)
 - [参考](https://qiita.com/simochee/items/e5b77af4aa36bd0f32e5)
- [nuxt-property-decorator](https://github.com/nuxt-community/nuxt-property-decorator)
- [vuex-class](https://github.com/ktsn/vuex-class)

`vue-property-decorator`を使うと次のように展開される。
展開後が本来のVue.jsの書き方なので、認識しておく。
デコレーターが便利すぎて、元のvue.jsの書き方を忘れてしまう(笑)

```vuejs:展開前.vue
<script>
import { Vue, Component, Prop } from 'vue-property-decorator'

@Component
export default class YourComponent extends Vue {
  @Prop(Number)propA!: number
  @Prop({ default: 'default value' }) propB!: string
  @Prop([String, Boolean]) propC!: string | boolean
}
</script>
```

```vuejs:展開後.vue
<script>
export default {
  props: {
    propA: {
      type: Number
    },
    propB: {
      default: 'default value'
    },
    propC: {
      type: [String, Boolean]
    },
  }
}
</script>
```

## その他
### [真偽値の受け渡し](https://jp.vuejs.org/v2/guide/components-props.html#%E7%9C%9F%E5%81%BD%E5%80%A4%E3%81%AE%E5%8F%97%E3%81%91%E6%B8%A1%E3%81%97)
- 値のないプロパティは`true`になる→trueは省略できる

```vuejs
🙆‍♂️ <blog-post is-published></blog-post>
🙅‍♂️ <blog-post is-published="true"></blog-post>
```

## コンポーネント
- コンポーネント化することでメンテナンス性を向上させ、改修工数を削減→同じ機能は各ページに記載せずに1つのコンポーネントにまとめて使い回す
- 状態管理はコンポーネントには持たせず、ページに持たせる
- 複雑な状態管理になったらVuexを使用
- Prop down（親から子への値の受け渡し）と Events up（子から親へイベントの伝播）のバケツリレーが肝

```vuejs
<script>
prop
@Prop({ type: Boolean, required: true })
public hoge: boolean;

$emit
@hoge="$emit('hoge')
</script>
```

### 単一ファイルコンポーネント
- vue.jsのコンポーネントを単独のファイルとして作成する機能
- **.vue拡張子**を使用
- `<template>、<script>、<style>`のブロックで構成
- **HTMLベース**の構文

```vuejs
<template>
  <p>{{ greeting }} World!</p>
</template>

<script>
module.exports = {
  data: function () {
    return {
      greeting: 'Hello'
    }
  }
}
</script>

<style scoped>
p {
  font-size: 2em;
  text-align: center;
}
</style>
```

### プリプロセッサ
- プリプロセッサとは独自の言語（メタ言語）で書かれた構文をcssやJavaScriptに変換してくれるもの
- **sass、pug、typescriptなどをlangに指定**するだけで、単一ファイルコンポーネント内で使えるようになる

```vuejs
<template lang="pug">
  .hoge-box
　　p.text ほげほげ
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
@Component({
  components: {}
})
export default class HogePage extends Vue {}
</script>

<style lang="sass">
.hoge-box
　&
　　width: 200px

　& > .text
　　font-size: 14px
</style>
```

### [scoped css](https://vue-loader-v14.vuejs.org/ja/features/scoped-css.html)
- styleタグに`scoped`を付与すると、**その単一ファイルコンポーネントのみに適用するカプセル化**ができる。

```vuejs
<style lang="sass" scoped>
.hoge-box
　&
  　color: red;
</style>
```

- 🙆‍♂️ 影響範囲やclass名などをあまり気にせずに書ける
- 🚨 単一ファイルの`<template>`内に記載されていないclass名にはアクセスできないので注意→子コンポーネントのcssにはアクセス不可

```vuejs:Parent.vue
// 呼ぶ方が親コンポーネント
<template lang="pug">
.parent-box
  Child.hoge-box
  // Childコンポーネント呼び出し
  // 親コンポーネントからアクセスできるように.hoge-boxを付与
</template>

<script lang="ts">
import { Component, Vue } from 'vue-property-decorator';
import Child from '@/components/Child.vue';
@Component({
  components: {
    Child
  }
})
export default class Parent extends Vue {
// ここにParentコンポーネントのJavaScriptを記載
}
</script>

<style lang="sass" scoped>
/* 🙆‍♂️ Parent.vueファイル内のclassなので、アクセス可能 */
.hoge-box
  &
    color: red

/* 🙅‍♂️ Parent.vueファイル外のclass（Child.vue内のclass）なので、アクセス不可 */
.child-box
  &
    color: red
</style>
```

```vuejs:Child.vue
// 呼ばれる方が子コンポーネント
<template lang="pug">
.child-box
　p.text 子テンプレート
</template>
```

### [ref](https://qiita.com/uecyan0826/items/5ed78baeb6d8dc235358#ref)

子コンポーネントのDOMを直接操作する。

### slot

子コンポーネントにテンプレートを挿入。

[1分でわかるVue.jsのslotの使い方(サンプル付き)](https://iwb.jp/javascript-vuejs-slot-howto-sample/) 

### slot-scope

### is

動的にコンポーネントを切り替える。

## ⭐️[Vuex](https://qiita.com/_P0cChi_/items/ebf8fbf035b36218a37e)
**Vuexとは複雑な状態管理を行えるライブラリ**。
簡単な状態管理であれば、Prop down（親から子への値の受け渡し）と Events up（子から親へイベントの伝播）のバケツリレーで行えるが、複雑になると管理しきれなくなる。
※状態とはデータのこと

### 用語
Actions
Mutations
State：状態
コンポーネント

### 参考
[2018年Vue.jsとVuexを使ってる人には必ず知っていてほしいドキュメントに書かれていないコンポーネントやストア、メンテナンスの際に役立つTips](https://qiita.com/k-okina/items/512b9e502f8db49981f3)

## SPA
- シングルページアプリケーション（SPA）とは、単一のウェブページを使用して、要素の内容のみを置き換えて画面遷移させるアプリケーション設計のこと。
- 画面の更新に必要なもののみを読み込むため、描画が早いのが特徴。
- 従来ではページ全体を読み込み、再描画していたので、動作が遅かった。

## ⭐️Vue Router
SPA構築のための拡張ライブラリ。
ルーティング（ページ遷移など）の管理を行う。

## 参考
[Vue.js+TypeScriptで開発するときの参考記事まとめ](https://qiita.com/kai_kou/items/19b494a41023d84bacc7)

------------------------------------------------------------------------


# ⭐️Nuxt.js
- vue.js用のフレームワーク => .vueで記載
- SSR（サーバーサイドレンダリング）
- SSR以外でのnuxtによる恩恵が実感できていない

https://ja.nuxtjs.org/


------------------------------------------------------------------------


# ⭐️TypeScript
## 参考
- [TypeScriptの型入門](https://qiita.com/uhyo/items/e2fdef2d3236b9bfe74a)
- [TypeScript超入門(2)：構文を理解する](https://qiita.com/ringtail003/items/7ccf992f18b768e0e633)

## 型
- 型は今まで意識してこなかったが、これからは意識して記載したい
- `==` と `===` では返り値が異なるので注意
- 型を厳密に管理することでコンパイルがエラーを吐いてくれるので、エラーの原因を突き止めやすい
- undefined と null と void の違い（イメージ）

```js
// null 空 空白
const hoge = ""; // null => 箱はあるけど、中身が入ってない

// undefined 未定義
const hoge; // undefined => 箱すらない、中身に何も入ってない（代入されていない）

// void 無/初期値がない
function hoge(): void { => 関数の返り値として何もないことを表す
  console.log('hello');
}
```

- null と undefined は厳密には異なるものなので注意

```js
null == undefined // true
null === undefined // false
```

- 型推論 => 初期値がある場合は型は不要
- 早期returnは void

### `property:?` 省略可能なプロパティ

プロパティの後ろに `?` をつけると省略可能

```js
interface MyObj {
  foo: string;
  bar?: number;
}

MyObj.bar; // number | undefined
```

### ref

TypeScriptの`this.$refs.ref`は、`Vue | Element | Vue[] | Element[]`なので、`instanceof `で型の判定が必要。
要素や要素の高さを取得したい場合によく使われるので、TypeScriptを使う場合は注意。

```vuejs
<script>
const { childComponent } = this.$refs;

// Vue Component
if (childComponent instanceof Vue) {
  this.childHeight = childComponent.$el.clientHeight;
}

// HTML Element
if (childComponent instanceof Element) {
  this.childHeight = childComponent.clientHeight;
}
</script>
```

## ジェネリック
- [TypeScriptのジェネリックについて調べてみた](https://qiita.com/rindarinda5/items/9b65b637257f67b93141)


------------------------------------------------------------------------

# JavaScript

## 参考

JavaScriptやプログラミング全般でためになりそう、参考になりそうな記事。

- [Naming -名前付け-](https://qiita.com/Koki_jp/items/f3d3e824f98d182d4100)

## 基本

## ES2015(ES6)

https://qiita.com/soarflat/items/b251caf9cb59b72beb9b

### 分割代入

```js
const foods = {a: 'パン', b: 'ご飯', c: '麺'};

const {a, b, c} = foods;  // プロパティ名と同じにする
const {a: a, b: b, c: c} = foods; // 省略

console.log(a); // 'パン'
```
https://qiita.com/amamamaou/items/1ec21316b8bf05ba9c34


### 配列操作
配列操作は、元のデータを変更せずに、新しい器（配列）を用意して作成。
ミューテートしてしまうと、意図しない結果になってしまう。

※ミューテート：既存のデータを変更すること


### map
処理をして、新しい配列をつくる。

```js

var images = [
  { height: '34px', width: '39px' },
  { height: '54px', width: '19px' },
  { height: '83px', width: '75px' },
];

var heights = images.map(function(image){
  return image.height;
});

heights; // ["34px","54px","83px"]

```

### filter
条件に合うものを絞り込んで、配列にする。

```js
var post = { id: 2, title: 'タイトル'};
var comments = [
  { postId: 1, content: '記事1' },
  { postId: 2, content: '記事2' },
  { postId: 3, content: '記事3' },
  { postId: 2, content: '記事2' }
]

function commentForPost(post, comments) {
  return comments.filter(function(comment){
    return comment.postId === post.id;
  });
}

commentForPost(post, comments); // [{"postId":2,"content":"記事2"},{"postId":2,"content":"記事2"}]

```

### find
条件に合う `最初の要素` のみ返す。それ以降は、実行されない。

### every, some
every：`全て`の条件を満たしていれば、true。
some：`1つでも`条件を満たすものがあれば、true。

### reduce, reduceRight
処理をして一つの値を取得。
previousValueはそれまでの結果, currentValueは今の結果。
reduceRightは右からの処理。

### forEach

------------------------------------------------------------------------

# ⭐️HTML & CSS

## Pug

HTMLのテンプレートエンジン。
HTMLタグ(開始と終了タグ)を省略できるので、慣れてしまうともう手放せなくなる。

- [PugでHTMLコーディングを効率化・メリットと使い方を知る](https://tech.qookie.jp/posts/info-pug-feature/)


## Sass（Scss）
- [これからはcssはSassで書こう。](https://qiita.com/m0nch1/items/b01c966dd2273e3f6abe)
- [CSSのメタ言語Sass(SCSS)](https://qiita.com/ritukiii/items/67b3c50002b48c6186d6)
- 変数に定義しておけば、変数を変更すれば全て変更される。ブレイクポイントなども変数に入れておくと良い。

```sass
$btn-white: #000
```

- @import：ファイル分割ができる

```sass
//ファイルの拡張子を省略できる
@import "button.sass";
@import "button";
```

- @extend：使い回すclassを継承することができる。

```sass
// ベースのボタン
.btn
  padding: 10px 20px
  border: 1px solid blue
  border-radius: 4px
  background-color: #fff
  color: #000

// .btnのスタイルを継承
.btn-submit
  @extend .btn
  // スタイルの上書き
  background-color: red
```

- @mixin：引数を使用できる。
- @mixinで定義して、@includeで呼び出す。 => scssとsassで記載方法が違うのでハマった。

```sass
@mixin cassette($color: black , $width , $height) // scss
=cassette($color: black , $width , $height) // sass
  border: 1px dotted $color
  width: $width
  height: $height

#saeach-cassette
  @include box(blue , 300px , 600px) // scss
  +box(blue , 300px , 600px) // sass
```

## RSCSS
- [RSCSS というCSS設計について](https://qiita.com/kk6/items/760efba180ec526903db)


------------------------------------------------------------------------


# ⭐️勉強

- Vue.js
 - v-for="(item, key, index)" のkeyのあるなし
 - トランジション&アニメーション
 - 配列操作 slice、pushなど
 - slot
 - ライフサイクル
 - virtualDOMとinputの字数制限注意(input自体に字数制限をかける、再レンダリング時に切り替わるので)
 - Vuex
 - 子、親、先祖など3ファイル以上のコンポーネント間での値の受け渡し
 - v-modelの使い方、書き換え
 - .sync
 - `$emit`と`@Prop`
- TypeScriptと構文
 - デコレーター（プロパティデコレータ）
 - instanceof
- ES2015
- その他
 - nullチェック => nullの可能性のあるDOMはnullチェック


# memo

コンポーネントが増えても大丈夫。それぞれの関係。

Vue.js：部品
Vuex：ページ（状態管理）
Vue Router：たくさんのページを管理（SPAで必要な箇所のみ更新）

---

Object.keys
Object.values
Object.entries

let obj = { a: 1, b: 2, c: 3 }

console.log( Object.keys( obj )    )  // [ "a", "b", "c" ]
console.log( Object.values( obj )  )  // [ 1, 2, 3 ]
console.log( Object.entries( obj ) )  // [ ["a", 1], ["b", 2], ["c", 3] ]

http://js-next.hatenablog.com/entry/2016/01/26/180324