Nuxt3 勉強

初めてVue3とNuxt3を触ってアプリを開発したのでアプトプットとして残します。

```vue
<script setup>
```
#### setupの必要性
・コンパイラ処理が事前にされるため、パフォーマンスが早くなる
・export default や setup()などのコードを書かなくても良くなる
・setupがないとエラーになる

#### template
・templateの中の書き方はReactと同じで`<div>`の中に記述が必要(ルート要素を一つにまとめる必要がある)

#### NuxtPage
・リロードは実際にされずに必要な部分のみ書き換えられる
・ルーティングをすることができる(app.vueで記述)
・PrivateLinkとは？違いとは？
```vue:画面遷移ができる
  <NuxtLink to="/price">Price</NuxtLink>
```
・ルーティングで正しく動作しないことが稀にある
```nuxt.config.ts
// この記述がないと、正しいルーティング動作がされない場合がある
  app: {
    baseURL: '/', // ベース URL を設定
  },
```

#### 動的ルーティング
・ファイル名は下記のように記述する
```
[id].vue
```
idの取り出し方は下記のように記述する
```
[user_id]の場合は{{$route.params.user_id}}とつけた名前によって変わる
{{$route.params.id}}
```

#### パラメータ値のバリデーション
パラメーター値によって取得するページでバリデーション処理をする
ミドルウェアでするべきどうかは要確認が必要！
```html
<script setup>
  //ルーティングパラメータ値のidをバリデーションする
  // エラーの場合は404ページを表示
  definePageMeta({
    validate:async(route) => {
      return /^\d+$/.test(route.params.id)
    }
  })
</script>
```
[id]page.vue 左のように書くのはダメみたい->規則を把握

### レイアウト
・NuxtLayoutの基本的な仕組み
・layoutsディレクトリに保存され、layouts.default.vueに保存される
カスタムレイアウトの場合はlayouts内に追加する
```app.vue
<NuxtLayout>
      <NuxtPage />
    </NuxtLayout>
```
```vue:layouts/default.vue
<template>
  <div>
    <div class="container">
      <slot></slot>
      <footer>
        &copy; proglus.jp
      </footer>
    </div>
  </div>
</template>

<style scoped>
.container {
  max-width: 800px;
  margin: 0 auto;
  padding: 0 20px;
  color: red;
}
footer {
  background-color: orange;
  color: white;
  text-align: center;
}
</style>

```

・NuxtLayoutはスロット(slot)を使用して、コンテンツをレイアウトに埋め込むことができる

#### ブラウザのヘッダーに指定する
app.vueにuseHead()で指定できる
```ts
useHead({
  title: '学習進捗管理',
  link:
  meta: [
    {
      name: 'description',
      content: 'Nuxt.js is a framework for creating Vue.js applications.'
    }
  ]
})
```
linkを追加するとエラーになったので要確認

### templateでfor文
```vue
 <ul>
      <li v-for="user in users" :key="user.id">
        {{ user.id }}, {{ user.name }}
      </li>
</ul>
```

#### 非同期処理
useFetchで簡単に行える(デフォルトGET)
```ts
const {data:users} = await useFetch('https://jsonplaceholder.typicode.com/users')
```


#### 例外を出す

```ts
const {data:users, error} = await useFetch('https://jsonplaceholder.typicode.com/userss');
console.log(error.value)
if (error.value) {
  throw createError({statusCode: 404, statusMessage: 'APIエラーが発生しました'});
}
```

### 画像を表示(assets)

```html
  <img src="~/assets/cat.jpg" alt="猫の画像">
```

#### 生的ファイルを検索エンジンに見られないようにする方法
全ての検索エンジンに対してサイト全体をクロールを拒否する設定をrobots.txt記述
public/robots.txt (デフォルトでファイルは作成されるがコードは自分で記述が必要)
```
User-agent: *
Disallow: /

```

### 状態管理
```ts
 // 初期値にtitleを設定 (初期値がないとラインダムな値が設定される)')
  const title= useState('title', () => '学習進捗管理')
```

### composables(グローバル変数)
auto-importが行われるディレクトリでcomposablesディレクトリ配下に設定されたファイルが自動的にインポートされる

 ```composables/index.ts
export const useTitle = () => useState('title', ()=> '学習進捗管理アプリ')
 ```

```ts:呼び出しもと
  const title = useTitle()
```


# Vue3勉強

## ディレクティブ
### v-bind
v-bindを使うと状態の変化によってHTML属性を変えることができる(動的に属性を変えることが可能)
vueでは、aタグにurlを変数から記載するには、普通の書き方では遷移することはできずにv-bindを使用する必要がある
```vue
 <a v-bind:href="url">詳細</a>
```
また省略することも可能

```vue
 <a :href="url">詳細</a>
```

### v-on
イベントによって関数を呼ぶことができる
(発火処理)
```vue
 <button v-on:click="buy">買う</button>
```
短縮系
```vue
 <button @click="buy">買う</button>
```

### リアクティブな値
 templateが再度読み込まれるのは、コンポーネントが再描画されるときです。(リアクティブな値が変わった時)
 そのため、リアクティブな値を使用しないと再描画されない

 #### ref
 単一的な値(stringやnumber)
```ts
import {ref} from 'vue';
  初期化
 const food1 = ref('');
 // 最代入
 food1.value = event.target.value;
```

#### reactive
複数の値を同時に行いたい(オブジェクトや配列)

```ts
import {ref, reactive} from 'vue';
const item1 = reactive({
  food: 'ラーメン',
  price: 400,
})
  item1.food = event.target.value;
```

#### v-model
:valueの短縮系で
v-on:inputも必要なくなる
双方向のデータバインディングを行い
v-modelで定義された変数と、Vueインスタンスのデータプロパティの変数の紐付けを行い、どちらかが変わるともう一方も変わる挙動をする





#### 一日悩ましたこと

クライアントサイドレンダリングとサーバーサイドレンダリングの違い
下記の二つのAPi通信があった場合にリロード後に1の方は正しい挙動だが、2の方が
通信が行われなかった。

```ts:1
 const {data:users, error} = await useFetch('https://jsonplaceholder.typicode.com/users');
```
```ts:2
const { data: studytimeData, error: studytimeError } = await useFetch('http://localhost/api/studytime/fetch');
```
原因はnuxtの設定ファイル(nuxt.config.ts)だと思ったが、違っていて
クライアントサイドレンダリングが原因だとわかった。
下記のように設定すると正しい挙動をした

```ts
if (process.client) {
  const { data: studytimeData, error: studytimeError } = await useFetch('http://localhost/api/studytime/fetch');
}
```

理由はnuxtではリロード後に最初にSSRが実行されて、その後にCSRが実行される。
今回の場合、1はSSRとCSR両方で成功するが、2の場合はlocalhostのため、SSRは自身のサーバーを指すため、失敗して、CSRでは成功する。
しかし、SSRで失敗したことがCSRで成功したことに上書きされて、失敗している可能性が高い。
ちなみにSSRの場合はサーバー側での処理のため、ブラウザでデバック結果が表示されないので注意




### onMounted
onMounted()はライフライクルのマウント時のCSRで呼ばれる
これは検証済み(ログがターミナルに表示されない)
```ts
onMounted(() => {
  // クライアントの場合
  if (process.client) {
    console.log('mounted')
   fetchStudyData()
  }
  // SSRの場合
  if (process.server) {
    console.log('server')
  }
})
```


Nuxtライフライくる
```
リロードまたは初回アクセス
        |
        v
    サーバーにリクエスト
        |
        v
 サーバー側でSSRフェーズ開始
 ┌────────────────────┐
 | beforeCreate (サーバー) |
 | created (サーバー)      |
 | beforeMount (サーバー) |
 └────────────────────┘
        |
        v
 サーバーがHTMLを生成
        |
        v
 HTMLをブラウザに送信
        |
        v
 ブラウザがHTMLを表示
        |
        v
 クライアント側でJavaScript実行開始
 ┌────────────────────┐
 | beforeCreate (クライアント) |
 | created (クライアント)      |
 | beforeMount (クライアント) |
 └────────────────────┘
        |
        v
 ハイドレーション実行
        |
        v
 クライアント側でmounted実行
        |
        v
 ユーザー操作に応じて更新
 ┌────────────────────┐
 | beforeUpdate                 |
 | updated                      |
 | beforeDestroy                |
 | destroyed                    |
 └────────────────────┘

```
```ts
export default {
  async setup() {
    // サーバー側とクライアント側両方で実行
    console.log('beforeCreate');

    // created相当
    console.log('created');

    // beforeMount相当
    onBeforeMount(() => {
      console.log('beforeMount');
    });

    // mounted相当
    onMounted(() => {
      console.log('mounted');
    });

    // 更新時のフック
    watchEffect(() => {
      // データが変更されたときにbeforeUpdateとupdatedが実行されます
    });

    // 破棄時のフック
    onBeforeUnmount(() => {
      console.log('beforeDestroy');
    });

    onUnmounted(() => {
      console.log('destroyed');
    });
  }
}

```