見出し画像

Vue3のComposition APIをつかって既存のVueコンポーネントを書き換えてみた(第3回)

Vue3のComposition APIを使って、今までのコンポーネントや作りかけのコンポーネントを書き換えて来ましたが、最近、Goldrush(弊社)のメンバーもVue3のComposition APIを使ってコンポーネントを作り始めました。
Composition APIでプログラムを書き始めたメンバーからいくつか質問が来て、その質問に答えたり一緒に考えたりしたものの中から興味深かかったものを2つ共有します。


setupファンクションの中からプライベート関数を使いたいときはどうするのか?

今週、弊社のメンバーのMさんが、setup関数のなかでvee-validateを使って、<input>タグで入力される様々な情報をバリデーションする部分を書いていました。
そのページは入力項目が多くて、たくさんの入力項目ごとにvee-validateのバリデーションルールを定義、セットして行く必要がありました。
自然とその部分が数十行になってしまったので、Mさんは、なんとなく、setup()関数のなかで変数定義の部分が長くなりすぎてしまったと感じて、下のようなコードを書きました。

  setup() {
    setupVeeValidate();

    // Methods
    const setupVeeValidate = () => {
      console.log('Setting up vee-validate validation');
    };

    return {
      setupVeeValidate,
    };
  },

setupVeeValidate()という関数を作って、それを呼ぶようにしました。
しかしこれはエラーが出ます。

TypeError: setupVeeValidate is not a function

「setupVeeValidateは関数ではありません。」という意味のエラーで、setup()で、setupVeeValidate()がまだ関数と認識されていないようです。

setup()関数は関数なので、変数の宣言、なにかの計算、関数の宣言などはすべて上から順番に評価・処理されていきます

この場合、setup()関数の先頭でsetupVeeValidateを読んでも、まだこの関数の宣言ができていないので、関数が見つからない、というエラーになってしまうわけです。

下のように、関数の宣言を上に持ってこれば、このエラーは出なくなり、関数は実行されます。

  setup() {    
    const setupVeeValidate = () => {
      console.log('Setting up vee-validate validation');
    };

    setupVeeValidate();

    return {
      setupVeeValidate,
    };
  },

ただMさんのケースの場合、setupVeeValidate()関数のなかで、setup()関数の中で宣言する変数をたくさん使っているので、
setupVeeValidate()関数を一番上に持ってくると、今度は関数のなかで使っている変数が定義されていない、というエラーが出てしまいました。

変数の宣言  →  setupValidate()関数の宣言  →  setupValidate関数の実行

という順番に書けばこの問題もなくなります↓

setup() {

    // 変数の宣言
    const someVariable = ref('');

    // 関数の宣言・定義
    const setupVeeValidate = () => {
      console.log('Setting up vee-validate validation');
      someVariable.value = 'something';
    };

    // 変数の実行
    setupVeeValidate();

    return {
      someVariable,
      setupVeeValidate,
    };
  },

setup()関数がクラスではなく関数だということをついつい忘れてしまうと、後に宣言したものを使える気になってしまう、という面白いケースだと思いましたの紹介しました。


次は、

Vue2 -> Vue3に書き換えるとどういうケースでアプリが効率的に動作するのか

これもMさんからの質問です。
これは第1回でも説明したのですが、Vue3のComposition APIでは、変数の宣言をrefや、reactiveでラップしないと、変数の値を変えても、UIに変更が反映されません。
refやreactiveで宣言してない場合、<template></template>のなかで変数を使っていたとしても、これらの変数の変更をモニターしていないので、<template></template>の記述を再レンダリングしてくれないからです。

さきほどのMさんになぜComposition APIでこのような仕様になったのか、私の考えを話しましたが、いまいち伝わってない感じでした。
第1回の記事で私は↓のように理由を説明していますが、今回も最初は同じような説明をしました。

data()からこのように、変数ごとにref()で初期化するように変えた理由は、パフォーマンス的なメリットではないかと私は考えています。data()にあるものの中には他の関数で使うだけで、UIから参照されないものもあります。これらのデータの変更まで全部モニタしているのは、ブラウザのリソース(そしてブラウザを動かしているコンピューターのリソース)がもったいないので、このように必要なものだけをリアクティブにするという意図ではないかと思います。

https://note.com/mizutory/n/n59ecb2aeb0a3

ただ、この説明だけでは、「全部の変数をモニターすることがどれくらいの非効率さを生む可能性があるのか」がうまく伝わっていませんでした。
言い換えると、変更をモニターしたら大変なことになるような変数のイメージというのがなかなか持てないようでした。

なにかいい例はないかなと少し考えてみて、Webアプリのケースではないのですが、過去のAndroidアプリでのケースを例に使いました。

すこし前ですが、人体のある生体データを計測するデバイスと連動するAndroidアプリの試作を複数のプロジェクトでいくつか作ったことがありました。
こういう生体データというのは数10ミリ秒単位で新しいデータが高速に送られてくるので、そのデータを一旦保存する変数の値は凄まじいスピードで更新されて行きます。
もし、生体データをリアルタイムで扱うWebアプリをVue.jsで書いていて、この生体データをdata()セクションの変数に格納し、その値を何らかの形で<template>で参照していたら、凄まじい速度でブラウザーの再描画処理が走ってしまいます。こうすると画面が固まったり環境によってはブラウザ、あるいはOSがフリーズしてしまうかもしれません。
こういうデータというのは、一時的に変数に格納したとしても、別のプロセスやスレッドでそこからUIに表示したい形にデータを加工し、加工したデータをUIに適切な更新頻度や適切なスピードのアニメーションで表示するのが望ましい場合がほとんどです。
こういうアプリを作る時は
データの取得と一時保存

データの加工と、UIへの表示
を全く別の2つの独立したプロセスとして動くように設計する必要がある、ということをまず、Mさんに説明しました。
次に、これをVue3のComposition APIに置き換えて考えてみると、
取得したデータの一時保存はrefやreactiveでラッピングされていない素の変数に格納し、

加工してUIに表示したいデータはrefやreactiveでラッピングしたリアクティブな変数に保存するということになります。

(データの加工はタイマーなどをつかって、デバイスから送られてくるデータのスピードとは独立した自分の好みのタイミングで行います。)

このデバイスからのデータ読み取りの例を使って説明したら、Mさんのなかですごく納得がいったようでした。

次回は↓↓↓

追記

Vue3.2から<script>タグを<script setup>とすることで宣言的に完結にVue3のComposition APIに即したコンポーネントが書けるようになりました↓↓↓

このブログに関する質問やWebアプリ、iOS・Androidアプリの開発の相談はこちらから↓↓↓

@mizutory
mizutori@goldrushcomputing.com




この記事が気に入ったらサポートをしてみませんか?