🤨

まだ.value書くのダルいって言ってるの?

2023/10/27に公開

はじめに

Vue.jsのComposition APIは、Vueのコンポーネントのロジックをより明確に、再利用可能にする新しいアプローチを提供しています。しかし、この新しいAPIには学ぶべき多くの概念やメソッドが含まれており、初めて触れる方や従来のOptions APIに慣れている方にとっては少し難解に感じるかもしれません。

その中でも、refやreactiveなどのreactivityを管理するメソッドに加えて、unref()という関数も重要な役割を果たしています。しかし、このunref()は名前から直接的な役割や機能が推測しづらく、多くのVue開発者がこの関数の真価を見過ごしているかもしれません。

Composition APIとは

従来のVue開発ではOptions APIを使用し、data, methods, computedなどのオプション内でコンポーネントのロジックを定義してきました。これは特定のオプションごとに機能を組み立てるアプローチでした。

対照的に、Composition APIは機能やロジックごとにコンポーネントを組み立てる焦点を持っています。これにより、ロジックの再利用や大規模なコンポーネントの管理が容易になります。

.value書くのは面倒くさい

Composition APIを使用すると、リアクティブな値を操作するために頻繁に.valueを使用する必要が出てきます。

この.valueの使用は、小さなコンポーネントやアプリケーションでは大きな問題にはなりません。しかし、アプリケーションの規模が大きくなり、多くのリアクティブな値やロジックが存在する場合、この.valueがコード中に頻繁に現れることになります。

特に、多くの計算プロパティやウォッチャが存在する場合、その中で多数の.valueを使用することはコードの可読性を低下させる可能性があります。そして、コードの可読性が低下すると、バグの原因となる可能性が高まります。

しかし、幸いなことにunref()という関数が提供されています。この関数を使用することで、上記のような問題を効果的に解決することができます。

unref()とは

引数が ref であればその内部の値を返し、そうでなければ引数そのものを返します。
これは val = isRef(val) ? val.value : val に対するシュガー関数です。
Vue.js公式より引用

unref()の主な目的は、コードの簡潔性と可読性を向上させることです。Vueのリアクティブな値を操作する際、常に値がrefであるかどうかを確認して.valueを使用するかどうかを判断することは煩わしいものです。この手間をunref()は解消してくれます。

実際、多くのコンポーネントや関数内では、受け取る値が常にrefであるとは限りません。複数のコンポーネントや関数を再利用する際に、その都度refの確認を行うことは、コードの冗長性を増加させ、エラーの原因ともなり得ます。

unref()を使用することで、これらのリスクを劇的に減少させ、より簡潔で安全なコードを書くことが可能になります。特に、TypeScriptと組み合わせた場合、型安全な操作を保証する上で非常に助けとなります。

実際に使ってみる

今回使う関数

今回の例で取り上げる関数は、数値の階乗を計算するfactorial関数です。階乗は、与えられた数値から1までの全ての自然数の積を計算するもので、数学やプログラミングの学習においてよく取り上げられるトピックです。

export default function factorial(input: number): number {
    if (input === 0 || input === 1) {
        return 1;
    } else {
        return input * factorial(input - 1);
    }
}

この関数は再帰的な方法で階乗の計算を行います。例えば、3の階乗は3 x 2 x 1 = 6となります。
このfactorial関数をComposition APIとの連携の中でどのように活用するのかを詳しく見ていきましょう。

引数の型定義を拡張する

引数の型定義の拡張は、Vue 3のComposition APIの中で一般的なパターンとなっています。この方法により、関数はリアクティブな値(例:refやcomputedによって返される値)と普通の値の両方を簡単にサポートすることができます。この柔軟性は、Composition APIを使用する際の強力なツールとなります。

function factorial(input: number | Ref<number>): number {
    const value = unref(input);
    if (value === 0 || value === 1) {
        return 1;
    } else {
        return value * factorial(value - 1);
    }
}

これでnumberだけでなくRef<number>も引数として受け取ることができるようになりました。このように、関数はより広範な使用ケースに適応することができます。

しかし、factorial()は再帰関数なので、このままではループする度にunref()を実行することになります。なので、unref()を一度だけ実行する処理に変更します。

function factorial(input: number | Ref<number>): number {
    const n = unref(input);
    const recursive = (v = n): number => {
        return v === 0 || v === 1 ? 1 : v * recursive(v - 1);
    };
    return recursive();
}

関数の再帰的な性質により、unref関数が繰り返し呼び出される問題が発生していましたが、この問題は、再帰処理を内部関数として切り出すことで解消されました。この変更により、unrefの呼び出しは1回だけに制限され、関数の効率が向上しました。

さらにまとめる(コンポーザブル関数)

Vue 3のComposition APIは、コードの再利用性を向上させるための強力なツールを提供しています。ここでは、その最も注目すべき特徴の1つ、コンポーザブル関数を紹介します。

コンポーザブル関数とは、特定の機能やロジックをカプセル化して再利用可能な関数にまとめる手法です。これは、Vueの新しいComposition APIをフルに活用したもので、複数のコンポーネントで共有することが可能です。

export default function useFactorial(input: number | Ref<number>) {
    return computed(() => {
        const n = unref(input);
        const recursive = (v = n): number => {
            return v === 0 || v === 1 ? 1 : v * recursive(v - 1);
        };
        return recursive();
    });
}

SFC内での使用イメージはこんな感じです。
スタイルはTailwindCSSを使っています。

<script lang="ts" setup>
const input = ref(5);
const factorial = useFactorial(input);
</script>
<template>
    <div class="p-10">
        <div class="mb-4">
            <label> 値を入力 </label>
            <input v-model.number="input" type="number" class="border rounded p-2" />
        </div>
        <div>
            <p>{{ input }}の階乗は{{ factorial }}です。</p>
        </div>
    </div>
</template>

  1. 命名慣習: use プレフィックスは、関数がコンポーザブル関数であることを明示的に示すための命名慣習です。これにより、コードを読む際にその関数の役割や用途が一目でわかります。

  2. カプセル化: コンポーザブル関数は、特定のロジックやステートをカプセル化することができます。この例では、useFactorial は階乗を計算するロジックをカプセル化しています。

  3. 可読性の向上: コンポーザブル関数を使用することで、コードの可読性が大幅に向上します。具体的には、リアクティブな変数の取り扱いや、計算ロジックなどの詳細が関数内に隠蔽され、主要な部分だけが明確に表示されるようになります。

まとめ

この記事を通じて、unref()を中心とした一連の手法を取り上げ、コンポーザブル関数への導入をステップバイステップで進める方法を示しました。

  1. .valueの冗長性: Vueのリアクティブな参照の取り扱いにおいて、.valueの使用は時に冗長に感じられるものでした。unref()を使うことでこの冗長性を解消し、よりクリーンなコードを書く手助けとなります。

  2. 型の柔軟性の向上: unref()の導入により、通常の値とリアクティブな参照の両方を効果的に扱うことができるようになり、関数やコンポジションの再利用性が飛躍的に向上します。

  3. コンポーザブル関数へのステップアップ: 最初の単純な関数から、unref()を活用して引数の型を拡張し、最終的にはコンポーザブル関数の導入まで、ステップバイステップでの進化を体験しました。この一連のステップは、コードの再利用性とメンテナンス性を高めるための重要な要素です。

  4. 可読性と効率の向上: 最終的には、unref()とコンポーザブル関数を組み合わせることで、コードの可読性が大幅に向上し、開発効率も高まります。

Discussion