はじめに
Svelte
にはスタイルガイドが存在しない。
Svelte
を始めたいけど躊躇している方の中には、スタイルガイドやベストプラクティスが無いことも不安になる一因な気がしています。今回はReact
の設計手法として知られているPresentational and Container Components
をSvelte
に流用できないか試してみました。
結論
Presentational and Container Components
はSvelte
でも導入メリットがあります。Svelte
にはスタイルガイドやベストプラクティスがまだありませんが、React
やVue
の設計手法を取り入れることが出来ます。
想定読者
-
Svelte
のベストプラクティスを集めいている方 -
Svelte
を触ってみたい方(Githubにコードを上げているので手元で確認できます) -
Presentational and Container Components
というコンポーネント設計を知りたい方
サンプルコードの紹介
環境
- "svelte": "^3.0.0",
- "typescript": "^3.9.3"
こんなの作りました
アドベントカレンダーということで、クリスマスツリーを飾り付けするゲームを作りました。
Presentational and Container Components コンポーネント設計
今回のフォルダ構成はBASE株式会社さんの資料「BASEにおける Vueコンポーネント設計の現在」1を参考にさせていただきました。ありがとうございます!
資料をもとに作成したフォルダ構成です。(今は"ふ〜ん そうなんだ"くらいの気持ちで見てください。)
Presentational and Container Components とは
Presentational and Container Components
とはReact
での設計手法で、一つのコンポーネントを見た目のコンポーネントとロジックのコンポーネントに分割します。
この設計手法のメリットは以下の通りです。
- 見た目とロジックの責務を分離できる(関心の分離)
- デザイナーの方がロジック処理を触ることなくスタイル調整できる
- 見た目のコンポーネントの再利用性が向上する
- アトミックデザインでの
layout
コンポーネントが作りやすくなる ※今回は言及しません
余談になりますが、この設計手法を明文化したDan Abramovさんは、今ではこの設計手法を推奨しておらず、ロジックの分離はReact
のCustom Hooks
を使うことで同じ効果が得られる2と言っています。Svelte
にはCustom Hooks
のようなeffect
(副作用)を制御しきれるものは無い認識なのでこちらは割愛いたします。
実装方法
以下の赤枠のコンポーネントが対象です。
名前から想像がつくと思いますが
- ChristmasTree.svelte
-
Container Components
(ロジック)です。
-
- ChristmasTreePresentation.svelte
-
Presentational Components
(見た目)です。
-
処理の流れ
Presentational and Container Components
思想のコンポーネントの処理の流れはContainer Components
でロジック処理(store
へのアクセスや関数)を記述し、それらをPresentational Components
に引き渡します。
実際のコード
Container Components
(ロジック)はこんな感じです。
<script lang="ts">
import ChristmasTreePresentation from "./ChristmasTreePresentation.svelte";
import { ornament } from "../../store/ornament";
import { treeOrnaments } from "../../store/treeOrnaments";
...(省略の意味。以下、同じ)
// ベルのボタンのクリック処理
function handleClickBell() {
ornament.set({ type: "bell", colorHex: DEFAULT_BELL_COLOR });
}
// ベルボタンがアクティブか?
let isActiveBell = false;
// 飾り付けのstore
let allOrnaments: TreeOrnament[] = [];
treeOrnaments.subscribe((currentTreeOrnaments) => {
allOrnaments = currentTreeOrnaments;
});
...
</script>
<!-- Presentational Component に props を渡すだけ -->
<ChristmasTreePresentation
{handleClickBell}
{isActiveBell}
{allOrnaments}
... />
一方、Presentational Components
(見た目)はこんな感じです。
<script lang="ts">
...
// Container Component からの props を受け取る
// export let 〜 で宣言するとコンポーネントの引数になる。Svelte簡単!!
export let handleClickBell: () => void; // イベント処理
export let isActiveBell: boolean; // ボタンの非活性
export let allOrnaments: TreeOrnament[]; // 全ての飾り付け
...
</script>
<!-- 見た目に関することだけ書いてあるのでHTML構造が分かりやすくなる -->
<Header />
<div class="mx-1 my-2">
<!-- 受け取った関数や変数をセットするだけ -->
<AppButton on:click={handleClickBell} color="red" isActive={isActiveBell}>
<AppIcon id="bell" />
</AppButton>
...
</div>
<div class="h-full rounded-lg bg-indigo-900 mx-2 mb-1">
<AppCanvas on:click={(event) => handleClickCanvas(event)}>
<!-- svelteはテンプレート構文 #each を使えます -->
{#each allOrnaments as ornament (ornament.id)}
<AppIcon ... /> // 省略してます
{/each}
</AppCanvas>
</div>
<Footer />
いかがでしょうか?
メリットである「1. 見た目とロジックの責務を分離できる(関心の分離)」
が少しでも伝わっていたら嬉しいです。
メリット
メリット解説① 〜開発とデザイナーの実装箇所の分離〜
1.見た目とロジックの責務を分離できる(関心の分離)
2.デザイナーの方がロジック処理を触ることなくスタイル調整できる
Presentational and Container Components
の設計手法を取り入れたことにより、開発はContainer Component
を、デザイナーはPresentational Component
を主に実装していくことになります。
実際の運用も考慮すると、変更対象ファイルがそのまま変更理由になるため、コードレビューもしやすくなると思われます。
たとえば、デザイナーの方がContainer Component
を変更していたら、誤ってロジックを変更していたり、見た目を変更したかったにも関わらず、Container Component
を修正してしまっているかもしれません。逆もしかり。
メリット解説② 〜コンポーネント管理のしやすさ〜
3.見た目のコンポーネントの再利用性が向上する
Presentational and Container Components
には
-
Container Components
はステートフル(store
にアクセスする等) -
Presentational Components
はステートレス
という概念があります。
もしコンポーネントにstore
のロジック処理が書いてあった場合、コンポーネント設計の要である再利用性は著しく低下するでしょう。
Storybook
を導入されている場合、コンポーネント管理もしずらくなるでしょうし、便利なアドオン Knobs
3 などでは、動的にprops
を変更して見た目を確認やすくなるでしょう。~~( と言いつつ、Knobs
は Svelte
未対応でした)~~対応してました!
結びの言葉
Svelte
にはスタイルガイドやベストプラクティスがまだありませんが、React
やVue
の設計手法を取り入れることが出来ます。もしSvelte
で開発していて解決策が見つからないときは、React
やVue
でもググってみてください。きっと良い解決方法が見つかると思います。
また、私の作った**「クリスマスツリーを飾ろう」**を是非、触ってみてください!!
ちなみに、娘には3分で飽きられ、妻には「音は出ないの?ダダダダッて飾れないの?こう〜(以下略)」と不満の声が絶えなかったゲームです。
昨日のアドベントカレンダーで弊社の爆速の貴公子@takaHALが「Svelte + Vercel 爆速でサイトを公開する」と題打って記事を執筆しました。**「クリスマスツリーを飾ろう」**をVercel
にアップするきっかけにもなりました。ありがとさん!
「クリスマスツリーを飾ろう」
https://svelte-christmas-tree.vercel.app/
Githubコードはこちら
https://github.com/Makohan/svelte-christmas-tree
明日は@odn_chiくん、よろしく!
余談...
開発に着手して数時間後の様子がこちらです。
プレイ前 | プレイ後 |
---|---|
「...これ、クリスマスツリーの飾り付けっていうか、ダンジョンメーカーじゃないか?!」
もう、バカです。バカバカバカ。
-
「BASEにおける Vueコンポーネント設計の現在」 @simezi9 さん。大変参考になりました。ありがとうございます! ↩