こんにちは。アーキテクトの堀内です。
前回の記事ではフロントエンドフレームワークの選定についてお話をしました。
blog.ecbeing.tech
今回は採用した Vue.js を使用して開発を進めるにあたり、つまずいた点と解決方法について話していきたいと思います。
今回やろうとしたこと
アプリの WebView で商品一覧を表示、スクロールに応じて追加読込をする画面です。
ざっくりですが以下のイメージです。
テンプレートも分離したのは、View 部分だけを別管理として個別に編集できるようにしておきたかったからです。 商品一覧データは、商品単位の JSON データを配列形式で持たせています。
初期表示
事前に API で取得した商品データとテンプレートを Vue.js に指定して画面表示を行います。
今回テンプレートは、事前に HTML 上の targetID の要素に追記しています。
let vm = new Vue({ el: '#targetID', data: { items: <取得した商品データ> } });
Vue のレンダリング結果は以下のようになります。
無限スクロールによる一覧の追加表示
次に、無限スクロールによって商品データを追加表示する時の方法です。
こちらも直感的となっており、Vue インスタンス生成時に指定した items プロパティに商品データを追加するだけです。
vm.items.push(...<追加で取得した商品データ>);
配列に要素を新規追加したことを検知して Vue が追加分を表示します。
イイ感じです。
動作検証で問題発生・・・
早速動作検証をしていたら、Android のスペックが低い端末でカクツキが出てしまいました・・・。
デバッグ検証してみたところ、配列の要素数が大量になると再描画に時間がかかるようになっていました。
※検証時は要素数が数百~程度からカクツキ始めました。
Vue.js は仮想 DOM の差分レンダリングで描画される認識だったので追加分だけのコストなのかなと考えていましたが、既存要素が変化していないといえど要素数の増加はレンダリング性能に影響を与えるようです。
今回は Vue インスタンスが一つである必要性は特にありませんでしたので、ある程度の要素数に達したら別の Vue インスタンスを生成するような形にすることで解決しています。
初期表示(改良)
このタイミングでの改修はほどんとありません。
Vue インスタンスが複数となるため、その描画先である HTML タグも複数必要になります。
template 要素として雛形を用意しておき、Vue インスタンスを生成するたびに描画先のタグを template 要素からコピー作成するようにしました。
無限スクロールによる一覧の追加表示(改良)
前回は無条件に配列に push していましたが、事前に検証処理を挟むようにします。
既に一つの Vue で管理する商品数を超過していたら、新しく Vue インスタンスを生成して、空きがあれば既存の Vue に追加します。
const VUE_MAX_ITEM = 100; ・・・ if ( vm.items.length < VUE_MAX_ITEM ) { vm.items.push(...<追加で取得した商品データ>); } else { new Vue({ ・・・ }); }
動作検証(改良)
無事、カクツキが無くなりました!
配列操作で問題発生・・・
上記の過程で Vue インスタンスの配列データを色々触れていたところ配列に癖があるところに引っ掛かりました。
※Vue.js 公式リファレンスにはしっかり掲載されています。私は事象が発生してから公式を調べて判明しました。とりあえず公式を見るのは大事ですね!!
ダメな例:配列内の要素を差し替え
配列の既存要素を操作する場合は注意が必要です。
vm.items[1] = newGoodsItem;
このように配列内の要素を差し替える操作を行った場合も対応する表示コンテンツに反映されるように思えますが、実際は何も変化しません。
問題ない例:配列内の要素のプロパティ値を変更
配列要素内のプロパティの変更を行った場合は問題ありません。
vm.items[1].name = '新しい商品名';
以下のように対応する表示コンテンツに反映されます。
配列の検知範囲として配列に格納されているオブジェクトの変更は検知していますが、配列要素自体が指し変わるような変化は検知していないように見受けられます。
ダメな例の解決策:Vue.js 専用処理を用いて配列内の要素を差し替え
上で示した要素の差し替えを Vue に検知させるには、Vue.js 専用の格納処理である $set() を使用します。
vm.$set(vm.items, 1, newGoodsItem)
これで要素の変更が反映されました。
テンプレートを別チームに依頼
分離した商品一覧のデザインは、デザイン専用のチームにお任せしました。
Vue.js の公式リファレンスと使用できる変数の資料をまとめてお願いしたところ、エンジニアサイドで調整する点はほとんどなくデザイン側のコーダーだけでテンプレートを組んでもらうことができました。
これは選定編でも検討事項にあった、テンプレート作成に関する学習コストの低さが良い形で現れたのではないかと思います。
まとめ
パフォーマンス面や Vue.js 固有の記述が必要になる点を除けば、Vue.js の構文自体はとても簡単ですんなり利用できるようになれました。
テンプレートについても、学習コストの低さが功を奏してデザインチームへの委託がスムーズに行え、今回は Vue.js を採用してよかったと思います。
もう少し規模が大きくなれば Vuex や VueRouter 等のライブラリを使用しての開発も視野に入れたいと思います。
それではまた。
堀内
~ecbeingでは、様々な観点で試行錯誤しながらよりよいプロダクトを目指してチャレンジできるフロントエンドエンジニアを大募集中です!~ www.softcreate-holdings.co.jp