Vue 3 の <script setup> で this が使えない
<script setup> はモジュールスコープで実行されるため Options API の this が存在しない。
テンプレート参照は ref、props は defineProps、ライフサイクルは onMounted などの import 関数で置き換える。
#vue3#script-setup#composition-api#this
公開:
要約
<script setup> で書かれた SFC は内部的にコンポーネントの setup() 関数として動き、トップレベルがモジュールスコープになる。
Options API のような暗黙の this は存在しないため、ライフサイクル・props・emit・refs・ルーターはすべて Composition API の import 関数で取り替える必要がある。
よくある原因
- Options API から移行する際、
mounted()を<script setup>に貼り付けてthis.$elを参照している definePropsを書かずにthis.fooで props にアクセスしようとしているthis.$emit('change', val)を書いて TypeScript がany推論のままになり、実行時にTypeError: Cannot read properties of undefinedが出るvue-routerのナビゲーションをthis.$router.push(...)の感覚で書いている
解決策
1. テンプレート参照は ref で受ける
<script setup lang="ts">
import { ref, onMounted } from 'vue'
const inputRef = ref<HTMLInputElement | null>(null)
onMounted(() => {
inputRef.value?.focus()
})
</script>
<template>
<input ref="inputRef" />
</template>テンプレート側の ref="inputRef" 属性名と、<script setup> 内の変数名を一致させる。
2. props と emit はコンパイラマクロを使う
<script setup lang="ts">
const props = defineProps<{ msg: string }>()
const emit = defineEmits<{ (e: 'update', value: string): void }>()
function onInput(v: string) {
emit('update', v)
}
</script>defineProps / defineEmits はコンパイラマクロのため import 不要で、<script setup> 内に直接書ける。
詳細は 公式の <script setup> リファレンス を参照。
3. ライフサイクルは onMounted などをインポート
import { onMounted, onUnmounted } from 'vue'
onMounted(() => {
console.log('mounted')
})
onUnmounted(() => {
console.log('unmounted')
})Options API の mounted() / beforeUnmount() などは、すべて on* 関数として個別にインポートする。
4. ルーターは useRoute / useRouter
import { useRoute, useRouter } from 'vue-router'
const route = useRoute()
const router = useRouter()
function goHome() {
router.push('/')
}Composition API では this.$route / this.$router は使えない。
各コンポーザブル経由で取得する。