はじめに
Vue3のComposition APIにもようやく慣れてきてサクサクっとウェブアプリを作っていたときに、ふと遭遇した挙動に悩まされたので、忘備録として記事にまとめておくことにした。
問題
computedで定義した値がHTML側にまったく反映されない。配列周りの挙動にも気をつけており、値がセットされてくるタイミングも問題なかったのに、全く動いてないか、computedがそもそも生成されていない・・・?
問題のコード
要点をまとめるとこのような処理にしていた。.vueファイルの書き方が嫌いなため、Vue3とQuasarというフレームワークにて、CDNを使った書き方でやっていた。やっていることはComposition API で.vueの書き方に似せていた。
あるコンポーネントにて、目的とするオブジェクトの配列をフィルタリングして返す filtering_array という関数を作り、親のコンポーネントに関数群として返していた。
それが親側としてはhogehogeというオブジェクトだ。
親のHTMLからは、hogehoge.filtering_arrayという形で指定することで、算出プロパティの恩恵を受けて目的を実現しようとしていた。
//Vue.component用の処理の外出し目的の、ある.jsファイル内にて(test.jsとする)
export function defineHoge() {
const filtering_array = Vue.computed(() => {
var arr = [];
//配列を編集する処理
return arr;
});
~中略~
return {
filtering_array
}
}
//呼び出し側
import { defineHoge } from "./test.js"
const hogehoge = defineHoge();
return {
hogehoge
}
そしてHTML側にて
<q-list>
<q-item v-for="(item, index) in hogehoge.filtering_array ">
<span>{{ item.title }}</span>
~中略~
</q-item>
</q-list>
Quasarというフレームワークでは、q-listという要素はリストを生成する。vuetifyなどでもほとんど同じ要素がある。
条件でフィルタリングした配列をこのリストに出力しようとしたかった。
しかし・・・上記のやり方ではこの q-listに、配列の要素は一切出力されなかった。それどころか、~中略~内でやっていた 子要素itemで指定していた内容がすべてエラーとしてコンソールに吐かれてしまっていた。
computedの指定の仕方に問題があったのかと思い、公式のリファレンスを何度読み直しても問題なさそうに見えた。
いっそ配列を直接呼び出す関数にしてしまおうかとも思った。(パフォーマンス的に問題が出てくるが)
解決
ふと、いっそ直接呼び出してみるか、と思った。定義した関数自体を動かすのはちょっと面倒だったので、こうした。
//呼び出し側
import { defineHoge } from "./test.js"
const hogehoge = defineHoge();
return {
filtering_array: hogehoge.filtering_array
}
<q-list>
<q-item v-for="(item, index) in filtering_array ">
<span>{{ item.title }}</span>
~中略~
</q-item>
</q-list>
つまり、hogehogeオブジェクト内にあるfiltering_arrayを、親のコンポーネントで同じ名前の関数として返すことにした。
そして親のHTMLで、hogehoge.をやめてみた。
すると、正常に配列の中身が出力されるようになった。
・・・投稿した後気づいたが、リアクティブ外になっていたのが原因か。
と気づいてこうしてみてもOKだった。
const filtering_array ...
~中略~
return {
hogehoge : Vue.reactive({
filtering_array
})
}
//呼び出し側
import { defineHoge } from "./test.js"
const {hogehoge} = defineHoge();
return {
hogehoge
}
<q-list>
<q-item v-for="(item, index) in hogehoge.filtering_array ">
<span>{{ item.title }}</span>
~中略~
</q-item>
</q-list>
OKだった。
Vue.reactiveでcomputedしたプロパティを囲い込んでみた。
なるほど、公式のリファレンスで言われていたuse****();の書き方に変な先入観を持っていたせいだったようだ。
返されるのはあくまでもただのオブジェクトで内包された関数・プロパティ群であって、リアクティブは存在していない形になるようだ。
Vue.reactive(別にVue.refでもいいのだろうが)で指定してやることでcomputedもそのまま生きてくる。
終わりに
公式のリファレンス等ではcomputedは単に const hoge = computed(...) としか描かれていない。作成するウェブアプリ内で関数やプロパティの整理のためにオブジェクト内の子としてcomputedを定義した関数を内包して使おうと目論んでいた。
しかしどうやら、computedをしたプロパティは、オブジェクトに内包して子プロパティ的に呼び出すことはできないことがわかった。やるならコンポーネント内では直接指定するようにしなければいけないようだった。
普通のプロパティのようにhogehoge.[プロパティ名]などとはできない。
別ファイルで定義したcomputedをそのまま使うには、次の方法のどちらかが必要だとわかった。
- 親のコンポーネント(呼び出し元)のreturn 内で、 hoge: myobject.hoge と同じ名称で呼び出させる
- use****を定義したファイル内で reactiveやrefで内包しておく
とくにuse****という形で外出しした場合には忘れやすく、なかなか気づけないこの挙動。気をつけたい。