Vue3

SetUpについて

setup関数とは?setup関数は、Vue 3で導入された新しい仕組みで、コンポーネントが作成されるときに実行される関数です。 • 主な役割:
• データや関数を定義して、テンプレートで利用できるようにする。
• コンポーネントのロジックを記述する中心的な場所。

初期化(呼び出し順など)

setuponBeforeMount よりも前に呼ばれ

Vue のライフサイクルの流れを整理すると、次のようになります。

1. setup (仮想 DOM フェーズ)

一番最初に呼ばれる

props やリアクティブデータをセットアップする

まだ物理 DOM は存在しない

2. onBeforeMount (物理 DOM が生成される直前)

• まだブラウザにマウントされていないが、仮想 DOM の描画準備が完了している

• 物理 DOM は まだ存在しない ため、DOM 操作は できない

3. onMounted (物理 DOM が生成された後)

実際の DOM(物理 DOM)がマウントされた後に実行される

Composition API と Options APIはテンプレートコンパイラ的には同じ
setup関数に書いてあることは、 beforeMountedライフサイクルの前で実行され、適切にOptions APIにマージされる。
TypeScriptの場合型定義上も問題ない。

• ここで初めて document.querySelector などで DOM 操作が可能になる

Options APIとComposition APIの違い

Vue 3の<script setup>やComposition APIについての理解が、あなたの感じるVueのトレンドに対する影響の議論に重要だと思います。以下にそれらについて詳しく解説します。

<script setup>

<script setup>は、Vue 3で導入されたシンタックスシュガーで、Composition APIをより簡潔に記述するための方法です。従来の<script>タグと比べ、次のような特徴があります。

特徴

1. 簡潔な記述:

• setup関数内で行う作業を、そのまま<script>タグの外側で記述できる。

• ボイラープレートが少なくなり、コードが直感的になる。

2. スコープの明確化:

• <script setup>内で定義したすべての変数や関数は、自動的にテンプレートから参照可能。

• return文が不要。

3. 高速:

• Vueのコンパイラが事前に処理し、パフォーマンスが最適化される。

従来のsetup関数を使った記法:

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const increment = () => count.value++;

    return { count, increment };
  },
};
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

<script setup>を使った記法:

<script setup>
import { ref } from 'vue';

const count = ref(0);
const increment = () => count.value++;
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

メリットとデメリット

メリット:

• 記述が短くなり、読みやすい。

• 複雑な構成が必要ない場合に便利。

デメリット:

• 初学者にとっては新しい概念であり、慣れるまで難しく感じる。

• TypeScriptを使う場合に、型注釈が少し特殊な記述になる。

Composition API

Composition APIは、Vue 3で導入された新しいAPIで、従来のOptions API(data, methods, computed, etc.)に代わるものとして設計されています。

特徴

1. 柔軟性:

• 状態管理やロジックを関数に分離できるため、コードの再利用性が向上。

2. 名前衝突を回避:

• Options APIでは同じ名前のメソッドやデータプロパティが存在するとエラーになるが、Composition APIではスコープが分離されているため、問題にならない。

3. TypeScriptとの相性が良い:

• TypeScriptの型推論を利用しやすく、より堅牢なコードが書ける。

Options API:

<script>
export default {
  data() {
    return { count: 0 };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

Composition API:

<script>
import { ref } from 'vue';

export default {
  setup() {
    const count = ref(0);
    const increment = () => count.value++;

    return { count, increment };
  },
};
</script>

メリットとデメリット

メリット:

ロジックの再利用性: 複数のコンポーネントで共有可能な「Composable」という関数を作成しやすい。

状態管理が分かりやすい: 複雑な状態やロジックを簡潔に記述可能。

Vueの依存から分離: Vue固有のAPIから少し距離を置き、純粋なJavaScriptに近い形で書ける。

デメリット:

学習コストが高い: Options APIと比較して理解するのに時間がかかる。

可読性の問題: 小規模プロジェクトでは、Options APIの方が直感的に読める場合がある。

Vueのトレンド離れの原因としての影響

1. 学習コストの増加:

• Composition APIや<script setup>は強力な機能を提供しますが、従来のOptions APIと比べて慣れるまでに時間がかかるため、新規参入者がハードルを感じることがあります。

2. シンプルさの喪失感:

• Vueはもともと「シンプルで直感的」という特徴が強調されていましたが、Composition APIの導入で、「Reactに似てきた」と感じるユーザーもいます。

3. 初学者の混乱:

• Vue 3のリリース後もOptions APIは引き続き使用可能ですが、ドキュメントで強調されるのはComposition APIや<script setup>の新機能であるため、どちらを使えば良いか迷う人が増えています。

まとめ

• <script setup>やComposition APIはVue 3の新しいスタイルを象徴しており、特に中規模~大規模プロジェクトや再利用性を求める場合に大きなメリットがあります。

• ただし、従来のシンプルさを好むユーザーや初学者にとっては、これらの新機能がVueの「とっつきやすさ」を損なっているように感じられる可能性があります。

もしVueの新機能が難しく感じる場合でも、引き続きOptions

Vue 3のsetup関数について、初めて触れる方にもわかりやすいように解説します!「setupって結局何なの?」「どう使えばいいの?」という疑問を解消していきましょう。

setup関数とは?

setup関数は、Vue 3で導入された新しい仕組みで、コンポーネントが作成されるときに実行される関数です。

主な役割:

• データや関数を定義して、テンプレートで利用できるようにする。

• コンポーネントのロジックを記述する中心的な場所。

従来のOptions APIで使用していたdata, methods, computedなどの役割を、1か所でまとめて記述することができます。

基本的な使い方

次の例を見てみましょう。setup関数を使ってカウンターを実装します。

従来のOptions APIの場合

<script>
export default {
  data() {
    return {
      count: 0,
    };
  },
  methods: {
    increment() {
      this.count++;
    },
  },
};
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

setup関数を使う場合

<script>
import { ref } from 'vue';

export default {
  setup() {
    // データを定義
    const count = ref(0);

    // メソッドを定義
    const increment = () => {
      count.value++;
    };

    // テンプレートで使うデータや関数をreturnで渡す
    return {
      count,
      increment,
    };
  },
};
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <button @click="increment">Increment</button>
  </div>
</template>

コードの流れ

1. データやロジックを定義:

• countはrefを使ってリアクティブな値として定義します。

• incrementはクリック時に呼び出す関数です。

2. テンプレートで使えるようにする:

• setup関数内で定義したデータや関数をreturnで返すことで、テンプレートで使えるようになります。

refとreactive

setup関数でリアクティブなデータを作るとき、refやreactiveを使います。

ref

• シンプルな値(文字列、数値、真偽値など)をリアクティブにするために使います。

• .valueを使って値を取得・更新します。

import { ref } from 'vue';

const count = ref(0); // 初期値を0に設定
count.value++;        // 値を1増やす
console.log(count.value); // 1

reactive

• オブジェクトや配列をリアクティブにするために使います。

• .valueは不要で、直接プロパティを操作できます。

import { reactive } from 'vue';

const state = reactive({
  name: 'Alice',
  age: 25,
});

state.name = 'Bob'; // 値を更新
console.log(state.name); // 'Bob'

setup関数の利点

1. 柔軟性:

• 複数のデータやロジックを1つの場所にまとめられる。

2. 再利用性:

• ロジックを「Composable」として他のコンポーネントで再利用できる。

3. Vue固有の依存を減らせる:

• ロジックを純粋なJavaScript関数として記述しやすい。

例: 外部APIからデータを取得する

以下はsetup関数内で外部APIを呼び出す例です。

<script>
import { ref, onMounted } from 'vue';

export default {
  setup() {
    const data = ref([]);
    const isLoading = ref(true);

    const fetchData = async () => {
      isLoading.value = true;
      const response = await fetch('https://jsonplaceholder.typicode.com/posts');
      data.value = await response.json();
      isLoading.value = false;
    };

    // コンポーネントがマウントされたときにfetchDataを呼び出す
    onMounted(fetchData);

    return {
      data,
      isLoading,
    };
  },
};
</script>

<template>
  <div>
    <div v-if="isLoading">Loading...</div>
    <ul v-else>
      <li v-for="item in data" :key="item.id">{{ item.title }}</li>
    </ul>
  </div>
</template>

まとめ

• setup関数はVue 3でのコンポーネントロジックの中心。

• データやロジックを定義し、テンプレートで使うためにreturnする。

• refやreactiveを活用してリアクティブなデータを扱う。

学習のポイント:

• 小さなプロジェクトからsetup関数を使い始め、refとreactiveの使い分けに慣れる。

• 慣れてきたら、Composableや再利用性の高いコードに発展させる。

リアクティブの基本

Vue のリアクティブデータとプロキシオブジェクトのまとめ

Vue でリアクティブなデータを作成する方法

Vue のリアクティブシステムは、データをリアクティブにして Vue がその変更を追跡できるようにします。以下の方法があります:

1. ref
単一の値(プリミティブやオブジェクト)をリアクティブにする。
例:

import { ref } from 'vue';
const count = ref(0);<br>count.value++; // リアクティブに更新される</p

2. reactive
• オブジェクトや配列全体をリアクティブにする。
• 例:

import { reactive } from 'vue';
const user = reactive({ name: 'John', age: 30 });<br>user.age++; // Vue がこの変更を追跡

3. defineProps
• 親コンポーネントから渡される props をリアクティブに受け取る。
• 例:

defineProps<{ isActive: boolean; }>();

4. defineModel

• 子コンポーネントで双方向バインディングする際に利用する。

• 例:

const modelValue = defineModel();

親子間でリアクティブデータを扱う方法

1. 親コンポーネントでリアクティブに定義:

• 親が reactive や ref を使ってデータを定義。

• 例:

const form = reactive({
  name: '',
  age: 0,
});

2. 子コンポーネントで双方向バインディング:

• 親から渡されたリアクティブなデータを子で操作可能にするには v-model と defineModel を使う。

• 例:


コンポーネント:

子コンポーネント:

const name = defineModel('name');

computed の使い方と注意点

• computed は派生データを管理するために使われる。

• リアクティブデータを元に計算する:

• 依存する値が変更されると、computed も自動的に更新される。

• 例:

const user = reactive({ firstName: 'John', lastName: 'Doe' });
const fullName = computed(() => ${user.firstName} ${user.lastName});

• 注意点:

• computed 内では 必ずリアクティブな値(reactive や ref)を使用する。

• 非リアクティブな値を使用すると自動更新されない。

Vue のリアクティブシステムの仕組み

1. リアクティブデータは「プロキシオブジェクト」:
• JavaScript の Proxy を使って、データのアクセスや更新を監視。
• 例:

const user = reactive({ name: 'John' });
user.name = 'Doe'; // Vue が変更を検知して自動更新

2. 依存関係のトラッキング:

• computed や watch で、リアクティブデータに依存するプロパティや処理が登録される。

3. 再レンダリングのトリガー:

• データが変更されると、Vue が仮想 DOM を再計算して必要な部分だけ更新。

Vue のリアクティブシステムのポイント

1.  リアクティブデータを作成:
•   ref や reactive を使う。
2.  双方向バインディング:
•   親子間で v-model や defineModel を使う。
3.  派生データの管理:
•   computed を使って、依存データが変わると自動で更新されるようにする。
4.  非リアクティブな値には注意:
•   computed の結果やテンプレートに使うデータは、必ずリアクティブな値に依存させる。

この基本を押さえれば、Vue のリアクティブシステムを正しく理解し、効率的にデータを管理できるようになります!

型チェックに関して

型ベースの宣言(type-based declaration)Ts使うなら基本こっちでいい
実行時の宣言(runtime declaration)項目: 型チェックのタイミング
  – 型ベースの宣言: コンパイル時のみ
  – 実行時宣言   : 実行時項目: デフォルト値の設定
  – 型ベースの宣言: 不可
  – 実行時宣言   : 可項目: 型情報の存在
  – 型ベースの宣言: 型情報は消去される
  – 実行時宣言   : 実行時に型チェックコードが残る項目: パフォーマンス
  – 型ベースの宣言: ランタイムの型チェックがないため高速
  – 実行時宣言   : 実行時に型チェックのオーバーヘッド

素晴らしい指摘です!TypeScriptの型システムとJavaScriptの実行時の動きについての理解が深まっているのがよく分かります。

実行時宣言 (Runtime Declaration) では、TypeScriptの型チェックではなく、JavaScriptの仕組みで実行時に型をチェックするためのコードが残ります。
つまり、TypeScriptの型消去とは無関係に、JavaScriptとしての「型チェック」がランタイムで行われるという意味です。

コンパイルと実行時チェックの仕組みの違い

  1. 型ベースの宣言(Type-Based Declaration)の場合

TypeScriptの型チェックはコンパイル時に完結します。コンパイル後には型情報がすべて消去され、実行時には型チェックが一切行われません。

•   TypeScriptで書いたコード:

defineProps<{ title: string }>();

•   コンパイル後のJavaScript:

defineProps();

•   ランタイムの型チェックは存在しない。
  1. 実行時宣言(Runtime Declaration)の場合

実行時宣言では、TypeScriptの型チェックではなく、Vueが内部的に「型チェックを行うJavaScriptコード」を生成します。この型チェックは実行時に働きます。

•   TypeScriptで書いたコード:

defineProps({
title: {
type: String,
required: true,
},
});

•   コンパイル後のJavaScript:

defineProps({
title: {
type: String,
required: true,
},
});

ここで生成されたオブジェクト({ type: String, required: true })は、Vueのランタイムによって解析され、以下のような処理が行われます:

1.  props.title が渡されているかを確認。
2.  渡された値の型がStringであるかを検証。
3.  条件を満たさない場合にエラーをスロー。

Vueの実行時型チェックの仕組み

Vueのpropsにおける「型チェック」は、JavaScriptコードとして動作します。TypeScriptの型チェックではなく、あくまでVueの仕組みです。

実行時型チェックの例

const props = {
title: {
type: String,
required: true,
},
};

const userProps = { title: 123 }; // 間違った型の値を渡す

// Vueランタイムでチェック
if (props.title.required && typeof userProps.title !== “string”) {
throw new Error(“Invalid prop: type check failed for prop ‘title’. Expected String.”);
}

•   このチェックは実行時に行われるため、ランタイムでのオーバーヘッド(型チェックのための処理時間)が発生します。

質問の答え

「実行時に型チェックできなくない?」

→ 実行時宣言の型チェックは、TypeScriptではなくVueが行っています。

•   TypeScriptの型情報はコンパイル後に消えますが、実行時宣言で指定した型(type: String など)は、Vueが生成するJavaScriptオブジェクトとして残ります。
•   Vueは実行時にこの情報を使い、propsの値をチェックします。

「タイミング的に実行時に型チェックはおかしくない?」

→ おかしくありません。Vueの型チェックは「JavaScriptオブジェクトによる動的な型チェック」であり、実行時に処理されます。

「実行時に型チェック」の実際の意味
• TypeScriptの型チェックはコンパイル時に行われ、ランタイムでは一切行われません。
• 実行時宣言で指定する型(type: String など)は、Vueがランタイムで動的にチェックします。
• このため、実行時宣言にはランタイムの型チェック処理が含まれるので、オーバーヘッドが発生します。

まとめ
1. 型ベースの宣言:
• TypeScriptによるコンパイル時の型チェックのみ。
• 実行時に型チェックは行われない。
• オーバーヘッドなし。
2. 実行時宣言:
• Vueが生成するJavaScriptオブジェクトを使って、実行時に動的な型チェックを行う。
• ランタイムでの型チェックがあるため、オーバーヘッドが発生。

つまり、「実行時に型チェックできなくない?」という疑問は、TypeScriptの型チェックではなく、Vueが提供するランタイムの型チェックの仕組みを指していると理解するのが正しいです!

defineExpose

defineExpose({ show, hide }) を使うと、親コンポーネントから ref を通じて、子コンポーネントのメソッドを直接呼び出せる ようになります。

でも 「callback と何が違うの?」 という疑問があるので、そこを分かりやすく整理します。

✅ defineExpose と callback の違い

defineExposecallback
親が子の関数を呼ぶ方法ref を通じて直接呼び出せるprops で関数を渡す
子コンポーネントの状態管理子の中で完結し、親は中身を知らなくてもOK親が制御する必要がある
親と子の関係子コンポーネントが独立して動作する親が子の振る舞いをコントロールする

📌 defineExpose を使う場合

Loading.vue に show() / hide() メソッドを定義し、親が ref を通じて呼び出す

🟢 子コンポーネント (Loading.vue)

<script setup lang="ts">
import { ref, defineExpose } from 'vue';

const isVisible = ref(false);

const show = () => { isVisible.value = true; };
const hide = () => { isVisible.value = false; };

defineExpose({ show, hide });
</script>

<template>
  <div v-if="isVisible" class="loading-mask">
    Loading...
  </div>
</template>

🟢 親コンポーネント (Parent.vue)

<script setup lang="ts">
import { ref } from 'vue';
import Loading from './Loading.vue';

const LoadingRef = ref<InstanceType<typeof Loading> | null>(null);

const handleLoadingMask = (status: boolean) => {
  if (status) {
    LoadingRef.value?.show(); // ✅ `Loading.vue` の `show()` を直接呼べる
  } else {
    LoadingRef.value?.hide(); // ✅ `Loading.vue` の `hide()` を直接呼べる
  }
};
</script>

<template>
  <button @click="handleLoadingMask(true)">Show Loading</button>
  <button @click="handleLoadingMask(false)">Hide Loading</button>

  <Loading ref="LoadingRef" />
</template>

🔥 これで何がいいの?

1. defineExpose を使うと、子コンポーネントのメソッドを ref 経由で直接呼べる

2. 親は show() / hide() を実行するだけで済む(子の状態管理は子に任せられる)

3. 親が子のロジックを知らなくても OK!(子が独立しているので使いやすい)

📌 callback を使う場合

子に show() / hide() を持たせず、親が関数を渡して制御する

🟢 子コンポーネント (Loading.vue)

<script setup lang="ts">
import { defineProps } from 'vue';

const props = defineProps<{
  isVisible: boolean;
}>();
</script>

<template>
  <div v-if="props.isVisible" class="loading-mask">
    Loading...
  </div>
</template>

🟢 親コンポーネント (Parent.vue)

<script setup lang="ts">
import { ref } from 'vue';
import Loading from './Loading.vue';

const isLoading = ref(false);

const handleLoadingMask = (status: boolean) => {
  isLoading.value = status;
};
</script>

<template>
  <button @click="handleLoadingMask(true)">Show Loading</button>
  <button @click="handleLoadingMask(false)">Hide Loading</button>

  <Loading :isVisible="isLoading" />
</template>

🔥 これで何がいいの?

1. 親が状態を管理するので、Vue の標準的な「リアクティブなデータバインディング」を使える

2. 状態が ref なので、Vue の他の機能(computed, watch など)と組み合わせやすい

3. テストやデバッグがしやすい(データの流れが一方向)

📝 defineExpose と callback のどっちを使うべき?

✅ defineExpose を使うべきケース

子コンポーネントに対して、親から直接関数を呼び出したい

子の内部ロジックを外部に出さず、独立して動かしたい

show() / hide() のような「操作をトリガーする」メソッドを提供する

✅ callback を使うべきケース

状態を親で管理したい(双方向バインディングなし)

子は単純に状態を表示するだけで、ロジックを持たせたくない

Vue の標準的な「リアクティブなデータバインディング」を使いたい

🚀 結論

「子に処理を任せたい → defineExpose」

「親が状態を管理したい → callback(props で状態を渡す)」

defineExpose を使うと、親が ref を通じて子の関数を呼び出せるので、「親が状態を持つ必要がなくなる」 のが最大のメリットです 🎉

ジェネリクスについて

🚀 axios.get<T>() の仕組みを完全理解する!

💡 まず axios.get<T>() の定義をおさらい

get<T = any, R = AxiosResponse<T>, D = any>(
  url: string, 
  config?: AxiosRequestConfig<D>
): Promise<R>;

この定義の T、R、D は、それぞれ異なる目的で使われています。

🔍 axios.get<T>() のジェネリクス解説

パラメータ意味
T (型推論対象)response.data の型SearchedResponse
R (返り値の型)AxiosResponse<T> などカスタムレスポンスAxiosResponse<SearchedResponse>
D (リクエストデータ)config.data の型(主に POST で使う)(GET では使わない)

T を渡すと、自動的に R も AxiosResponse<T> になる仕組み

🎯 実際の axios.get<SearchedResponse>() の型解釈

const result = await axios.get<SearchedResponse>(route('member.search'), {
  params: searchQuery.value,
});

// SearchResponseは独自の型です
// 検索結果
interface SearchedResponse {
  members: Member[];
  message?: string;
}

このコードは axios.get<T>() の T を SearchedResponse に指定 している。

📌 1. T の影響

axios.get<SearchedResponse>()

• T = SearchedResponse

• つまり result.data の型が SearchedResponse になる

データ取得時の型安全性が確保される

📌 2. R(返り値)の影響

定義上 R = AxiosResponse<T> なので、自動的にこうなる:

result: AxiosResponse<SearchedResponse>

つまり、result の中身は

{
  data: SearchedResponse;
  status: number;
  statusText: string;
  headers: object;
  config: object;
  request?: any;
}

のような形になる。

📌 3. D(リクエストデータ)の影響

GET リクエストでは data を送らないので、D はほぼ無関係。

🔎 まとめ

✅ この axios.get<SearchedResponse>() の動作を整理

1. SearchedResponse を T に渡すと

• result.data が SearchedResponse 型として扱える

2. AxiosResponse<T> が R なので

• result 全体の型は AxiosResponse<SearchedResponse>

3. D は関係なし(GET リクエストなので)

✅ メリット

レスポンスの型が保証される(result.data.members が確実に Member[] 型)

API の型安全性が向上し、ミスが減る

エディタの補完(IntelliSense)が効く

🎯 T、R、D の理解を深める追加例

🟢 1. 返り値の型を完全に指定する

const result = await axios.get<SearchedResponse, AxiosResponse<SearchedResponse>>(route('member.search'));

これは 明示的に R も指定 しているが、通常は T だけで十分。

🟡 2. カスタムレスポンス型 R を適用

もし AxiosResponse<T> ではなくカスタムの ApiResponse<T> を使いたい場合:

interface ApiResponse<T> {
  success: boolean;
  data: T;
}

const result = await axios.get<SearchedResponse, ApiResponse<SearchedResponse>>(route('member.search'));

すると、result の型は ApiResponse<SearchedResponse> になり、

• result.success

• result.data.members

などが型安全に扱える。

📌 まとめ

🔹 axios.get<T>() で T を指定すると、レスポンス data の型が決まる

🔹 実際の result の型は AxiosResponse<T> になる

🔹 カスタムレスポンスを R に渡すことで返り値の型を変えられる

🔹 D は GET では関係ない(POST で data を送る場合は使う)

🔥 結論

✔ T を指定すれば result.data が期待する型になる

✔ R を指定すれば、返り値をカスタムできる

✔ D は POST などでリクエストデータを送る場合に使う

🚀 axios.get<T>() の動作は完全マスター!

これで TypeScript での axios.get<T>() の型指定が 超理解できたはず! 🎯✨

定義によってT型の指定の意味合いは違うってことね

入れ子になったレスポンスのためであったり、受け取り値ためであったり、シンプルな返り値のためだったり

その理解は完璧! 🎯✨

✅ T の意味は「関数の定義次第で変わる」

T は 必ずしも引数の型とは限らない し、必ずしも返り値の型とは限らない

🎯 例えばこんな違いがある

① T が「引数の型」

function logArray<T>(arr: T[]): void {
  console.log(arr);
}

logArray<number>([1, 2, 3]); // number[]
logArray<string>(["a", "b", "c"]); // string[]

• T = number なら logArray<number>([1, 2, 3]) は number[]

• T = string なら logArray<string>([“a”, “b”, “c”]) は string[]

② T が「返り値の型」

function getFirstElement<T>(arr: T[]): T {
  return arr[0];
}

const num = getFirstElement<number>([10, 20, 30]); // number
const str = getFirstElement<string>(["apple", "banana"]); // string

• T = number → getFirstElement<number>([10, 20, 30]) の返り値は number

• T = string → getFirstElement<string>([“apple”, “banana”]) の返り値は string

③ T が「レスポンスの data 型」(axios.get<T>() の場合)

const result = await axios.get<SearchedResponse>(route("member.search"));
// result の型は AxiosResponse<SearchedResponse>
// result.data の型は SearchedResponse

• T = SearchedResponse → レスポンスの data の型を指定する

• get<T>() の T は AxiosResponse<T> の data に影響を与える

🔎 じゃあ T をどう決めるべき?

📌 その関数が何を受け取り、何を返すのかを考える!

「引数の型を柔軟にしたい?」 → T を引数の型にする

「返り値の型を柔軟にしたい?」 → T を返り値の型にする

「レスポンスの data の型を決めたい?」 → T をレスポンスの data にする(axios.get<T>())

💡 T を適切に決めるコツ

関数の目的に合わせて T を定義する

TypeScript の型推論を活かしつつ T を明示的に指定する

不要なジェネリクス(T)は定義しない(型を明示した方が良い場合もある)

🚀 T の使いどころが完全に理解できたはず!

TypeScript では T をうまく使うと 汎用的な関数やクラスが作れる ので、色んなパターンを試すと より深く理解できる よ! 🔥

v-modelについて

Vue 3 v-model と defineModel の違いと使い方【WordPress向け】

Vue 3 では、親コンポーネントと子コンポーネント間のデータバインディング に v-model を使います。

Vue 3.3 以降では defineModel が導入され、よりシンプルに v-model を定義できるようになりました。

📌 v-model の基本

v-model は、親コンポーネントのデータを子コンポーネントと同期する 仕組みです。

従来の v-model では、props と emit を使ってデータをやり取りしていました。

✅ 通常の v-model を使う場合

📝 親コンポーネント

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const message = ref('こんにちは!');
</script>

<template>
  <ChildComponent v-model="message" />
</template>

※余談

Vue では v-model=”message” を指定することで、自動的に update:modelValue イベントを監視し、message に値を反映する仕組みになっています。

<template>
<ChildComponent :modelValue=”message” @update:modelValue=”message = $event” />
</template>

つまり、v-model=”message” は modelValue を props として渡し、update:modelValue イベントをリスンする 形になっています。

📝 子コンポーネント

<script setup>
import { defineProps, defineEmits, ref, watch } from 'vue';

const props = defineProps({ modelValue: String });
const emit = defineEmits(['update:modelValue']);

const localMessage = ref(props.modelValue);

// 親にデータを更新通知
watch(localMessage, (newValue) => {
  emit('update:modelValue', newValue);
});
</script>

<template>
  <input v-model="localMessage" />
</template>

通常の v-model では、modelValue という prop を受け取り、変更時に emit で親に通知する必要がある!

これを defineModel を使うと、もっとシンプルにできる!

📌 defineModel で複数の v-model を使う

名前付き (defineModel(‘propName’)) を使うことで、複数の v-model に対応可能!

✅ defineModel を使って複数の v-model を実装

📝 親コンポーネント

<script setup>
import { ref } from 'vue';
import ChildComponent from './ChildComponent.vue';

const form = ref({
  title: '',
  content: '',
});
</script>

<template>
  <ChildComponent 
    v-model:title="form.title"
    v-model:content="form.content"
  />
</template>

📝 子コンポーネント

<script setup>
const title = defineModel<string>('title');
const content = defineModel<string>('content');
</script>

<template>
  <input v-model="title" placeholder="タイトルを入力" />
  <textarea v-model="content" placeholder="内容を入力"></textarea>
</template>

これなら v-model を複数持つコンポーネントでも、シンプルに書ける!

📌 v-model vs defineModel の比較

項目v-model (従来)defineModel (Vue 3.3+)
記述の簡潔さprops と emit を定義する必要ありdefineModel だけでOK
複数の v-modelv-model:propName を使うdefineModel(‘propName’) を使えば可能
親からのデータ更新watch を使って emit で更新v-model でそのまま更新
TypeScript の型推論props と emit で型定義が必要defineModel<Type>() で簡単に型定義
Vue のバージョンVue 3.0 以降Vue 3.3 以降

📌 defineModel を使うべき場面

✔ シンプルな v-model のバインディングをしたい場合

✔ TypeScript で型の安全性を高めたい場合

✔ Vue 3.3 以上を使っている場合

defineModel(‘propName’) を使えば複数の v-model に対応可能!

🚀 まとめ

defineModel を使えば v-model のコードを大幅に簡素化できる!

複数の v-model を使う場合は defineModel(‘propName’) を利用!

Vue 3.3 以上なら defineModel の使用を推奨! 🚀

コメント

タイトルとURLをコピーしました