できない.dev

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 関数で取り替える必要がある。

よくある原因

  1. Options API から移行する際、mounted()<script setup> に貼り付けて this.$el を参照している
  2. defineProps を書かずに this.foo で props にアクセスしようとしている
  3. this.$emit('change', val) を書いて TypeScript が any 推論のままになり、実行時に TypeError: Cannot read properties of undefined が出る
  4. 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 は使えない。
各コンポーザブル経由で取得する。

この記事は役立ちましたか?