Nuxt3 でプロジェクトを作成するときのメモ  (ついでにVite, HMR, Bootstrap5, icons)

nuxt-3.0.0 を使用する前提です。

# 結論

- [Nuxt3: Quick Start](https://v3.nuxtjs.org/getting-started/quick-start)

上のページを読めばできます。がしかし、他のモジュールを使おうとすると、微妙に説明が足りないので、以下で補完します。

# バージョンなど

```
$ npm -v
8.19.1
$ yarn -v
1.22.19
$ node -v
v16.16.0
$ nuxi -v
Nuxi 3.0.0-rc.9
```

# プロジェクト作成

```
$ nuxi init nuxt3-firebase-bootstrap-sample
$ cd nuxt3-firebase-bootstrap-sample
$ yarn
$ yarn dev
```

これだけです。TS はデフォルトで対応です。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/bfb8e777-c4b6-fa8d-a399-8ef12cdd33b9.png)


## HMR (Hot Module Reload)

builder はデフォルトで vite になっています。HMR はデフォルトではオンではないようです。下記設定で、オンになります。

```nuxt.config.ts
import { defineNuxtConfig } from 'nuxt'

// https://v3.nuxtjs.org/api/configuration/nuxt.config
export default defineNuxtConfig({

    vite: {
        server: {
            watch: {
                usePolling: true
            }
        },
    }
})
```

これでオンになります。これで更新が爆速になります。

## ルーティングのテスト

- [Nuxt3: Pages Directory](https://v3.nuxtjs.org/guide/directory-structure/pages)

`pages` というフォルダを作って、その下に `index.vue`, `sample.vue` ファイルを作ります。`App.vue` は下記のようにします。

```App.vue
<template>
  <div>
    <NuxtPage />
  </div>
</template>
```

`div` なしでルートに `<NextPage/>` を置くとエラーになります。

`pages/index.vue` は `/` のパスに対応します。`/index` でも読めます。

```pages/index.vue
<template>
    <div>
        <h1>Top Page!</h1>
        <NuxtLink to="/sample">Sample page</NuxtLink>
    </div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/cdb5dca4-aabd-24c6-b769-452b13b3fa91.png)

`pages/sample.vue` は `/sample` のパスに対応します。

```pages/sample.vue 
<template>
    <div>
        <h1>Sample Page!</h1>
        <NuxtLink to="/">Top page</NuxtLink>
    </div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/4bb1255d-304a-9299-8f34-910d989e70bf.png)

# Bootstrap5

モジュールをインストールします。

```
$ yarn add bootstrap @popperjs/core bootstrap-icons
```

`nuxt.config.ts` に追記します。アイコンを使わない場合は、`bootstrap-icons` は不要です。

```nuxt.config.ts
export default defineNuxtConfig({
    css: [
        "bootstrap/dist/css/bootstrap.min.css",
        "bootstrap-icons/font/bootstrap-icons.css"
    ],
    ...
}
```

これであとは普通に Bootstrap を使えます。

- [Navbar example](https://getbootstrap.jp/docs/5.0/examples/navbar-static/)

上記のサンプルのページを作ってみた例を示します。

```app.vue
<template>
<div>
  <header>
    <nav class="navbar navbar-expand-md navbar-dark bg-dark mb-4">
      <div class="container-fluid">
        <a class="navbar-brand" href="#">Top navbar</a>
        <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" aria-expanded="false" aria-label="Toggle navigation">
          <span class="navbar-toggler-icon"></span>
        </button>
        <div class="collapse navbar-collapse" id="navbarCollapse">
          <ul class="navbar-nav me-auto mb-2 mb-md-0">
            <li class="nav-item">
              <a class="nav-link active" aria-current="page" href="#">Home</a>
            </li>
            <li class="nav-item">
              <a class="nav-link" href="#">Link</a>
            </li>
            <li class="nav-item">
              <a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
            </li>
          </ul>
          <form class="d-flex">
            <input class="form-control me-2" type="search" placeholder="Search" aria-label="Search">
            <button class="btn btn-outline-success" type="submit">Search</button>
          </form>
        </div>
      </div>
    </nav>
  </header>
  <main>
    <NuxtPage />
  </main>
</div>
</template>
```

`pages/index.vue`

```pages/index.js
<template>
  <div class="bg-light p-5 rounded">
    <h1>Navbar example</h1>
    <p class="lead">This example is a quick exercise to illustrate how the top-aligned navbar works. As you scroll, this navbar remains in its original position and moves with the rest of the page.</p>
    <NuxtLink type="button" class="btn btn-primary" to="/sample">Go To Sample</NuxtLink>
  </div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/55bd3631-2a9c-445d-4bf5-e55a3b753180.png)


`pages/sample.vue`

このページはアイコンのテストも兼ねてます。

```pages/sample.vue 
<template>
<div class="bg-light p-5 rounded">
    <p class="h1">Sample Page</p>
        <i class="bi bi-house-fill" 
            style="font-size: 2.5rem; color: black;"></i>
        <NuxtLink 
            class="btn btn-primary mb-3 ms-2" role="button" type="button"
            to="/">Top page</NuxtLink>
</div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/e2b61709-9fde-d6d7-23b5-74b2cce7aff4.png)

アイコンを追加したときは、ブラウザのリロードが必要です（多分）。

map ファイルがないというエラーが出るときは、プロジェクトのルートフォルダで下記のようにします。

```
$ mkdir public
$  ln -s ./node_modules/bootstrap/dist/css/bootstrap.min.css.map ./public
```

# Material Design Icons

- [Material Design Icons](https://materialdesignicons.com/)

MDI を使用する方法として、`nuxt.config.ts` に CSS を追加してフォントとして使う方法と、個別に `import` して SVG として使う方法があります。後者のほうが、ファイルサイズは小さくなります。

## nuxt.config.ts にCSS を追加



フォントをインストールします。

```
$ yarn add @mdi/font
```

`nuxt.config.ts` に CSS を追記します。

```nuxt.config.ts
export default defineNuxtConfig({
    css: [
        "@mdi/font/css/materialdesignicons.css"
    ],
    ...
}
```

これで、下記のようなかんじでつかえます。

```sample.vue
<i class="mdi mdi-home" style="font-size: 2.5rem; color: black;"></i>
```

例：

```pages/sample.vue 
<template>
<div class="bg-light p-5 rounded">
    <p class="h1">Sample Page</p>
        <i class="bi bi-house-fill" 
            style="font-size: 2.5rem; color: black;"></i>
        <i class="mdi mdi-home"
            style="font-size: 2.5rem; color: black;"></i>
        <NuxtLink 
            class="btn btn-primary mb-3 ms-2" role="button" type="button"
            to="/">Top page</NuxtLink>
</div>
</template>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/9dfd55be-4f89-f333-5aa1-c6af23b14f9d.png)

この方法はお手軽です。ただし、CSS 全体を出力ファイルに組み込むので、ファイルサイズは大きくなります。フォントを追加したときは、ブラウザのリロードが必要です（多分）。

## 個別に import する

モジュールを追加します。

```
$ yarn add @mdi/js
```

個別にインポートして、SVG の path として使います。


```src/sample.vue
<template>
<div class="bg-light p-5 rounded">
    <p class="h1">Sample Page</p>
        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
            width="64" height="64" viewBox="0 0 32 32">
            <path :d="mdiHomePath"></path>
        </svg>
        <NuxtLink 
            class="btn btn-primary mb-3 ms-2" role="button" type="button"
            to="/">Top page</NuxtLink>
</div>
</template>
<script>
import { mdiHome } from '@mdi/js';
export default {
    data: () => ({
        mdiHomePath: mdiHome
    })
}
</script>
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/266247/1fda9695-c530-3840-af33-d282e4bcf729.png)

svg としてレンダリングされるので、サイズや表示位置、色などかなり自由度高く設定できます。また、全体のファイルサイズも（かなり）小さくなります。

`<script setup>` で書く場合は下記です。

```pages/sample.vue
<template>
<div class="bg-light p-5 rounded">
    <p class="h1">Sample Page</p>
        <svg version="1.1" xmlns="http://www.w3.org/2000/svg" 
            width="64" height="64" viewBox="0 0 32 32">
            <path :d="mdiHome"></path>
        </svg>
        <NuxtLink 
            class="btn btn-primary mb-3 ms-2" role="button" type="button"
            to="/">Top page</NuxtLink>
</div>
</template>

<script setup>
import { mdiHome } from '@mdi/js';
</script>
```

変数定義が不要になるので、かなりすっきりします。

それでも、ただアイコン置きたいだけみたいなときには、まあまあ面倒ではあるので、ファイルサイズ圧縮のためだけなら、下記のようなラッパーのモジュールを使うのもありです。

- [Vue Material Design Icon Components](https://github.com/lacrioque/vue3-material-design-icons)