Vue 3 Composition API:参照またはリアクティブ





今、私がこの記事を書いているとき、私たちはVue 3のリリースに近づいています。私の意見では、最も興味深いのは、他の開発者がそれをどのように認識して使用するかを観察することです。過去数か月でVue3をいじる機会がありましたが、そうでない人もいることは知っています。



新しいバージョンで最も重要な革新は、CompositionAPIです。これは、コンポーネントを作成するための代替アプローチを提供し、既存のオプションAPIとは大きく異なります。私が最初に彼に会ったとき、私は彼を理解していなかったことを認めるのは難しいことではありません。しかし、それが適用されるにつれて、意味が現れ始めました。 Composition APIを使用してアプリケーション全体を書き直すわけではないかもしれませんが、この記事では、コンポーネントの作成と構成がどのように機能するかを考える機会を提供します。







最近、いくつかのプレゼンテーションを行いましたが、よくある質問は、Refを使用する場合と、Reactiveを使用してリアクティブプロパティを宣言する場合です。良い答えがなく、答えを見つけるのに数週間かかりました。この記事は私の調査の結果です。



また、上記は私の意見であり、「この方法でのみ」行う必要があるとは思わないでください。誰かが別の方法でアドバイスするまで、または自分でより良いアプローチを見つけるまで、この方法でRefとReactiveを使用する予定です。新しい技術を理解するには時間がかかると思います。そうすると、実績のある技術が現れるかもしれません。



先に進む前に、少なくとも簡単にコンポジションAPIに精通し、それが何で構成されているかを理解していることを前提としています。この記事では、API構成メカニズムではなく、refとreactiveの違いに焦点を当てています。



Vue2の反応性



理解を深めるために、Vue 2アプリケーションでリアクティブプロパティを作成する方法を簡単に説明します。Vue2でプロパティの変更を追跡する場合は、data関数によって返されるオブジェクトでそれらを宣言する必要があります。



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    data() {
      return {
        title: "Hello, Vue!"
      };
    }
  };
</script>


Vue 2の内部では、変更を追跡するためのゲッターとセッターを作成するために、各プロパティに対してObject.defineProperty()が呼び出されます。これはプロセスの最も簡単な説明であり、私はその考えを伝えたいと思います。それには魔法はありません。どこにもリアクティブプロパティを作成して、Vueがそれらへの変更を追跡することを期待することはできません。データ関数でリアクティブプロパティを設定する必要があります。



REFおよびREACTIVE



オプションAPIを使用する場合、リアクティブプロパティを宣言するときにいくつかのルールに従う必要があります。これは、コンポジションAPIを使用する場合にも当てはまります。プロパティを作成して反応性を期待することはできません。次の例では、titleプロパティを宣言し、setup()関数がそれを返すことで、テンプレートで使用できるようにします。



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";
      return { title };
    }
  };
</script>




これは機能しますが、titleプロパティはリアクティブではありません。それら。誰かがタイトルを変更した場合、それらの変更はハウスに反映されません。たとえば、5秒後にタイトルを変更した場合、以下のコードはホームを変更しません。



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  export default {
    setup() {
      let title = "Hello, Vue 3!";

      setTimeout(() => {
        title = "THIS IS A NEW TITLE";
      }, 5000);

      return { title };
    }
  };
</script>




refをインポートし、それを使用してプロパティをリアクティブにすることができます。内部的には、Vue3はプロキシを作成します



<template>
  <h1>{{ title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const title = ref("Hello, Vue 3!");

      setTimeout(() => {
        //   ,   .value ...
        //   
        title.value = "New Title";
      }, 5000);

      return { title };
    }
  };
</script>




RefとReactiveについて言えば、2つの異なるケースがあると思います。1つ目は、上記の例のようにコンポーネントを作成することであり、リアクティブプロパティが必要です。2つ目は、コンポーネントや関数でコンポジションを使用できるようにする関数を作成する場合です。この記事では、両方のシナリオについて説明します。



REF



単純型のプロパティを作成する場合、ref()が最初の選択肢です。それは特効薬ではありませんが、始める価値があります。JavaScriptの7つのプリミティブタイプを思い出させてください。



  • ストリング
  • BigInt
  • ブール
  • シンボル
  • ヌル
  • 未定義




import { ref } from "vue";

export default {
  setup() {
    const title = ref("");
    const one = ref(1);
    const isValid = ref(true);
    const foo = ref(null);
  }
};




前の例では、タイトルはString型であるため、プロパティをリアクティブにするために、ref()を選択します。以下のコードがあなたにいくつかの質問を引き起こしているなら、心配しないでください、私は同じ質問をしました。



import { ref } from "vue";

export default {
  setup() {
    const title = ref("Hello, Vue 3!");

    setTimeout(() => {
      title.value = "New Title";
    }, 5000);

    return { title };
  }
};




タイトルが変わるのになぜconstを使うのですか?letを使ってみませんか?コンソールにタイトルを印刷すると、Hello、Vue 3!が表示されると思われるかもしれませんが、代わりにオブジェクトが表示されます。



{_isRef: true}
value: (...)
_isRef: true
get value: ƒ value()
set value: ƒ value(newVal)
__proto__: Object




ref()関数は引数を取り、リアクティブで可変のrefオブジェクトを返します。Refオブジェクトには、引数を参照する1つのプロパティvalueがあります。つまり、値を取得または変更する場合は、title.valueを使用する必要があります。これは変更されないオブジェクトであるため、constと宣言しました。



REFの呼び出し



次の質問は、テンプレートでtitle.valueを呼び出さない理由です。



<template>
  <h1>{{ title }}</h1>
</template>




Refがレンダリングコンテキスト(setup()関数によって返されるオブジェクト)のプロパティとして返され、テンプレートで参照されると、Refは自動的に値を返します。テンプレートに.valueを追加する必要はありません。



計算されたプロパティは同じように機能します。setup()関数内で、それらを.valueと呼びます。




反応性



ref()を使用して、単純なタイプのプロパティをリアクティブにする方法を見てきました。リアクティブオブジェクトを作成したい場合はどうなりますか?ref()を使用することもできますが、内部ではVueはreactive()を使用するため、reactive()を使用します。



一方、reactive()はプリミティブ型では機能しません。リアクティブ()関数はオブジェクトを受け取り、元のリアクティブプロキシを返します。これは、Vue 2の.observable()と同等であり、この関数名は、RxJSのobservableとの混同を避けるために変更されました。



import { reactive } from "vue";

export default {
  setup() {
    const data = reactive({
      title: "Hello, Vue 3"
    });

    return { data };
  }
};




主な違いは、テンプレートでリアクティブオブジェクトを参照する方法です。前の例では、dataはtitleプロパティを含むオブジェクトです。次のようなテンプレートで参照する必要があります--data.title:



<template>
  <h1>{{ data.title }}</h1>
</template>

<script>
  import { ref } from "vue";

  export default {
    setup() {
      const data = ref({
        title: "Hello, Vue 3"
      });

      return { data };
    }
  };
</script>




コンポーネントのREFとREACTIVEの違い



私たちが議論したことに基づいて、答えはそれ自体を示唆しているように思われますか?単純型にはref()を使用し、オブジェクトにはreactive()を使用します。コンポーネントの作成を開始したとき、これが常に当てはまるとは限らないことが判明し、ドキュメントには次のように記載されています。



refとreactiveの使用の違いは、JavaScriptで標準のプログラムロジックを作成する方法にある程度匹敵する可能性があります。




私はこのフレーズを熟考し、次の結論に達しました。アプリケーションが大きくなるにつれて、次のプロパティが得られました。



export default {
  setup() {
    const title = ref("Hello, World!");
    const description = ref("");
    const content = ref("Hello world");
    const wordCount = computed(() => content.value.length);

    return { title, description, content, wordCount };
  }
};




JavaScriptでは、これらはすべて私のページのプロパティであることを理解します。この場合、それらをページオブジェクトにグループ化しますが、今すぐ同じことをしてみませんか。



<template>
  <div class="page">
    <h1>{{ page.title }}</h1>
    <p>{{ page.wordCount }}</p>
  </div>
</template>

<script>
  import { ref, computed, reactive } from "vue";

  export default {
    setup() {
      const page = reactive({
        title: "Hello, World!",
        description: "",
        content: "Hello world",
        wordCount: computed(() => page.content.length)
      });

      return { page };
    }
  };
</script>




これはRefまたはReactiveに対する私のアプローチですが、ご意見をお聞かせいただければ幸いです。あなたは同じことをしますか?多分これは正しいアプローチではありませんか?コメントしてください。



構成ロジック



コンポーネントでref()またはreactive()を使用する場合、間違いはありません。どちらの関数もリアクティブデータを作成します。セットアップ()関数とテンプレートでデータにアクセスする方法を理解していれば、問題はありません。



合成関数を書き始めるときは、違いを理解する必要があります。ニュアンスをよく説明しているものもあるので、RFCドキュメントの例を使用します。



マウスの位置を追跡するためのロジックを作成する必要があります。また、必要に応じて、どのコンポーネントでもこれと同じロジックを使用できるはずです。 x座標とy座標を追跡し、それらをクライアントコードに返す合成関数を作成します。



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const x = ref(0);
  const y = ref(0);

  function update(e) {
    x.value = e.pageX;
    y.value = e.pageY;
  }

  onMounted(() => {
    window.addEventListener("mousemove", update);
  });

  onUnmounted(() => {
    window.removeEventListener("mousemove", update);
  });

  return { x, y };
}




コンポーネントでこのロジックを使用してこの関数を呼び出す場合は、返されたオブジェクトを分解してから、x座標とy座標をテンプレートに返します。



<template>
  <h1>Use Mouse Demo</h1>
  <p>x: {{ x }} | y: {{ y }}</p>
</template>

<script>
  import { useMousePosition } from "./use/useMousePosition";

  export default {
    setup() {
      const { x, y } = useMousePosition();
      return { x, y };
    }
  };
</script>




これは機能しますが、関数を確認した後、xとyの代わりに位置オブジェクトをリファクタリングして返すことにした場合:



import { ref, onMounted, onUnmounted } from "vue";

export function useMousePosition() {
  const pos = {
    x: 0,
    y: 0
  };

  function update(e) {
    pos.x = e.pageX;
    pos.y = e.pageY;
  }

  // ...
}




このアプローチの問題は、反応性が持続するために、構成関数のクライアントが常に返されたオブジェクトへの参照を持っている必要があることです。これは、オブジェクトを分解したり、スプレッド演算子を適用したりできないことを意味します。



//  
export default {
  setup() {
    //  !
    const { x, y } = useMousePosition();
    return {
      x,
      y
    };

    //  !
    return {
      ...useMousePosition()
    };

    //     
    //   `pos`  ,      x  y : `pos.x`  `pos.y`
    return {
      pos: useMousePosition()
    };
  }
};




ただし、この場合、リアクティブを使用できないという意味ではありません。リアクティブオブジェクトを単純なオブジェクトに変換するtoRefs()関数があり、その各プロパティは、元のオブジェクトの対応するプロパティの参照です。



function useMousePosition() {
  const pos = reactive({
    x: 0,
    y: 0
  });

  // ...
  return toRefs(pos);
}

// x & y  ref!
const { x, y } = useMousePosition();




したがって、合成関数を作成するときに考慮すべきいくつかの側面があります。クライアントコードが関数でどのように機能するかを理解していれば、すべて問題ありません。



合計



私が最初にCompositionAPIを使い始めたとき、いつref()を適用するのか、いつreactive()を適用するのかという質問に戸惑いました。たぶん私はまだそれを正しくやっていない、そして誰かが私にこれを言うまで、私はこのアプローチに固執するでしょう。うまくいけば、いくつかの問題を明確にするのに役立ち、あなたのフィードバックを聞いてみたいです。



ハッピーコーディング



All Articles