はじめての Nuxt3 × TypeScript

Nuxt3を学ぶに当たって参考になったサイト

https://zenn.dev/azukiazusa/articles/676d88675e4e74

## Data
関数`ref()`を使用し、変数に代入します。
こちらの変数の値を使用する時は`count.value`のように`.value`が必要です。※代入時も同様
`<template>`内から参照する場合は`.value`が必要ありません。

オブジェクトをリアクティブに管理したい時などは、`reavtive()`を使用して定義します。
こちらの記事を見た感じだと、基本的に`ref()`のみで良さそうです。

https://qiita.com/Yametaro/items/2a37f18fb52f7565b2cb

```vue
<script setup lang="ts">
const count = ref(0);

// オブジェクトの場合
const user = reactive({
  name: "佐藤",
  age: 21,
});

// 出力結果：0
console.log(count.value);

// 出力結果：21
console.log(user.age);

count.value = 2;
user.name = "山田";
</script>
```

## Props
コンポーネントのpropsの定義は以下のように行います。
以下のコードのように`props`変数に入れなくても使用できますが、`<script setup>`内から参照出来ないため、統一も含め変数に入れることが好ましいと思います。

また、`<template>`内では`props.class`のようにしなくても`class`と記述するだけでpropsの値を使用できます。
```vue
<script lang="ts" setup>
interface Props {
  message: string | number;
  class?: string;
}

// デフォルトを定義
const props = withDefaults(defineProps<Props>(), { class: "msg-class" });

// デフォルトがない場合はこのように定義する
// const props = defineProps<Props>();
</script>

<template>
  <!-- props.を使用しなくても使える例 -->
  <p :class="class">
    {{ props.message }}
  </p>
</template>
```

## Emit
コンポーネントからのemitは以下のようにすることで定義できます。
`type Emits`は型定義だけでなく見ただけで、そのコンポーネントが何をemitするのか分かりやすくしてくれます。
```vue
<script lang="ts" setup>
type Emits = {
  // 1つめにイベント名, ２つ目にemitする値の型
  (event: "change", value: string): void;
};

const emits = defineEmits<Emits>();
const onChange = (event: any): void => {
  emits("change", event.target.value);
};
</script>

<template>
  <input :type="text" @change="onChange" />
</template>
```

## Mounted
mounted動きはNuxt2の時と同じです。
書き方は以下になります。
```vue
<script setup lang="ts">
const count = ref(0)
// 出力結果：0
console.log(count.value)

onMounted(() => {
  count.value = 1
  // 出力結果：1
  console.log(count.value)
})

// 出力結果：0
console.log(count.value)
</script>
```

## Computed
computedの動きも基本的にNuxt2の時と同じです。
書き方は以下になります。
```vue
<script setup lang="ts">
const count = ref(0);

const buttonStyle = computed((): { width: string } => {
  // ここではbuttonタグのstyleを定義
  return {
    width: `${count.value * 10}px`,
  };
});
</script>

<template>
  <button :style="buttonStyle">sample</button>
</template>
```

### テスト時に注意すること(Vitest)
Nuxt3でcomputedは自動でインポートされていますが、Vitestはそれを認識することができず、エラーになります。  
そこで以下のライブラリを導入し、`vitest.config.ts`に書き加えます。 
```
$ yarn add unplugin-auto-import -D
```
`vitest.config.ts`
```ts
/// <reference types="vitest" />
import { defineConfig } from "vite";
import Vue from "@vitejs/plugin-vue";
import path from "path";
import AutoImport from "unplugin-auto-import/vite"; // 追加
export default defineConfig({
  plugins: [
    Vue(),
    AutoImport({  // 追加
      imports: ["vue"],
    }),
  ],
  test: {
    globals: true,
    environment: "jsdom",
  },
  resolve: {
    alias: {
      "@": path.resolve(__dirname, "./src"),
    },
  },
});
```


## Watch
基本的にwatch関数は以下のように使用します。
```vue
<script lang="ts" setup>
const count = ref(0);

// 複数同時に監視したい場合、配列にする[count, ...]
watch(count, (count, prevCount) => {
  console.log(`watch: ${prevCount} -> ${count}`);
});

// method
function countUp() {
  count.value++;
}
</script>

<template>
  <div>
    <p>count: {{ data.count }}</p>
    <button @click="countUp">COUNT UP</button>
  </div>
</template>
```

オブジェクトをwatchしたい場合は、記述方法が異なります。
```js
watch(() => props.監視したいプロパティ名, () => {
  // 処理
})
```

deepなどのオプションはこのように記述します。
```js
watch(() => props, () => {
  // 処理
},
{ deep: true }
)
```