Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
BASEにおける Vueコンポーネント設計の現在
Search
simezi9
October 30, 2019
Programming
13
50k
BASEにおける Vueコンポーネント設計の現在
Vue.jsアーキテクチャリング勉強会
https://cw-engineers.connpass.com/event/146975/
で話したスライドです
simezi9
October 30, 2019
Tweet
Share
More Decks by simezi9
See All by simezi9
次の5年を支えるVue.js製UIコンポーネントライブラリを育てる / build up Vue.js component library for next five years.pdf
simezi9
7
5.2k
VBScript meets Vue.js 〜20年越しの出会い〜
simezi9
9
1.8k
Other Decks in Programming
See All in Programming
Azure AI Foundryではじめてのマルチエージェントワークフロー
seosoft
0
190
なぜ適用するか、移行して理解するClean Architecture 〜構造を超えて設計を継承する〜 / Why Apply, Migrate and Understand Clean Architecture - Inherit Design Beyond Structure
seike460
PRO
3
780
AIプログラマーDevinは PHPerの夢を見るか?
shinyasaita
1
230
AI時代の『改訂新版 良いコード/悪いコードで学ぶ設計入門』 / ai-good-code-bad-code
minodriven
20
8.3k
Agentic Coding: The Future of Software Development with Agents
mitsuhiko
0
110
脱Riverpod?fqueryで考える、TanStack Queryライクなアーキテクチャの可能性
ostk0069
0
270
10 Costly Database Performance Mistakes (And How To Fix Them)
andyatkinson
0
450
PHPでWebSocketサーバーを実装しよう2025
kubotak
0
290
『自分のデータだけ見せたい!』を叶える──Laravel × Casbin で複雑権限をスッキリ解きほぐす 25 分
akitotsukahara
2
640
AI時代のソフトウェア開発を考える(2025/07版) / Agentic Software Engineering Findy 2025-07 Edition
twada
PRO
93
31k
Flutterで備える!Accessibility Nutrition Labels完全ガイド
yuukiw00w
0
170
明示と暗黙 ー PHPとGoの インターフェイスの違いを知る
shimabox
2
540
Featured
See All Featured
Bash Introduction
62gerente
613
210k
The Pragmatic Product Professional
lauravandoore
35
6.7k
It's Worth the Effort
3n
185
28k
Improving Core Web Vitals using Speculation Rules API
sergeychernyshev
18
980
Mobile First: as difficult as doing things right
swwweet
223
9.7k
Optimising Largest Contentful Paint
csswizardry
37
3.3k
Code Review Best Practice
trishagee
69
19k
Imperfection Machines: The Place of Print at Facebook
scottboms
267
13k
Done Done
chrislema
184
16k
Building Applications with DynamoDB
mza
95
6.5k
The Myth of the Modular Monolith - Day 2 Keynote - Rails World 2024
eileencodes
26
2.9k
Become a Pro
speakerdeck
PRO
29
5.4k
Transcript
© - BASE, Inc. BASEにおける Vueコンポーネント設計の現在
⾃⼰紹介 © - BASE, Inc. 松原 佑介 フロントエンドエンジニア 2018年9⽉BASE⼊社 :
simezi
BASEのプロダクト © - BASE, Inc. ネットショップ作成サービス 「BASE」 ショッピングアプリ 「BASE」 Payment
to the People, Power to the People MISSION
© - BASE, Inc. 2019年10⽉25⽇ マザーズ上場しました
BASEのフロントエンド開発の現状 © - BASE, Inc. サーバーサイド:PHP, Go フロントエンドエンジニア:6名 フロントエンド:Vue.js, TypeScript,
Sketch デザイナー:8名 (2019年10⽉時点)
フロントエンドチームは何をしてきたか © - BASE, Inc. • 管理画⾯刷新プロジェクト開始で jQueryからVue.js+TS化 • SPAではなく、⼤きな機能単位で
MPA(Multi Page Application)化 • 内製のUIコンポーネントライブラリ の作成 ࣍ͷ5Λࢧ͑ΔVue.jsUIίϯϙʔωϯτϥΠϒϥϦΛҭͯΔ
今ではプロジェクト開始から約1年半 © - BASE, Inc. 本⽇はそこで追加で得られた コンポーネントづくりの知⾒ について話します
おしながき © - BASE, Inc. BASEでのコンポーネント設計 Storybookの整備 個別のコンポーネントの実装
© - BASE, Inc. ①BASEでのコンポーネント設計
コンポーネントの種類 © - BASE, Inc. Atomic DesignとContainer Componentの考え⽅を 借りて、役割に応じて⼤きく4つに分類 .
Container Component . Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .
Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
Container Component © - BASE, Inc. • Atomic DesignでいうPageに相当 •
API通信、Storeへのアクセスを⾏うことができる 唯⼀のコンポーネント • 複数のPresentational ComponentやContainer Componentを束ねる役割 • ContainerComponentの中に多少のUIを持つこと は認める
コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .
Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
Presentational Component © - BASE, Inc. • Template/Organismに相当 • 副作⽤を持たず、与えられたプロパティを表⽰するだ
けのコンポーネント • 具体的なビジネスロジック‧表現に紐づくため再利⽤ 性は考えないコンポーネント • ContainerComponentと PresentationalComponentはディレクトリを分け ず、名前で区別する
コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .
Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
Common Presentational Component © - BASE, Inc. • Template /
Organism / Moleculeに相当 • Presentational Componentの中でアプリケーション の内部で頻出するデザインのパターンを定義する • 例)全体のヘッダーやページのパンくずとタイトル • 具体的なコンテキストがない抽象的で使い回しができ るコンポーネント群
コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .
Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
Atom Component © - BASE, Inc. • Atomic DesignでいうMolecule/Atomに相当 •
個別のアプリケーションにとどまらず、サービス全体 で流⽤できるような⼩さいコンポーネント群 • form部品やbuttonなどの利⽤頻度がたかいもの • スタイルの局所的な上書きも許容できるように Scoped CSS / CSS Modulesを使わない • コンポーネント外との関係を決めるスタイルは当てな い 例) margin, float, z-index‧‧‧etc
概念図 © - BASE, Inc.
概念図 © - BASE, Inc. ΠϕϯτͷྲྀΕ
概念図 © - BASE, Inc. ֎෦ͱͷ௨৴ Container͚ͩͰߦ͏
概念図 © - BASE, Inc. Common/Presentational ͲͪΒͰʹͳΕΔ
© - BASE, Inc. ②Storybookの扱い
Storybookに求められる2つの役割 © - BASE, Inc. .サンドボックス 実際のコードに対してどう動くかを試す環境 実装の際のエンジニア向けガイドとしての働き .カタログ 提供されているコンポーネントを網羅的に⾒る
どちらかの要素が⽋けたものが出来上がりがち
サンドボックス性の⾜りないStorybook © - BASE, Inc. 昔ながらのUIカタログの機能しかないケース • やりたいことはわかっても渡すべきprops、 キャッチすべきevent、他コンポーネントと の組み合わせなど、実装に必要な情報が⾜
りない • storybookに置く意味があまりない • 実装を意識しないならsketch上にあるだけ でいい
カタログ性の⾜りないStorybook © - BASE, Inc. 実装者が作るだけ作って満⾜してしまった ケース • コンポーネントが⼀個おいてあるだけ 想定されている使い⽅が伝わらない
• knobsに⼤量のパラメータ パラメータの組み合わせもわからず、作った ⼈にしかわからない。特にデザイナーとはコ ミュニケーションが難しい 協業の場として機能しない
storybookを機能させるために © - BASE, Inc. • Knobsに頼りすぎない • あくまでknobsは補助 •
UIとして利⽤するパターンはなるべく⼀覧できる ように(= カタログ) • 実装に必要な情報はきちんと書く(=サンドボックス) • コンポーネントごとにREADME.mdを⽤意する
コンポーネントごとのREADME.md © - BASE, Inc. @storybook/addon-notesで読み込む 必要な情報 • 簡単なサンプルコード •
動作の仕様 • Propsの値、型、デフォルト値 • Eventの発⽕タイミング、payload • slotの名前 • scoped-slotで渡される値 …etc 将来的には@storybook/addon-docsで markdownを活⽤できるようになるはず (現在はVueのサポートがWIP)
コンポーネントのレイヤ別に必要なこと © - BASE, Inc. ίϯϙʔωϯτͷछྨ Χλϩάੑ αϯυϘοΫεੑ Container Storybook্ʹ
ొ͠ͳ͍ Presentational Common ߴ Atom ߴ ߴ レイヤの低い抽象的なコンポーネントほど説明が必要
© - BASE, Inc. ③個別のコンポーネントの実装
フロントエンドの性質 © - BASE, Inc. • フロントエンドのコードはライフサイクルが短い • 修正=全取替であることが珍しくない •
ドメインロジックに落としづらい細かい分岐の連続 • 例)ある⼀箇所だけ「0円」を「無料」にしたい… • 共通化しても仕様がすぐに変わってしまう
© - BASE, Inc. コンポーネントで作る最⼤の メリットは共通化ではなく 責務の分離
コンポーネントの責務の分離 © - BASE, Inc. • コンポーネントで作ることでスタイル、マークアッ プ、スクリプトを外部に影響しない形でカプセル化 できる •
作る側もとにかく疎結合を優先して、類似コードの 重複を恐れない。 • 本当に必要なパターンと確定したら共通化する • いつでもコードの塊で捨てられる状態をキープする
実装のtips① props © - BASE, Inc. • ObjectやArrayを型に指定するのはなるべく避ける 特にTSを採⽤している場合にはPropTypeを使う props:
{ deliveryInfo: { type : Array as PropType<DeliveryMethod[]> }, payment: { type : String as PropType<Payment>, } },
実装のtips① props © - BASE, Inc. • HTML標準に沿った名前、汎⽤的な名前をつかう ίϯϙʔωϯτར༻ऀ͔ΒΈͨࣗવͳΠϯλʔϑΣΠεΛ৺͕͚Δ name:
'AppInput', props: { id: String, name: String, type: String, value: { default: '', type: [String, Number], }, placeholder: String, }
実装のtips②event © - BASE, Inc. • イベント名にカラフルな語彙を使わない BASEͰ {Πϕϯτͷಈࢺ}:{Πϕϯτͷର} ͱ͍͏ϑΥʔϚοτΛར༻͍ͯ͠Δ
ྫ) update:someValue • XMLͷ໊લۭؒͷϑΥʔϚοτʹ߹Θͤͯಈࢺ+(͋Εతޠ)ͷܗࣜ • ಈࢺͷޠኮΛߜΓγϯϓϧͳ୯ޠΛ͏͜ͱͰݟ௨͕͕͋͠Δ • ಛʹಈࢺίϯϙʔωϯτʹ ͨ͠Πϕϯτ໊Λ͚ͭͳ͍ <app-controller @click:search="openSearchBox" @change:all="checkAll" @change:sortOrder="onChangeSortOrder" @change:editMode="onChangeEditMode" @change:keyword="onChangeKeyword" @search="onSearch"> </app-controller>
実装のtips②event © - BASE, Inc. • イベントのpayloadにObjectを使う • RORO(Receive an
object, return an object) • emitするプロパティが増えても壊れにくい // emit͢Δଆ this.$emit('change', { enabled: defaults, dirty: false }) // ड͚औΔଆɹ updateShippingFee({ enabled, dirty }) { // ObjectͷׂೖͰड͚औΔ this.$store.useCase(new EditItemDetail()).updateShippingFee(enabled, dirty) }
実装のtips③style © - BASE, Inc. • classを条件分岐や処理で渋滞させない • すべてclassで表現するとパターンが増えたときに 爆発しがち
// Մಡੑͷ͍ίʔυ <span :class="[type ? 'c-'+ type' : '', hasError ? ‘error’ : ‘’]”></span>
実装のtips③style © - BASE, Inc. • ⼀時的な状態に依存するスタイルはdata-*やaria-* と属性セレクタの組み合わせで当てる • 例)
ローディング中、選択中か、など <app-component class=“foo” :aria-selected=“''+isHoge"> .foo { &[aria-selected="true"] { color: $green; } &[aria-selected="false"] { color: $gray; } }
実装のtips④scoped-slot © - BASE, Inc. コンポーネントの内部状態を⼀元管理するコンポー ネントが欲しくなる場合にはscoped-slotを利⽤す る 例) •
テーブルのヘッダとリストの結びつき管理 • モーダルのトリガとなるコンポーネントとモー ダルのコンテンツ
実装のtips④scoped-slot © - BASE, Inc. モーダルのサンプル <app-modal> <!-- Ϟʔμϧͷදࣔঢ়ଶ͜ͷίϯϙʔωϯτͷ෦ʹӅṭ͢Δ -->
<template #activator="{activate}"> <!-- ϞʔμϧදࣔͷτϦΨͱͳΔؔΛscoped-slotͰఏڙ --> <app-button @click=activate>දࣔ͢Δ</app-button> </template> <template #body="{deactivate}"> <!-- ϞʔμϧඇදࣔͷτϦΨͱͳΔؔΛఏڙ --> <div>ίϯςϯπ</div> </template> </app-modal>
まとめ © - BASE, Inc. • コンポーネントでいちばん⼤事なのは責務の分離 • 特にContainerとPresentationalなコンポーネン トの役割わけをきっちり守るのは重要
• いつでもコード(特にUI側)を捨てられるように。 • Storybookには広い利⽤者に向けたおもてなしの⼼ が必要 • ちょっとした⾶び道具的な修正でコンポーネントの前提が壊れても泣かない
© - BASE, Inc. ありがとうございました