Vue3で登場したComposition APIがNuxt3のデフォルトっぽい。
ただだいぶ書き方が変わったのでそのあたりを調べたときの備忘録。
Vue2からある書き方は、Options API。
<script setup>構文
新しく増えた<script setup>
はこんな感じ。
- SFC内でComposition API を使用するシンタックスシュガー
- 内部のコードは、コンポーネントの setup() 関数の内容としてコンパイルされる
- つまり、コンポーネントのインスタンスが作成されるたびに実行される
・Composition API | Vue.js
・SFC <script setup> | Vue.js
基本的な書き方
数字をカウントアップする例だと、
data/computed/method/watchはこんな感じ。
<script lang="ts" setup> import { ref } from "vue"; // constant const defaultCount = 1; // data const count = ref(defaultCount); // computed const plusOne = computed(() => count.value + 1); // watch watch(count, (count, prevCount) => { console.log(`watch: ${prevCount} -> ${count}`); }); // method function countUp() { data.count++; } </script> <template> <div> <div>count: {{ count }}</div> <div>plusOne: {{ plusOne }}</div> <div> <button @click="countUp">COUNT UP</button> </div> </div> </template>
変数/定数
Vue3でReactivity APIが追加され、
値を監視し、変更を検知するときの扱いが変わった。
従来のように変数に値を代入するだけでは変更が反映されず、
Reactivity APIのref()
を使う必要がある。
// constant const defaultCount = 1; // data const count = ref(defaultCount); count.value = 2; console.log(count.value);
若干、.value
を使わないと値が参照できないので、
手間がかかるが、reactive()
を使うと楽になる。
// data const count = ref(1); const data = reactive({ count }) // `data.count` が更新される count.value++ console.log(count.value) // 2 console.log(data.count) // 2 // `count` の ref も更新される data.count++ console.log(data.count) // 3 console.log(count.value) // 3
computed
computedもReactivity APIを使う形に。
const count = ref(1); // computed const plusOne = computed(() => count.value + 1);
従来どおり、getter/setterを使う形でもOK。
const count = ref(1); // computed: setter/getter const plusOne = computed({ get: () => count.value + 1, set: val => count.value = val - 1 })
watch
watchもリアクティブな変数が対象になる。
const count = ref(1); // watch watch(count, (count, prevCount) => { console.log(`watch: ${prevCount} -> ${count}`); });
また、直接refではなく、getter関数でもOK。
// ゲッタを監視 const state = reactive({ count: 0 }); watch( () => state.count, (count, prevCount) => console.log(`watch: ${prevCount} -> ${count}`) );
複数のrefを監視したい場合は、配列で指定する形。
watch([fooRef, barRef], ([foo, bar], [prevFoo, prevBar]) => { /* ... */ })
ほかにも、watchEffect/watchPostEffect/watchSyncEffectが追加され、
前の値が不要な場合は、watchEffect
で簡単に書けるように。
method
methodはシンプルで、関数を定義すればOK。
Props/Emits
PropsとEmitsは、それぞれdefineProps
/defineEmits
で定義する。
<script lang="ts" setup> // prop interface Props { count: number; loading?: boolean; } const props = withDefaults( defineProps<Props>(), { loading: false } ); // emit interface Emits { (e: 'set-count', count: number): void; } const emits = defineEmits<Emits>(); // method function onInput(e: InputEvent) { emits("set-count", Number((e.target as HTMLInputElement).value)); } </script> <template> <div class="mb-4"> <div>{{ value }}</div> <input type="number" :value="props.count" @input="onInput" /> </div> </template>
Props
propsはdefineProps
から取得できる。
初期値も必要なときは、withDefaults
を使う感じ。
// prop interface Props { count: number; loading?: boolean; } // 初期値なしのprops const props = defineProps<Props>(); // 初期値ありのprops const propsWithDefault = withDefaults(props, { loading: false });
Emits
emitsはdefineEmits
で定義が必要。
呼び出すときは返り値のemits
を使う。
// emit interface Emits { (e: 'set-count', count: number): void; } const emits = defineEmits<Emits>(); emits('set-count', 1);
カスタムコンポーネントのv-model
個人的にはかなり衝撃的な破壊的変更。。
<input>
タグは今まで通りだけど、カスタムコンポーネントでv-model
を使う場合は注意。
<input>
を含むコンポーネントとかだと、computedを使うと良さそう
<script lang="ts" setup> // props interface Props { modelValue: number; } const props = withDefaults( defineProps<Props>(), { } ); // emit interface Emits { (e: 'update:modelValue', count: number): void; } const emits = defineEmits<Emits>(); // computed const modelValue = computed({ get: () => props.modelValue, set: (value: number) => emits('update:modelValue', value) }); </script> <template> <div class="mb-4"> <div>{{ modelValue }}</div> <input type="number" v-model.number="modelValue" /> </div> </template>
複数のv-model
バインディングや修飾子などは便利。
以上!! SFC基本的な部分を一通り触れた気がする(*´ω`*)