🌊

Nuxt.jsあるいはVue.jsで画面サイズをリアルタイムに取得し利用する

2024/07/22に公開

これはなに

Vue.jsのコンポーザブル(Composable)機能を使うと、画面サイズをリアルタイムに取得する関数を簡単に実装し、複数のコンポーネントで再利用できるという話。

画面サイズをリアルタイムに取得し利用する

たとえば、画面の幅に応じてプレースホルダーの文章を変更する機能を単純に実装すると、以下のようになる。

search.vue
<script setup lang="ts">
  // 画面サイズを格納しておく変数
  const windowSize = ref({
    width: 0,
    height: 0,
  });

  // 格納された画面サイズの値を変更する関数
  const onResize = (): void => {
    windowSize.value = {
      width: window.innerWidth,
      height: window.innerHeight,
    };
  };

  // 画面サイズが変更されるたびに`onResize`関数が呼ばれるように設定
  onMounted(() => {
    onResize();
    window.addEventListener('resize', onResize);
  });

  // 使わなくなったらイベントリスナーを削除
  onUnmounted(() => {
    window.removeEventListener('resize', onResize);
  });

  // 画面の幅に応じてプレースホルダーの文章を変更する
  const placeholderText = computed(() =>
    windowSize.value.width > 768 // md
      ? '検索を始めよう!キーワードをこちらに入力してください。'
      : 'キーワードを入力してください',
  );
</script>

この実装は動作するが、画面サイズの取得ロジックをコンポーネント内に直接記述してしまっている。そのため、複数のコンポーネントで同様の機能が必要な場合、コードの重複が発生してしまう。

Composableを利用する

Vue.jsの機能であるコンポーザブル(Composable)を利用すると、この画面サイズ取得ロジックをカプセル化し、再利用可能にできる。これにより、ロジックとUIを分離でき、複数のコンポーネントで同じロジックを使いまわせる。

まず、useWindowSize.tsというファイルを作成し、以下のようにコンポーザブルを用いてロジックをカプセル化する。この実装は、もとの実装から、画面サイズをリアルタイムに取得する部分を切り出しただけである。

composables/useWindowSize.ts
import { ref, onMounted, onUnmounted } from 'vue';

interface WindowSize {
  width: number;
  height: number;
}

export function useWindowSize(): globalThis.Ref<WindowSize> {
  // 画面サイズを格納しておく変数
  const windowSize = ref<WindowSize>({
    width: 0,
    height: 0,
  });

  // 格納された画面サイズの値を変更する関数
  const onResize = (): void => {
    windowSize.value = {
      width: window.innerWidth,
      height: window.innerHeight,
    };
  };

  // 画面サイズが変更されるたびに`onResize`関数が呼ばれるように設定
  onMounted(() => {
    onResize();
    window.addEventListener('resize', onResize);
  });

  // 使わなくなったらイベントリスナーを削除
  onUnmounted(() => {
    window.removeEventListener('resize', onResize);
  });

  return windowSize;
}

次に、コンポーネント内でこのコンポーザブルを使用する。実装は下記のようにシンプルになる。

search.vue
<script setup lang="ts">
  import { useWindowSize } from '@/composables/useWindowSize';

  const windowSize = useWindowSize();

  // 画面の幅に応じてプレースホルダーの文章を変更する
  const placeholderText = computed(() =>
    windowSize.value.width > 768 // md
      ? '検索を始めよう!キーワードをこちらに入力してください。'
      : 'キーワードを入力してください',
  );
</script>

Composeableの利点

コンポーザブルの良いところは、実装したコンポーザブルを使い回せば、複数のコンポーネントで同じロジックを利用できる点である。たとえば、上記のsearch.vue以外で画面サイズをリアルタイムに取得したくなった場合でも、useWindowSizeを呼び出すだけでそのロジックを再利用できる。

この実装だけで同じロジックを使いまわせる!便利!
<script setup lang="ts">
  import { useWindowSize } from '@/composables/useWindowSize';

  const windowSize = useWindowSize();
</script>

公式サイトにはマウストラッカーの例があるが、こういったなんらかの状態を持つロジックをカプセル化できるのが、コンポーザブルの強みである。

また、UIロジックとビジネスロジックを分離できるため、テストも容易になる。

まとめ

Vue.jsのコンポーザブルはいいぞ。

参考文献・URL

Discussion