コンポジションAPIで$refsを使用する方法
Vue 3のコンポジションAPIを使用する際に、テンプレート参照(template refs)を適切に取得する方法について解説します。
問題点
Vue 2のOptions APIでは、this.$refs
を使用して簡単に子コンポーネントやDOM要素への参照を取得できました:
mounted() {
console.log(this.$refs.table.temp());
}
しかし、コンポジションAPIではthis
が利用できないため、異なるアプローチが必要です。
基本的な解決策
単一要素の参照
コンポジションAPIでは、ref()
関数を使用してテンプレート参照を作成します:
<template>
<div ref="table"/>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const table = ref(null);
onMounted(() => {
console.log(table.value); // DOM要素またはコンポーネントインスタンス
});
return { table };
}
}
</script>
コンポーネントのメソッド呼び出し
子コンポーネントのメソッドを呼び出す場合:
<!-- 親コンポーネント -->
<template>
<ChildComponent ref="childRef"/>
</template>
<script>
import { ref, onMounted } from 'vue';
import ChildComponent from './ChildComponent.vue';
export default {
components: { ChildComponent },
setup() {
const childRef = ref(null);
onMounted(() => {
childRef.value.someMethod(); // 子コンポーネントのメソッドを呼び出し
});
return { childRef };
}
}
</script>
<!-- 子コンポーネント -->
<script>
export default {
setup() {
const someMethod = () => {
console.log('子コンポーネントのメソッドが呼ばれました');
};
return { someMethod };
}
}
</script>
注意
参照はコンポーネントがマウントされた後にのみアクセス可能です。onMounted
ライフサイクルフック内や、それ以降のタイミングで参照を使用してください。
複数要素の参照
v-for
ディレクティブで複数の要素を参照する場合、いくつかの方法があります。
方法1: 配列での参照(Vue 3.2.25〜3.2.31の問題あり)
<template>
<div v-for="(item, i) in items" ref="elements" :key="item.id">
<div>ID: {{ item.id }}</div>
<div>Name: {{ item.name }}</div>
</div>
</template>
<script>
import { ref, onMounted } from 'vue';
export default {
setup() {
const items = [
{id: 1, name: "item name 1"},
{id: 2, name: "item name 2"},
{id: 3, name: "item name 3"},
];
const elements = ref([]);
onMounted(() => {
console.log(elements.value.map(el => el.textContent));
});
return { elements, items };
}
}
</script>
注意
Vue 3.2.25から3.2.31のバージョンでは、この方法で参照の配列が空になる問題があります。
方法2: 関数参照(推奨)
互換性の高い方法として、関数参照を使用します:
<template>
<div
v-for="(item, i) in items"
:ref="el => elements[i] = el"
:key="item.id"
>
<div>ID: {{ item.id }}</div>
<div>Name: {{ item.name }}</div>
</div>
</template>
TypeScriptでの型付け
TypeScriptを使用する場合、適切な型を指定します:
import { ref, onMounted } from 'vue';
const table = ref<HTMLDivElement | null>(null);
// またはコンポーネントの型を指定
// const childComponent = ref<InstanceType<typeof ChildComponent> | null>(null);
onMounted(() => {
if (table.value) {
// 型安全なアクセス
table.value.addEventListener('click', () => console.log("クリックされました"));
}
});
条件付きレンダリングの考慮
参照がv-if
などで条件付きにレンダリングされる場合、参照がnull
になる可能性があります:
<template>
<div v-if="isVisible">
<div ref="conditionalRef"/>
</div>
</template>
<script>
import { ref, watch } from 'vue';
export default {
setup() {
const isVisible = ref(false);
const conditionalRef = ref(null);
watch(isVisible, (newValue) => {
if (newValue && conditionalRef.value) {
// 参照が利用可能になった
console.log(conditionalRef.value);
}
});
return { isVisible, conditionalRef };
}
}
</script>
Vue 3.5+での最新アプローチ
Vue 3.5以降では、useTemplateRef
関数が利用可能です(実験的機能):
<script setup>
import { useTemplateRef } from 'vue';
const tableRef = useTemplateRef('table');
</script>
<template>
<div ref="tableRef">コンテンツ</div>
</template>
まとめ
コンポジションAPIでのテンプレート参照の基本:
ref()
で参照を作成し、テンプレートで同じ名前を指定onMounted
以降で参照を利用- 条件付きレンダリングの場合、参照の可用性を確認
- TypeScriptでは適切な型を指定
- 複数要素の参照には関数参照が確実
これらの方法を適切に使い分けることで、コンポジションAPIでもOptions APIと同様にテンプレート参照を効果的に利用できます。