Upgrade to Pro — share decks privately, control downloads, hide ads and more …

BASEにおける Vueコンポーネント設計の現在

simezi9
October 30, 2019

BASEにおける Vueコンポーネント設計の現在

Vue.jsアーキテクチャリング勉強会
https://cw-engineers.connpass.com/event/146975/

で話したスライドです

simezi9

October 30, 2019
Tweet

More Decks by simezi9

Other Decks in Programming

Transcript

  1. フロントエンドチームは何をしてきたか © - BASE, Inc. • 管理画⾯刷新プロジェクト開始で jQueryからVue.js+TS化 • SPAではなく、⼤きな機能単位で

    MPA(Multi Page Application)化 • 内製のUIコンポーネントライブラリ の作成 ࣍ͷ5೥Λࢧ͑ΔVue.js੡UIίϯϙʔωϯτϥΠϒϥϦΛҭͯΔ
  2. コンポーネントの種類 © - BASE, Inc. Atomic DesignとContainer Componentの考え⽅を 借りて、役割に応じて⼤きく4つに分類 .

    Container Component . Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
  3. コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .

    Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
  4. Container Component © - BASE, Inc. • Atomic DesignでいうPageに相当 •

    API通信、Storeへのアクセスを⾏うことができる  唯⼀のコンポーネント • 複数のPresentational ComponentやContainer Componentを束ねる役割 • ContainerComponentの中に多少のUIを持つこと は認める
  5. コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .

    Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
  6. Presentational Component © - BASE, Inc. • Template/Organismに相当 • 副作⽤を持たず、与えられたプロパティを表⽰するだ

    けのコンポーネント • 具体的なビジネスロジック‧表現に紐づくため再利⽤ 性は考えないコンポーネント • ContainerComponentと PresentationalComponentはディレクトリを分け ず、名前で区別する
  7. コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .

    Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
  8. Common Presentational Component © - BASE, Inc. • Template /

    Organism / Moleculeに相当 • Presentational Componentの中でアプリケーション の内部で頻出するデザインのパターンを定義する • 例)全体のヘッダーやページのパンくずとタイトル • 具体的なコンテキストがない抽象的で使い回しができ るコンポーネント群
  9. コンポーネントの種類 © - BASE, Inc. コンポーネントを役割に応じて⼤きく4つに分類 . Container Component .

    Presentational Component . Common Presentational Component . Atom Component ( UIライブラリ )
  10. Atom Component © - BASE, Inc. • Atomic DesignでいうMolecule/Atomに相当 •

    個別のアプリケーションにとどまらず、サービス全体 で流⽤できるような⼩さいコンポーネント群 • form部品やbuttonなどの利⽤頻度がたかいもの • スタイルの局所的な上書きも許容できるように Scoped CSS / CSS Modulesを使わない • コンポーネント外との関係を決めるスタイルは当てな い 例) margin, float, z-index‧‧‧etc
  11. カタログ性の⾜りないStorybook © - BASE, Inc. 実装者が作るだけ作って満⾜してしまった ケース • コンポーネントが⼀個おいてあるだけ 想定されている使い⽅が伝わらない

    • knobsに⼤量のパラメータ パラメータの組み合わせもわからず、作った ⼈にしかわからない。特にデザイナーとはコ ミュニケーションが難しい 協業の場として機能しない
  12. storybookを機能させるために © - BASE, Inc. • Knobsに頼りすぎない • あくまでknobsは補助 •

    UIとして利⽤するパターンはなるべく⼀覧できる ように(= カタログ) • 実装に必要な情報はきちんと書く(=サンドボックス) • コンポーネントごとにREADME.mdを⽤意する
  13. コンポーネントごとのREADME.md © - BASE, Inc. @storybook/addon-notesで読み込む 必要な情報 • 簡単なサンプルコード •

    動作の仕様 • Propsの値、型、デフォルト値 • Eventの発⽕タイミング、payload • slotの名前 • scoped-slotで渡される値 …etc 将来的には@storybook/addon-docsで markdownを活⽤できるようになるはず (現在はVueのサポートがWIP)
  14. コンポーネントのレイヤ別に必要なこと © - BASE, Inc. ίϯϙʔωϯτͷछྨ Χλϩάੑ αϯυϘοΫεੑ Container Storybook্ʹ

    ొ࿥͠ͳ͍ Presentational ௿ ௿ Common ௿ ߴ Atom ߴ ߴ レイヤの低い抽象的なコンポーネントほど説明が必要
  15. フロントエンドの性質 © - BASE, Inc. • フロントエンドのコードはライフサイクルが短い • 修正=全取替であることが珍しくない •

    ドメインロジックに落としづらい細かい分岐の連続 • 例)ある⼀箇所だけ「0円」を「無料」にしたい… • 共通化しても仕様がすぐに変わってしまう
  16. コンポーネントの責務の分離 © - BASE, Inc. • コンポーネントで作ることでスタイル、マークアッ プ、スクリプトを外部に影響しない形でカプセル化 できる •

    作る側もとにかく疎結合を優先して、類似コードの 重複を恐れない。 • 本当に必要なパターンと確定したら共通化する • いつでもコードの塊で捨てられる状態をキープする
  17. 実装のtips① props © - BASE, Inc. • ObjectやArrayを型に指定するのはなるべく避ける  特にTSを採⽤している場合にはPropTypeを使う props:

    { deliveryInfo: { type : Array as PropType<DeliveryMethod[]> }, payment: { type : String as PropType<Payment>, } },
  18. 実装のtips① props © - BASE, Inc. • HTML標準に沿った名前、汎⽤的な名前をつかう ίϯϙʔωϯτར༻ऀ͔ΒΈͨࣗવͳΠϯλʔϑΣΠεΛ৺͕͚Δ name:

    'AppInput', props: { id: String, name: String, type: String, value: { default: '', type: [String, Number], }, placeholder: String, }
  19. 実装の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>
  20. 実装の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) }
  21. 実装のtips③style © - BASE, Inc. • ⼀時的な状態に依存するスタイルはdata-*やaria-* と属性セレクタの組み合わせで当てる • 例)

    ローディング中、選択中か、など <app-component class=“foo” :aria-selected=“''+isHoge"> .foo { &[aria-selected="true"] { color: $green; } &[aria-selected="false"] { color: $gray; } }
  22. 実装のtips④scoped-slot © - BASE, Inc. コンポーネントの内部状態を⼀元管理するコンポー ネントが欲しくなる場合にはscoped-slotを利⽤す る 例) •

    テーブルのヘッダとリストの結びつき管理 • モーダルのトリガとなるコンポーネントとモー ダルのコンテンツ
  23. 実装の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>
  24. まとめ © - BASE, Inc. • コンポーネントでいちばん⼤事なのは責務の分離 • 特にContainerとPresentationalなコンポーネン トの役割わけをきっちり守るのは重要

    • いつでもコード(特にUI側)を捨てられるように。 • Storybookには広い利⽤者に向けたおもてなしの⼼ が必要 • ちょっとした⾶び道具的な修正でコンポーネントの前提が壊れても泣かない