SHOEISHA iD

※旧SEメンバーシップ会員の方は、同じ登録情報(メールアドレス&パスワード)でログインいただけます

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

ますます便利になるVue.jsの新機能を探ろう!

Vue3.3の新機能とは? ジェネリクスコンポーネントを中心にdefineSlots()などの新機能を解説【後編】

TypeScriptのサポートがアップデート! Vue3.3の新機能まとめ 後編


  • X ポスト
  • このエントリーをはてなブックマークに追加

 Vue3、すなわち、Vue.jsのバージョン3がリリースされたのが3年前の2020年9月です。そのVue3が、Vueプロジェクトのデフォルトになったのが、2022年2月です。その時点でのVue本体のバージョンは3.2です。それから1年以上が経過した2023年5月11日に、Vue3.3がリリースされました。本稿では、前編と後編の2回にわたって、このVue3.3での新機能をまとめて紹介します。後編である今回は、ジェネリクスコンポーネントを中心に、defineSlots()などの他の新機能を紹介していきます。

  • X ポスト
  • このエントリーをはてなブックマークに追加

ジェネリックコンポーネント

 前編で紹介した新機能に続いて、次に紹介するのは、新機能リストの5です。これは、scriptタグに新たに導入されたgeneric属性についてです。具体的なコードを見る前に、少しサンプルを紹介したいと思います。

各組の人数を表示するサンプル

 例えば、図1のような画面を考えてみます。

図1: 各クラスの人数を表示する画面
図1: 各クラスの人数を表示する画面

 これは1〜6組の各クラスの人数とA〜F組の各クラスの人数を表示した画面です。この画面の元データとして考えられるのがリスト1の(1)と(3)の2種のMapオブジェクトです。

リスト1:scriptタグのgeneric属性を利用したコンポーネント
<script setup lang="ts">
  :
const classSizeNoInit = new Map<number, number>();
classSizeNoInit.set(1, 35);
classSizeNoInit.set(2, 34);
  :
const classSizeNo = ref(classSizeNoInit);  // (1)
const focusedClassNo = ref(4);  // (2)

const classSizeCharInit = new Map<string, number>();
classSizeCharInit.set("A", 35);
classSizeCharInit.set("B", 34);
  :
const classSizeChar = ref(classSizeCharInit);  // (3)
const focusedClassChar = ref("B");  // (4)
</script>
<template>
  <section>
    <h2>1〜6組の人数</h2>
    <ClassSizeList v-bind:classSizeList="classSizeNo" v-bind:focused="focusedClassNo"/>  // (5)
  </section>
  <section>
    <h2>A〜F組の人数</h2>
    <ClassSizeList v-bind:classSizeList="classSizeChar" v-bind:focused="focusedClassChar"/>  // (6)
  </section>
</template>

 リスト1の(1)は、組の識別子が1組、2組、…、のような数字になっています。一方で、(3)は、A組、B組、…、のように文字になっています。それに合わせて、Mapのキーのデータ型は、(1)の元データとなるclassSizeNoInitではnumber型を、(3)の元データであるclassSizeCharInitではstring型としています。同様に、図1で「注目!!!」と表示させる組を表すリアクティブ変数である(2)と(4)に関しても、(2)はnumber型であり(4)はstring型となっています。

 そして、これらのデータを子コンポーネントであるClassSizeListにPropsで渡して、図1のようなレンダリングにしたいとします。それが、(5)と(6)です。Prop名は、それぞれclassSizeListとfocusedです。

ユニオン型Props定義の問題点

 では、このClassSizeListコンポーネントのProps定義はどのようにすればよいのかというと、まず思いつくのはユニオン型の定義です。これは、リスト2のようになります。

リスト2:ユニオン型でのClassSizeListのProps定義
type Props = {
  classSizeList: Map<number | string, number>;
  focused: number | string;
};

 これでとりあえずは動作します。ただし、問題があります。それは、例えば、リスト1の(5)において、v-bind:focusedとして渡さなければならない値、すなわち数値型であるはずのfocusedClassNoに「C」のような文字を渡しても、問題なく動作してしまうということです。コード上もエラーになりませんし、レンダリング結果でもエラーになりません。ただ、「注目!!!」の表記がなくなるだけです。この種類のバグは非常に発見しにくいです。

generic属性によるジェネリックコンポーネント

 そこでVue3.3で新たに導入されたのが、ジェネリックコンポーネント、すなわち、scriptタグのgeneric属性です。これを利用したClassSizeListコンポーネントは、リスト3のコードとなります。

リスト3:scriptタグのgeneric属性を利用したコンポーネント
<script setup lang="ts" generic="CN">  // (1)
type Props = {
  classSizeList: Map<CN, number>;  // (2)
  focused: CN;  // (3)
};

defineProps<Props>();
</script>

 リスト3の(1)では、scriptタグにgeneric属性としてCNを定義しています。この記述でいわゆるジェネリクスとして機能するようになります。もちろん、ジェネリクスですので、CNという記述は、なんでもよく、TでもTypeでもかまいません。

 そして、このgeneric属性で指定した文字列をコンポーネント内では型指定として利用できます。(2)ではPropsのclassSizeListであるMapオブジェクトのキーとしてCN型を指定しています。同様に、Propsのfocusedも同じCN型となっています。こうすることで、classSizeListであるMapオブジェクトのキーのデータ型とfocusedのデータ型が同一でなければならなくなります。

 そして、このCN型がどのようなデータ型になるかは、このコンポーネントを利用する側から渡されるPropsのデータ型から自動判定されるようになっています。例えば、リスト1の(5)では、Mapのキーとしてnumber型を指定したclassSizeNoをPropsのclassSizeListとして渡しています。この時点でCNがnumberと判定され、Propsのfocusedもnumber型である必要がでてきます。仮に、これに反して文字列を渡すとすると、図2のようにコーディング段階でエラー表示してくれます。

図2: ジェネックコンポーネントを利用した際のエラー表示
図2: ジェネックコンポーネントを利用した際のエラー表示

 もちろんリスト1の(6)のCNがstringとなるパターンにおいても、focusedにnumber型の値を渡すと、同様にエラーとなります。実行してもエラーとならずにバグとなるユニオン型でのProps定義と比べると、コーディング段階からエラー表示となるこのジェネリクスコンポーネントとでは、安心感に雲泥の差がありますね。

次のページ
defineSlots()とdefineOptions()

この記事は参考になりましたか?

  • X ポスト
  • このエントリーをはてなブックマークに追加
ますます便利になるVue.jsの新機能を探ろう!連載記事一覧
この記事の著者

山田 祥寛(ヤマダ ヨシヒロ)

静岡県榛原町生まれ。一橋大学経済学部卒業後、NECにてシステム企画業務に携わるが、2003年4月に念願かなってフリーライターに転身。Microsoft MVP for Visual Studio and Development Technologies。執筆コミュニティ「WINGSプロジェクト」代表。主な著書に「独習シリーズ(Java・C#・Python・PHP・Ruby・JSP&サーブレットなど)」「速習シリーズ(ASP.NET Core・Vue.js・React・TypeScript・ECMAScript、Laravelなど)」「改訂3版JavaScript本格入門」「これからはじめるReact実践入門」「はじめてのAndroidアプリ開発 Kotlin編 」他、著書多数

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

WINGSプロジェクト 齊藤 新三(サイトウ シンゾウ)

WINGSプロジェクトについて>有限会社 WINGSプロジェクトが運営する、テクニカル執筆コミュニティ(代表 山田祥寛)。主にWeb開発分野の書籍/記事執筆、翻訳、講演等を幅広く手がける。2018年11月時点での登録メンバは55名で、現在も執筆メンバを募集中。興味のある方は、どしどし応募頂きたい。著書記事多数。 RSS X: @WingsPro_info(公式)、@WingsPro_info/wings(メンバーリスト) Facebook <個人紹介>WINGSプロジェクト所属のテクニカルライター。Web系製作会社のシステム部門、SI会社を経てフリーランスとして独立。屋号はSarva(サルヴァ)。HAL大阪の非常勤講師を兼務。

※プロフィールは、執筆時点、または直近の記事の寄稿時点での内容です

この記事は参考になりましたか?

この記事をシェア

  • X ポスト
  • このエントリーをはてなブックマークに追加
CodeZine(コードジン)
https://codezine.jp/article/detail/18397 2023/10/05 11:14

おすすめ

アクセスランキング

アクセスランキング

イベント

CodeZine編集部では、現場で活躍するデベロッパーをスターにするためのカンファレンス「Developers Summit」や、エンジニアの生きざまをブーストするためのイベント「Developers Boost」など、さまざまなカンファレンスを企画・運営しています。

新規会員登録無料のご案内

  • ・全ての過去記事が閲覧できます
  • ・会員限定メルマガを受信できます

メールバックナンバー

アクセスランキング

アクセスランキング