- defineProps や defineEmits をなぜimportせずに使えるのか疑問に思っている
- Compiler Macros(コンパイラマクロ)という言葉を見かけたが、何のことか分からない
Vue 3 の <script setup> を書いていると、defineProps や defineEmits が 何もimportしていないのに使えることに気づきます。 通常、外部の関数を使う場合は import { ref } from 'vue' のように明示的なimportが必要なはずです。 なぜこれらだけimportが不要なのでしょうか?
その答えが Compiler Macros(コンパイラマクロ) という仕組みです。 この記事では、Compiler Macrosとは何か・何のために存在するのかを、解説します。
defineProps・defineEmitsにimportが不要な理由
結論: defineProps と defineEmits は、実行時に呼び出される通常の関数ではなく、 コンパイル時にVueコンパイラが変換するマクロ(Compiler Macros) だからです。 実際のJavaScript関数として存在しないため、importする必要がありません。
通常の関数(例:ref)は、ブラウザでのJavaScript実行時にも実体として存在する関数です。
// ref は実行時に存在するので import が必要
import { ref } from 'vue'
const count = ref(0)一方、defineProps や defineEmits は <script setup> の中でしか使えません。 コンパイラがコードをビルドする段階で別のコードに変換されてしまうため、 ブラウザが実行するJavaScriptには残っていないのです。
// ✅ import なしで使える
<script setup>
const props = defineProps({
title: String,
count: Number
})
const emit = defineEmits(['update', 'delete'])
</script>もし <script setup> の外で使おうとするとエラーになります。これがマクロである証拠です。
Compiler Macros(コンパイラマクロ)とは
Compiler Macros(コンパイラマクロ)とは、VueのSFCコンパイラ(Single File Componentのビルドツール)が 特別に認識し、コンパイル時に別のコードへと変換するシンタックスのことです。
プログラミングにおける「マクロ」とは、もともと「コード変換の仕組み」を指します。 CやLispでのマクロが有名ですが、Vueにおいても同様に「書いたコードが別のコードに変換される」という動作をします。
主なCompiler Macros一覧
| マクロ名 | 役割 | 使用可能な場所 |
|---|---|---|
defineProps() | コンポーネントのpropsを定義する | <script setup> のみ |
defineEmits() | コンポーネントが発行するイベントを定義する | <script setup> のみ |
defineExpose() | 親コンポーネントへ公開するプロパティを指定する | <script setup> のみ |
withDefaults() | definePropsにデフォルト値を設定する | <script setup> のみ |
defineOptions() | コンポーネントオプション(name等)を定義する | <script setup> のみ(Vue 3.3+) |
Compiler Macrosは何のためにあるのか
Compiler Macrosが存在する理由は主に2つです。
① <script setup> をシンプルに書けるようにするため
<script setup> が登場する前は、defineComponent を使ってより冗長な書き方をする必要がありました。
// <script setup> 以前の書き方
export default defineComponent({
props: {
title: String,
count: Number
},
emits: ['update', 'delete'],
setup(props, { emit }) {
// ロジック...
return {}
}
})Compiler Macrosによって、同じことをより直感的に書けるようになりました。
// <script setup> + Compiler Macros を使った書き方
<script setup>
const props = defineProps({
title: String,
count: Number
})
const emit = defineEmits(['update', 'delete'])
</script>記述量が減るだけでなく、TypeScriptとの親和性も上がり、型推論も効きやすくなっています。
② コンパイル時の型安全を実現するため
TypeScriptを使う場合、defineProps にジェネリクスで型を渡すことができます。 これはコンパイル時のみ意味を持つ記法であり、まさにマクロならではの表現です。
<script setup lang="ts">
// ジェネリクスによる型定義(TypeScript専用の書き方)
const props = defineProps<{
title: string
count: number
}>()
</script>通常の関数では、ジェネリクスは実行時の型情報として扱われます。 しかしコンパイラマクロとして処理することで、 型情報をそのままコンポーネントのpropsバリデーションに変換できるのです。
注意:<script setup> 以外では使えない
Compiler Macrosはあくまでコンパイラが処理する特殊な構文です。 <script setup> の外や、通常の .js / .ts ファイルでは使用できません。
// ❌ これはエラーになる
import { defineProps } from 'vue' // defineProps は export されていない
// ❌ <script setup> 以外では使えない
<script>
export default {
setup() {
const props = defineProps({ title: String }) // エラー
}
}
</script>
NuxtでもCompiler Macrosは同じように使える
NuxtはVue 3をベースにしているため、Compiler Macrosはまったく同じように使用できます。 さらにNuxtは独自の自動インポート機能も持っており、 ref や computed といった Vue Composition API の関数も 明示的なimportなしで使えるケースがあります。
ただし、これはNuxtの「Auto Import」機能によるものであり、Compiler Macrosとは別の仕組みです。 混同しないよう注意しましょう。
まとめ
最後まで読んでいただき、ありがとうございました!
今回は、「Nuxt/VueでdefineProps・defineEmitsのimportが不要な理由」についてご紹介しました。
ポイントを整理すると、以下のとおりです。
defineProps/defineEmitsはCompiler Macros(コンパイラマクロ)であり、コンパイル時に別のコードへ変換される- 実行時に実体として存在する関数ではないため、importは不要(というよりできない)
- Compiler Macrosは
<script setup>構文を簡潔に書けるようにするために設計されている - NuxtのAuto ImportとCompiler Macrosは別物であり、混同しないことが大切
「なんとなく使えてるからいいか」で済ませるのも一つの選択ですが、 こうした仕組みを理解しておくと、エラーの原因を特定したり、 より適切なコードを書いたりする力につながります。
