この記事について
Vueを開発・運用してアプリケーションの規模が段々増えてきました。
チームの人員も増やしていく中で慣れていないエンジニアがコンポーネントの実装に関わるというケースが増えてきて、どんなデータ構造やアクション定義を作るのが良いのか悩んでいるケースを見受けるようになりました。
今回は自分がコンポーネントを実装する時に考えていることを簡単に紹介しておきます。
コンポーネントはユーザーに最も近いコード
コンポーネント思考で実装していくと、ユーザーと近い距離に居るコードは各コンポーネント郡になります。言い換えるとコンポーネントとユーザーは密接な関係にあり、お互いの共通認識が合っていることが望ましいということになります。
利用するユーザーの思い浮かべるデータ構造や行動と実装されたコードの変数や関数が同じであるということです。つまりコンポーネントを使うユーザー側の気持ちがわからなければコンポーネントの実装も困難ということです。
サンプルです。
<template>
<div>
...何かしらのコンテンツ
<button @click="toggleFavorite">
<template v-if="favorite">
いいね!を解除
</template>
<template v-else>
いいね!する
</template>
</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
data() {
return {
favorite: false
}
},
methods: {
toggleFavorite() {
this.favorite = !this.favorite
}
}
})
</script>
何かしらのコンテンツに対していいね!したりするサンプルです。この実装には2つの問題点があります。
- ユーザーにとってはいいね!なのにコード上ではお気に入りという変数名になっている
- いいね!を変更する処理がコード上では同一の関数で定義されている
どちらも動作上は問題ありません。バグもありません。ですが後々の機能追加や変更などで破壊的変更をする必要が出てしまうコードで良いコードとは言えません。何故ならユーザーの行動原理から外れている実装だからです。
ユーザー側の名称でデータ構造を定義
favorite
をいいね!として扱う場合、機能追加で「お気に入りを追加したい」と言われた時にどうでしょうか? favorite2
と定義してやり過ごすことはできますが、今後コードを見た人が混乱してしまうと思います。
古くから運用しているサービスになると歴史上データベースやAPIが favorite
になっているケースがあります。サンプルではバックエンドと連携していませんが、こういったアプリケーションのサービス内の名称とユーザー側の名称が違う問題はそのままコンポーネントの実装にまで影響してしまうケースが多くあります。
統一することが理想ですがデータベースごと名称の変更を全て反映するのは規模によっては難しいです。でも差異を無くしておくことはできます。
フロントエンドは比較的変更がやりやすい分類なのでこういったものは先にフロントエンド側で吸収しておくことで、機能追加や改善・インフラ整備の時に役立つことになるでしょう。
ユーザーの行動に沿った関数を定義
toggleFavorite
という関数は効率的です。ですがユーザーの意思とは連動していません。
極端な例ですが、例えば「誤動作を避ける為にいいねする時だけ確認のアラートをして欲しい」といったユーザーの要望を反映する時にどうでしょうか?両方の機能を内包している為、片方のアクションに対して何か付加価値を付け足す時に拡張しにくいと感じませんか?
ユーザーにとって「いいね!する」という行動と「いいね!を解除する」という行動は全く別の意思から行われるアクションです。こういったユーザーの行動ごとに特殊な処理を挟むケースはフロントエンドではとても多いです。経験上Vuexでステート管理すると更に複雑化していきます。
「関数は最低限の方が...」「HTML上では同じ場所にあるボタンなのに...」と思うこともあるかもしれません。ですが、あくまでアプリケーションは利用するユーザーの為に動くべきであり、機能追加や変更もユーザーの行動結果から発生していくものです。
修正後のコードです。
<template>
<div>
...何かしらのコンテンツ
<button v-if="like" @click="unlikeContent">
いいね!を解除
</button>
<button v-else @click="likeContent">
いいね!
</button>
</div>
</template>
<script lang="ts">
import Vue from 'vue'
export default Vue.extend({
data() {
return {
like: false
}
},
methods: {
likeContent() {
this.like = true
},
unlikeContent() {
this.like = false
}
}
})
</script>
コンポーネントの実装 = ユーザーの意思
この考え方はテストケースも作成しやすい利点があります。
toggleFavorite
は現実のユーザーの行動と反しています。 likePost
や unlikePost
を実行してデータ構造をチェックする方がよりユーザーの行動に近いテストケースになると思います。
同じようにバグの発生源を特定する上でも有効です。問い合わせなどのユーザーの行った行動がそのまま再現手順に近くなり、関連するデータやメソッドに対してデバッグを行えば見つかりやすくなります。
実装上ではとても小さなことですが、こういった考えが規模の大きくなったアプリケーションにおいて効果的になっていきます。まずはユーザーの気持ちになって脳内でコンポーネントを操作するイメージを掴むことが大事です。