Vue3 便利帳（随時更新）

Vue開発してて、これは便利だなって裏技がたくさんあるんですが、すぐ忘れてしまい毎回ググらないで済むよう、自分用のメモのつもりでまとめておきます。他にもこんなんあるで！ってのあればぜひ教えてくださいね。
(ps. Vue3 x CompositionAPI x TypeScript に移行したので、内容を書き換え中。)

# 環境構築、基本設定

### Pug の Code Folding を有効にする (VSCode)

デフォルトの設定では、.vueファイル上でPugのインデントBlockをFoldできず不便。
setting.jsonに以下を追加するとFoldできるようになります。

```json:setting.json
{
    "[vue]": {
        "editor.foldingStrategy": "indentation"
    }
}
```

### ローカル開発中、ホットリロード時にコンソールをクリア
コンソールがエラーだらけになってると、ちゃんとコンパイル通ったかよくわからなくなりますよね。そんな時はこちら。変更を保存してホットリロードされる度にコンソールが綺麗になります。

```typescript:main.ts
if (module.hot) {
  module.hot.accept()
  module.hot.addStatusHandler(status => {
    if (status === 'prepare') console.clear()
  })
}
```

### ローカル開発中、ターミナルの表示をシンプルにする
`yarn serve` でターミナルに表示される情報をシンプルにすると、エラーを把握しやすくなるかも。

```javascript:vue.config.js
module.exports = {
  devServer: {
    progress: false
  }
}
```


### グローバルなfiltersは外部ファイルにまとめておく
Vue3からはFilterが非推奨になったようです。でも、頻繁に使うものについては、まとめて定義ファイルを作っておくと便利ですよね。

```typescript:filters.ts
export default {
  toPercentage (n: number): string { return (Math.round(n * 10000) / 100).toFixed(2) },
  toLocaleString (val: number, decimal = 0): string {
    const pow = Math.pow(10, decimal)
    const rounded = Math.round(val * pow) / pow
    return rounded.toFixed(decimal).replace(/(\d)(?=(\d\d\d)+(?!\d))/g, '$1,')
  }
}
```

グローバルで使えるようにする：

```typescript:main.ts
import filters from '@/filters'
const app = createApp(App)

// register global filters
app.config.globalProperties.$filters = filters

app.mount('#app')
```

使い方:

```vue:hoge.vue
<template lang="pug">
section
  p Balance ${{ $filters.toLocaleString(balance, 2) }}
  //- 出力結果： Balance $1,234,567.89
</template>
```

`setup()`の中で使いたい場合はやや面倒。

```javascript:hoge.vue
// getCurrentInstance をインポート
import { defineComponent, getCurrentInstance } from "vue"

export default defineComponent({
  name: "App",
  props: ["balance"],
  setup() {
    const app = getCurrentInstance(); // instanceを取得
    const $filters = app.appContext.config.globalProperties.$filters; // filtersを取得
    const balanceStr: string = $filters.toLocaleString(balance, 2); // filterを使う
    return { balanceStr }
  }
})
```



### SCSSの変数やmixinをvueファイル内でグローバルに使えるようにする
```javascript:vue.config.js
module.exports = {
  productionSourceMap: false,
  css: {
    loaderOptions: {
      scss: {
        additionalData: `
          @import "~@/assets/scss/base/_variables.scss";
          @import "~@/assets/scss/base/_media_queries.scss";
        `
      }
    }
  }
}
```
2022/5/4更新："prependData"はsass-loader 8+から使えなくなりました。代わりに"additionalData"を使いましょう。)


### コンパイルされるファイル名からハッシュを取り除く
```javascript:vue.config.js
module.exports = {
  filenameHashing: false,
}
```


### CodeSplitting やめてほしい時
```javascript:vue.config.js
module.exports = {
  configureWebpack: {
    optimization: {
      splitChunks: false
    }
  }
}
```


# Composition API Syntax

### Lifecycle hooks

```
beforeCreate  -> use setup() { ... }
created       -> use setup() { ... }
beforeMount   -> onBeforeMount(() => { ... })
mounted       -> onMounted(() => { ... })
beforeUpdate  -> onBeforeUpdate(() => { ... })
updated       -> onUpdated(() => { ... })
beforeDestroy -> onBeforeUnmount(() => { ... })
destroyed     -> onUnmounted(() => { ... })
errorCaptured -> onErrorCaptured(() => { ... })
```

### `Props` の Type Annotation
`PropType`をインポートして使いましょう。やや面倒ですね。

```typescript:hoge.vue
import { defineComponent, PropType } from "vue";

interface Hoge {
  name: string;
  type: 'foo' | 'faa' | 'hoge';
  age: number;
}

export default defineComponent({
  name: "SectionInvestments",
  props: {
    hogeData: {
      type: Object as PropType<Hoge>,
      required: true
    }
  }
});
```

### Scoped CSS のセレクタ

```vue:hoge.vue
<style lang="scss" scoped>
// deep selectors
.a ::v-deep(.b) { ... }

// slot
::v-slotted(.b) { ... }

// global
::v-global(.b) { ... }
</style>
```

以下のシンタックスも議論されていましたが、今のところうまく動かない模様。
vue3.0.0で試しましたが、global扱いになってしまいます。

```vue:hoge.vue
<style lang="scss" deep>
// deep styles
</style>

<style lang="scss" slotted>
// slotted styles
</style>
```

### $nextTick は async/await を使ってこう書く

```javascript:hoge.vue
// nextTick をインポート
import { defineComponent, ref, onMounted, nextTick } from "vue"

export default defineComponent({
  name: "App",
  setup() {
    onMounted(async () => {
      await nextTick()  // サブコンポーネントのマウントを待つ
      console.log('すべてのサブコンポーネントのマウント完了！')
    })
  }
})
```

### $refs へのアクセスはこうする

```javascript:Hoge.vue
<template lang="pug">
#hoge(ref="hoge")
  h1 Hello World!
</template>

import { defineComponent, ref } from "vue"

export default defineComponent({
  name: "Hoge",
  setup() {
    const hoge = ref(null);
    console.log(hoge.value); // ← これで#hogeにアクセスできる。
    return { hoge };
  }
})
```

# Plugin 関連

### 重たいコンポーネントはLazyLoadしよう。
```javascript
// 普通にコンポーネントを読み込む場合
import Hoge from '@/Hoge.vue'

// LazyLoadで呼ばれた時に読み込む場合
const Foo = () => import('@/Foo.vue')
```

その他にも、ロジックをjsファイルに切り出した場合も、WebpackでLazyLoadすると初期ロードを軽くできます。

```javascript
import(/* webpackChunkName: "Hoge" */ '@/Hoge.js').then(module => {
  const Hoge = module.default
  Hoge.doSomething()
})
```



### FontAwesomeを使う

参考: [Font awesome を Vue.js で使ってみよう](https://qiita.com/kurararara/items/d76776a7dc2d763a068b)

```zsh:vue-cli
$ yarn add @fortawesome/fontawesome-svg-core
$ yarn add @fortawesome/free-solid-svg-icons
$ yarn add @fortawesome/vue-fontawesome
```

```javascript:@/plugins/fontawesome.js
import Vue from 'vue'
import { library } from '@fortawesome/fontawesome-svg-core'
import { fas } from '@fortawesome/free-solid-svg-icons'
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome'

library.add(fas)
Vue.component('font-awesome-icon', FontAwesomeIcon)
```

```javascript:main.js
import '@/plugins/fontawesome'
```

アイコンの使い方：

```hoge.vue
<font-awesome-icon icon="file-download" />
```


# 便利ツール

### 準備中