Vue の関数型コンポーネントのメリットとデメリット

関数型のコンポーネントが何かも、そのメリットもいまいちわかりません。

状態の管理や
渡された状態の watch をしておらず、
また、何もライフサイクルメソッドを持ちません。

このようなケースにおいて、私たちは 関数型 としてコンポーネントをマークすることができます。

関数型コンポーネント - Vue.js

「状態の管理」とは data プロパティを持たないということを言いたい様子です。 関数型コンポーネントとは、以下のようなプロパティ、メソッドを持たない、コンポーネントということでしょうか。

  1. data
  2. watch
  3. beforeCreate, created, mounted ...

メリットとデメリット

デメリット

1. データの生成と表示が分離する。

取り扱うデータが別れてしまいます。 これは親コンポーネントでデータを生成する処理を書き、 子コンポーネントデータを表示する書き方になるからです。

2. props が面倒。

これは文字通り。props を書くのが面倒です。 いろんなところで使われるコンポーネントの場合、もし props を変更しようとすると大変なことになります。

メリット

最初はデメリットしか感じない上に、色々と勘違いもしていて、全くメリットがわかりませんでした。 VuePress の default theme いじってた時に、 こんな書き方、親要コンポーネントでデータを生成して 子コンポーネントに渡すような書き方がされてて。

なんだ?と思って考えてたら、ふと関数型コンポーネントという言葉を思い出しました。 それがきっかけになりました。 VuePress は、データベース操作しませんが...

1. 描画コストが少ない(機能面)

どうも「関数型としてコンポーネントをマークすること」言い換えると functional: true を指定することで描画コストが少なくなるらしいです。

関数型コンポーネントはただの関数なので、描画コストは少ないです。

関数型コンポーネント - Vue.js

この理由は以下にあるらしいです。

ライフサイクルや監視が行われないため
基礎から学ぶ Vue.js

でも、パフォーマンスは、そこまで改善しないみたいなことが、 以下の記事のどこかに書かれてたきがするのですが失念しました。

2. 使い回しがしやすくなる(書き方)

状態を親から受け取ることになります。 例えば、データベースとの処理を子コンポーネントに書かなくて済むようになります。 すると、 その子コンポーネントは、そのシステムのデータベースとは関係なくなるので、他のシステムでも流用しやすくなります。

また特殊なケースであるのに加えて 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 のコンテキストが無い) ことを意味します。

関数型コンポーネント - Vue.js

props を変化すると message は変化するんだからリアクティブなデータ持ってるんじゃないの?と勘違いしていました。 動作的にはリアクティブっぽいけど、どうも違う様子。

恐らく data を変更した場合は、コンポーネントが部分的に変更される。 一方 props で値が呼び出された場合は、関数が呼び出されるように全てが再生成されるからなのでしょうか?

<template>
  <div>
    {\{ message }\}  
  </div>
</template>

全体的に...

関数型コンポーネントを使うと、全体的に親側で状態を管理する感じになるのかな。 関数型コンポーネントを多用すると全体的に親コンポーネントでデータを生成して、子に渡すというような。