# 関数型コンポーネントってなに?
関数型のコンポーネントが何かも、 そのメリットもいまいちどころか、全くわかりませんでした。
状態の管理や
渡された状態の watch をしておらず、
また、何もライフサイクルメソッドを持ちません。このようなケースにおいて、私たちは 関数型 としてコンポーネントをマークすることができます。
公式ドキュメントは「状態の管理」とは data プロパティを持たないということを言いたい様子です。 関数型コンポーネントとは、以下のようなプロパティ、メソッドを持たない、コンポーネントということでしょうか。
- 状態 ... data
- 監視 ... watch
- ライフライフルフック ... beforeCreate, created, mounted, etc
# メリットとデメリット
# ◯ デメリット
# 1. データの生成と表示が分離する。
取り扱うデータが別れてしまいます。 これは親コンポーネントでデータを生成する処理を書き、 子コンポーネントデータを表示する書き方になるからです。
# 2. props と emit が面倒。
これは文字通り。props を書くのが面倒です。 また emit で受けるのも面倒です。 いろんなところで使われるコンポーネントの場合、 もし props を変更しようとすると大変なことになります。
# ◯ メリット
最初はデメリットしか感じない上に、色々と勘違いもしていて、全くメリットがわかりませんでした。 VuePress の default theme いじってた時に、 こんな書き方、親要コンポーネントでデータを生成して 子コンポーネントに渡すような書き方がされてて。
なんだ?と思って考えてたら、ふと関数型コンポーネントという言葉を思い出しました。 それがきっかけになりました。 VuePress は、データベース操作しませんが...
# 1. 描画コストが少ない(機能面)
どうも「関数型としてコンポーネントをマークすること」言い換えると
functional: true
を指定することで描画コストが少なくなるらしいです。
関数型コンポーネントはただの関数なので、描画コストは少ないです。
この理由は以下にあるらしいです。
ライフサイクルや監視が行われないため
基礎から学ぶ Vue.js
でも、パフォーマンスは、そこまで改善しないみたいなことが、 以下の記事のどこかに書かれてたきがするのですが失念しました。
# 2. 使い回しがしやすくなる(書き方)
状態を親から受け取ることになります。 例えば、データベースとの処理を子コンポーネントに書かなくて済むようになります。 すると、 その子コンポーネントは、そのシステムのデータベースとは関係なくなるので、 他のシステムでも流用しやすくなります。
ただ、そんなどこでも使う汎用的なコンポーネントが、 そこまであるのかは疑問なのですが。 React のドキュメントですが、以下のようなコメントがありました。
ステートフルなロジックをコンポーネント間で再利用するのは難しい
フックの導入 - React (opens new window)
また特殊なケースであるのに加えて props が面倒になるのですが、 データを複数の子のコンポーネントで使い分けるようなケースでは、 データの生成する処理を1箇所にまとめられるます。
<!-- Parent.vue -->
<template>
<div>
<ChildA :parameter="argument" v-if="condition" />
<ChildB :parameter="argument" v-else />
</div>
</template>
反面、props で渡さない形式だと argument
を計算する処理を、各子コンポーネントで書かないといけなくなります。
<!-- Parent.vue -->
<template>
<div>
<ChildA v-if="condition" />
<ChildB v-else />
</div>
</template>
依存性逆転の原則とかと絡みがあるのかな。
どちらかというと「書き方」が主なメリットで、「機能面」は副次的なメリットかなと。 書き方を制限して書く分、機能面では若干だけど改善してあげるよ、的な。
# 勘違いしていたこと
以下2つの勘違いが、関数型コンポーネントの理解を遠ざけていました。
# 1. props を状態と勘違いする。
props に状態はいるんじゃないの?と思いました。 たしかに状態なのかと聞かれれば状態に分類されると思うのですが。 Vue.js のドキュメントは「副作用がない」ことを「状態がない」と言いいた様子です。 このあたりで自分は混乱しました。
props を子コンポーネント側で変更しようとすると Vue.js から警告が出ます。 Vue.js は props を関数の引数に近いものとして取り扱って欲しい様子です。
# 2. リアクティブを勘違いする。
以下のように書かれていて...
それは状態を持たない (リアクティブデータが無い) でインスタンスを持たない ( this のコンテキストが無い) ことを意味します。
props を変化すると message
は変化するんだからリアクティブなデータ持ってるんじゃないの?と勘違いしていました。
動作的にはリアクティブっぽいけど、どうも違う様子。
恐らく data を変更した場合は、コンポーネントが部分的に変更される。 一方 props で値が呼び出された場合は、関数が呼び出されるように全てが再生成されるからなのでしょうか?
<template>
<div>
{{ message }}
</div>
</template>
# おわりに
関数型コンポーネントを使うと、全体的に親側で状態を管理する感じになるのかな。 関数型コンポーネントを多用すると全体的に親コンポーネントでデータを生成して、子に渡すというような。
当プロダクトでは、当初からコンポーネント分割の基準として導入していたAtomic Designという設計手法に基づいた簡潔なルールを元に、リファクタリングを行いました。 「Atom」「Molecule」レベルの汎用コンポーネントは、Vuex Stateを参照しないというルールです。
Vue.js+Vuex+TypeScriptのWebフロントエンド開発現場を前向きに改善した話 (opens new window)
Vuex を避けるべき理由について、考えました。