Vue.jsでref属性を利用してResizeObserverを使用する

# はじめに
要素の幅が動的に切り替わる画面を実装していて、ResizeObserverを利用する時にVue.jsのref属性を利用して取得した要素の情報を利用しました。
ネットで検索しても全く情報が出てこなかったので、同じようにVue.jsで実装をしていてResizeObserverを使用する方の為に情報を残します。

## ResizeObserverについて
ResizeObserverはJavaScriptが用意している標準の機能なので、ライブラリを読み込む等のことはせず利用出来ます。
特定の要素を渡すことで、特定の要素の変化を検知してコールバック関数を呼び出すことが出来ます。

以下サンプルです。
```js
const resizeObserver = new ResizeObserver((entries) => {
    // idがsampleの要素のサイズが変更される度に、idがsampleの要素の横幅を出力する
    console.log(entries[0].contentRect.width); 
});
resizeObserver.observe(document.getElementById("sample"));
```

まず`ResizeObserver`インスタンスを用意して、要素の変更行われた際に実行される処理をコールバックで定義します。

インスタンスを変数に格納して、監視したいElementを渡してobserveメソッドを実行することで、指定の要素のサイズ変更が行われる度にコールバックに定義した処理が実行されます。

---
サンプルでは`document.getElementById()`を実行することでElementを取得していましたが、Vueではref属性を利用して要素にアクセスすることが出来るのでref属性を利用した時のResizeObserverの使い方を紹介します。

## Options API
まずはVue2でお馴染みの、Options APIを利用している場合において、ref属性を利用してResizeObserverを使用する方法を紹介します。
### DOM要素にref属性を付与
まずはシンプルにref属性をdivタグ等のDOM要素に付与します。
```html
<div ref="sample"></div>
```
```js
const resizeObserver = new ResizeObserver((entries) => {
    console.log(entries[0].contentRect.width)
});
resizeObserver.observe(this.$refs.sample);
```

上記のように設定することでref属性がsampleのdiv要素の変更を検知してコールバックを実行します。

### コンポーネントにref属性を付与
次に、子コンポーネント等のコンポーネントに対しref属性を付与した場合です。

```vue
<Sample ref="sample></Sample>
```
```js
const resizeObserver = new ResizeObserver((entries) => {
    console.log(entries[0].contentRect.width);
});
resizeObserver.observe(this.$refs.sample.$el);
```
コンポーネントに対しref属性を付与した場合は、`$el`プロパティにアクセスすることでコンポーネントのElementをアクセスすることが出来ます。

**注意**
もし仮に$elを利用せず以下のように要素を渡した場合、エラーが出るのでご注意ください
```js
resizeObserver.observe(this.$refs.sample);
```
```bash
Failed to execute 'observe' on 'ResizeObserver': parameter 1 is not of type 'Element'.
```

## Composition API
Vue3でComposition APIが使えるようになり、今はこちらの書き方で書かれている人の方が多いと思います。Composition APIの場合のResizeObserverの使い方を紹介します。

Vue3でref属性をする方法については以前記事に残したので参考にしてください。

[Vue3×TypeScript×Composition APIで、子コンポーネントにref属性を利用してアクセスする
](https://qiita.com/manzoku_bukuro/items/f438a7d5cafdb41ce0ac)

### DOM要素にref属性を付与
特定のdiv要素のサイズを監視しようと思います。その場合型定義では`HTMLDivElement`を定義します。span要素の場合は`HTMLSpanElement`にする等、監視する要素に合わせて型を変更してください。
```vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';

const sampleDom = ref<HTMLDivElement | null>(null);
onMounted(() => {
    const resizeObserver = new ResizeObserver((entries) => {
        console.log(entries[0].contentRect.width);
    });
    if (sampleDom.value) {
        resizeObserver.observe(sampleDom.value);
    }
})
</script>

<template>
  <div>
    <div ref="sampleDom"></div>
  </div>
</template>
```

### コンポーネントにref属性を付与
最後に、子コンポーネント等のコンポーネントに対しref属性を付与した場合です。
```vue
<script setup lang="ts">
import { ref, onMounted } from 'vue';
import Sample from '@/components/Sample.vue';

const sampleComponent = ref<InstanceType<typeof Sample> | null>(null);
onMounted(() => {
    const resizeObserver = new ResizeObserver((entries) => {
        console.log(entries[0].contentRect.width);
    });
    if (sampleComponent.value) {
        resizeObserver.observe(sampleComponent.value.$el);
    }
})
</script>

<template>
  <div>
    <Sample ref="sampleComponent"></Sample>
  </div>
</template>
```

Composition APIの場合でも、コンポーネントをref属性で取得した場合は$elプロパティにアクセスすることでコンポーネントのElementを取得出来ます。

# おわりに
Vue.jsにおいてOptions APIとComposition APIの場合でref属性を利用してResizeObserverを使用する方法を紹介しました。
せっかくフレームワークを使っているのであれば、フレームワークの機能を利用した方がツールを使いこなしてる感があるので、私はこの要素の取得方法を気に入ってます。

---
[Twitter](https://twitter.com/manzoku_bukuro)もやってます