Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥

CSS Architecture on Vue.js

tacamy
November 26, 2019

CSS Architecture on Vue.js

tacamy

November 26, 2019
Tweet

More Decks by tacamy

Other Decks in Programming

Transcript

  1. 2 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. ⾃⼰紹介 Takami Yamada, aka @tacamy Design Engineer HTML / CSS : 10年 9⽉に株式会社プレイドに転職しました :tada: Vue.js : 2年 39
  2. 3 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. KARTEについて ֬ೝ͢Δ جຊαʔϏε΍͝ར༻ํ๏Λ͝঺հ͠·͢ɻ ͓ಘͳಛయ΍໾ཱͭ৘ใ͕ຬࡌͰ͢ɻ ॳΊͯͷํ΁ ְתּׅ然钠ׅ׷ 6*رؠ؎ش٦׌ֽוזַזַ♳麦׃זְծ➙ ״׶إٔؗ،حف׃׋ְהְֲ倯䗳铣דׅկ UIرؠ؎ش٦ָ濼׏גֶֻץֹ 7אךرؠ؎ٕٝ٦ٕ DESIGN RULES » CHECK ׆׏ה⢪ִ׷،؎ذي׌ֽ׾䲧ִת׃׋կ ֿך堣⠓׾ֶ鋅鷕׃זֻկ 窫㼎ծ 妜׃ְ ౙͷओ໾ɺ Ξ΢λʔ COLLECTION OUTER 嗚稊勴⟝׾㼰׃㢌刿ׅ׷׌ֽדծ֮ז׋ך椚 䟝ך暟⟝ח⳿⠓ִ׷〳腉䚍ָ넝ֻז׶תׅկ 勴⟝׾㢌ִג嗚稊׃ג׫גֻ׌ְׁկ ׀䋞劄ך暟⟝כ 鋅אַ׶תׇ׿ד׃׋ַ ꟗׄ׷ ⼀⼈⼀⼈に合わせた 顧客体験を提供 Webサイトの訪問者の⾏動を 顧客ごとにリアルタイムに解析 $9 ސ٬ମݧ ϓϥοτϑΥʔϜ
  3. 4 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 対象 こんな⼈向けの話です • CSS設計は得意だが、Vue.jsのScoped CSSでの設計⽅法に迷いがある… • Scoped CSSなら安⼼!と適当に書いたらスタイルがバッティングしてハマる…
  4. 5 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. VueコンポーネントのScoped CSS コンポーネントってなんだろう?
  5. 6 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. VueコンポーネントのScoped CSS ひとまとまりの機能を持つ ⾃⼰完結型の再利⽤可能な部品 機能や振る舞いがワンセット どこで利⽤しても同じように動作
  6. VueコンポーネントのScoped CSS 7 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ Vue.jsのSFCは 1つのファイルで コンポーネントを実現 // ίϯϙʔωϯτΛߏ੒͢Δཁૉ <template> <div class="MyComponent"></div> </template> // ίϯϙʔωϯτͷঢ়ଶ΍ػೳΛఆٛ <script></script> // ίϯϙʔωϯτͷݟͨ໨Λ੍ޚ <style scoped> .MyComponent { /* ... */ } </style> コンポーネント単位のスコープでCSSを書ける
  7. 8 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. VueコンポーネントのScoped CSS 念願のCSSのスコープを⼿に⼊れた
  8. VueコンポーネントのScoped CSS 9 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ でも実際はただのCSS // Example.vue <template> <div class="Example">hi</div> </template> <style scoped> .Example { color: red; } </style> // ੜ੒͞ΕΔHTML <style> .Example[data-v-f3f3eg9] { color: red; } </style> <div class="Example" data-v-f3f3eg9> hi </div> 属性セレクタによりScopedを擬似的に実現 コンポーネント単位で⾃動的に付与される
  9. 10 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. VueコンポーネントのScoped CSS そのため、本物のスコープを持つ Shadow DOMとは異なる
  10. 11 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 Vue.jsのScoped CSSの留意点 https://vue-loader-v14.vuejs.org/ja/features/scoped-css.html
  11. Scoped CSSの留意点 12 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ 要素セレクタへの スタイル指定はNG // NG <template> <p>hi</p> </template> <style scoped> p { color: red; } </style> // OK <template> <p class="Example">hi</p> </template> <style scoped> .Example { color: red; } </style>
  12. 13 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 • 要素セレクタへのスタイル指定は、属性セレクタと組み合わせたとき何倍も遅くなる • クラスセレクタに対するスタイル指定なら、パフォーマンスに影響なし
  13. Scoped CSSの留意点 14 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ ⼦コンポーネントの ルート要素は 親スコープの影響をうける // Parent.vue <template> <div class="Parent Container"> <Child /> </div> </template> <style scoped> .Container { color: red; } </style> // Child.vue <template> <div class="Child Container">...</div> </template> <style scoped> ... </style> // ੜ੒͞ΕΔHTML <div data-v-e1272e36 class="Parent Container"> <div data-v-49e3088a data-v-e1272e36 class="Child Container">...</div> </div> ⼦コンポーネントの⽂字⾊もredになる
  14. 15 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 • ⼦のルート要素には、⼦⾃⾝のデータ属性+親のデータ属性が付与される - 親のScoped CSS内で、⼦のルート要素と同じクラス名がある場合、 ⼦にそのスタイルが適⽤される 親はレイアウト⽬的で⼦のルート要素をスタイリングできる - コンポーネント⾃体にはmarginを持たせず、親コンポーネントでmarginをつける等 意図せぬスタイルのバッティング • 親と⼦で同じクラス名がなければ問題ない
  15. Scoped CSSの留意点 16 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ deepセレクタで ⼦コンポーネント以下に 影響を与えることが可能 // VueϑΝΠϧ <style scoped> .Parent >>> .Child { /* ... */ } </style> // ίϯύΠϧ݁Ռ .Parent[data-v-f3f3eg9] .Child { /* ... */ } スコープが消え去る
  16. 17 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 • deepはセレクタのネストにより優先度が⾼くなり、⼦のスタイルが負ける場合もある • Scopedも崩壊するので極⼒つかわない • Element UIなどの外部ライブラリのスタイル調整にはテーマ機能を利⽤ ⾜りないスタイルは専⽤のラッパーコンポーネントを⽤いて影響範囲を最⼩化
  17. 18 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 これらの特徴によるトラブルを 回避する⽅法
  18. 19 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 命名規則に則って 要素に固有のクラス名を付与
  19. 20 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. Scoped CSSの留意点 BEMの考え⽅に似ている…?
  20. 21 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 コンポーネントを BEMのBlockのように捉えてみる
  21. 22 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 .block .ComponentName • BEMのblockの粒度 ≒ Vueのコンポーネントの粒度 と捉える • コンポーネントのルート要素がblockにあたる
  22. 23 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 .block__element .ComponentName__element • block部分がComponentNameに変わるだけで、他はBEMと同様 • .ComponentName__element__element にならないよう - 粒度が⼤きすぎる ≒ 機能も複雑になりやすい • 詳細度を⼀定に保つため、CSSは1階層で記述
  23. 24 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 .block__element--modifire .ComponentName__element._modifire • modifireは状態によってtemplate内で付け外しすることが多いため、 BEM式はさすがに冗⻑で⾒づらくなってしまう • blockやelementと組み合わせてスタイル指定することを前提に _modifireの形式とする - 組み合わせることで、blockやelementより詳細度が⾼くなるメリットも • modifireはJSで扱うことが多いため、ハイフンよりアンダーバーの⽅が楽
  24. 25 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 BEM式で命名するメリット • 広く知られている命名ルールで共通認識があり、書き⽅に統⼀性が⽣まれる • コンポーネント内のCSSに秩序が⽣まれる • HTMLのクラス名を⾒るだけで、どこにスタイルが書かれているか分かる • あとからコンポーネントを分割するときも、クラス名がバッティングしない
  25. 26 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. BEM的Scoped CSS設計 でもBEMはクラス名が⻑くて⾯倒?
  26. BEM的Scoped CSS設計 27 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari

    x Merpay Frontend Tech Talk vol.3 ʛɹ $options.nameと Sassの&で楽 <template> <div :class="$options.name"> <div :class="`${$options.name}__element`" > ... </div> </div> </template> <script> export default { name: 'Example' }; </script> <style lang="scss" scoped> .Example { &__element { /* ... */ } } </style>
  27. 28 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネント名の命名規則 コンポーネント名の命名規則 https://jp.vuejs.org/v2/style-guide/index.html Vue.jsのスタイルガイド
  28. 29 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネント名の命名規則 2単語以上で構成 • HTML要素との衝突を防⽌ - HTMLでは⼤⽂字も⼩⽂字扱いになるため <Button>は<button>となり、HTML要素とバッティングする - 将来追加される未知のHTML要素のことも考慮
  29. コンポーネント名の命名規則 30 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari x

    Merpay Frontend Tech Talk vol.3 ʛɹ 密結合コンポーネントの名前 // NG components/ |- TodoList/ |- Item/ |- index.vue |- Button.vue |- index.vue // OK components/ |- TodoList.vue |- TodoListItem.vue |- TodoListItemButton.vue • 親コンポーネントと密結合した⼦コンポー ネントは、親コンポーネントの名前をプレ フィックスとして含む • 親コンポーネントのディレクトリの中に⼦ コンポーネントを⼊れるのは⾮推奨 - 同じような名前のファイルが多数できてしまい、 エディタ上でのファイル切り替えが難しくなる - ネストが深くなると、エディタのサイドバーでコン ポーネントを参照するのに不便
  30. 31 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. ディレクトリ構成 ディレクトリ構成
  31. ディレクトリ構成 32 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari x

    Merpay Frontend Tech Talk vol.3 ʛɹ // ྫͱͯ͠Nuxt.jsͷߏ଄Λ΋ͱʹ // ελΠϧʹؔ࿈͢Δ෦෼Λൈਮ project/ |- assets/ |- scss/ |- _mixins.scss |- _variables.scss |- images/ |- components/ |- lauouts/ |- default.vue |- pages/ |- index.vue |- directory/ |- index.vue • mixin.scssとvariables.scssは components、layouts、pages以下 すべてのコンポーネントから読み込む • layoutsは共通レイアウト • pagesはルーティング • 共通コンポーネントはcomponentsに ここの分類を考える
  32. 33 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 コンポーネントの分類
  33. 34 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 コンポーネントの分類 • 複雑すぎる分類はコストがかかる - 所属するコンポーネントを議論するためのコスト - コンポーネント利⽤時の探すコスト - 正しい分類を維持していくための定期的な⾒直しコスト - 新メンバー⽴ち上がりまでのコスト • 何のために分類するのか⽬的を明確に
  34. 35 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 コンポーネントの粒度(⼤きさ) • 階層が深くなりすぎると、propsバケツリレーと$emit地獄に • 細かすぎる分割はパフォーマンスにも影響あり
  35. 36 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 粒度による分類 Part / Module • Partコンポーネント - 機能が成⽴する最⼩単位の部品 - 単体でも利⽤可能 - ボタン、フォームパーツ、アイコン • Moduleコンポーネント - Partsを組み合わせて別の機能を持つ - Moduleに別Moduleも含めることもできる - カード、検索ボックス、ヘッダー
  36. 37 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 役割による分類 Presentational / Container https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0 • Presentationalコンポーネント - 再利⽤される前提の要素 - 親からpropsを受け取って表⽰ - 親へ$emitで伝達 - storeに依存しない • Containerコンポーネント - ページそのもの、またはページを構成する要素 - ⼦へのデータの受け渡し - ⼦からイベントを受け取って処理を⾏う - storeのデータの取得や更新を⾏う
  37. 38 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 プロジェクトに最適な分類とは? • 最初はなるべくシンプルにはじめて、必要に応じて分類を増やすとよさそう • 議論に時間がかかる場合は、そもそもその分類がToo muchかも?
  38. 39 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 分類しないですべてのコンポーネントを同⼀階層に 1st STEP
  39. 40 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 分類(ディレクトリ) コンポーネントの例 parts ボタン、フォームパーツ、アイコン modules カード、検索ボックス、ヘッダー 共通コンポーネントが増えてきた 探しやすさのために粒度で分類 2nd STEP
  40. 41 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. コンポーネントの分類 分類(ディレクトリ) 役割 Store依存 parts Presentational × modules Presentational × container Container ◦ データの流れが追えなくなってきてデバッグしづらい 役割の明確化のために分類 3rd STEP
  41. 42 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 理想と現実
  42. 43 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 これまでの話は理想であり 最初から理想どおりにつくるのは難しい すべてのCSSをコンポーネントに紐づくScoped CSSにするメリット • 構造に⼀貫性があり、保守しやすい(⻑期運⽤にも耐えうる) 実現するための課題 • 初期段階で、すべてのユースケースを考慮したコンポーネントをつくるのは困難 • スタートアップのプロトタイピング的なプロジェクトでは、時間をかけてつくっても無駄になる可能性 • 調整⽤クラスはないほうがいいけど、利⽤者側からするとあった⽅がやっぱり便利
  43. 44 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 徐々に導⼊する コンポーネント的CSS設計
  44. 45 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 すべてのスタイルを外部CSSファイルに記述 templateのHTMLにクラスを付与するだけでスタイルを適⽤できる デザイナーがCSSを書くときに、Vueコンポーネントの粒度を意識しなくてよい 記述されたスタイルがどこで利⽤されているかわからない - 不要なスタイルが残り続けることで負債が蓄積し、メンテナンスコスト⾼ - 修正したつもりが意図しない箇所が壊れてしまう、エンジニアは触りたくない CSSを書かないエンジニアも、状態によるクラスの付け替えルールを把握する必要がある 1st STEP
  45. 46 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 • 共通コンポーネントのスタイルが書かれた外部CSSを全ページで読み込む • それ以外のスタイルは、各コンポーネントにScoped CSSで記述 外部CSSファイルとScoped CSSのハイブリッド式 2nd STEP KARTEはイマココ!
  46. 47 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 ハイブリッド式のメリット・デメリット スピード感を維持したまま、⼀部でScoped CSSの恩恵を受けられる Scoped内なら安⼼して、誰でもCSSを触ることができる スタイルの適⽤元がわからなくなるときがある • 共通コンポーネントに接頭辞をつける命名ルールである程度回避 Scoped内を⾃由に書けすぎてしまう • UIのルールが守られていない箇所もある • 同じような機能やスタイルがコピペであちこちに散在(でも微妙に違ったり) コンポーネントが外部のCSSに依存しているので、CSSが古いページにコンポーネントを 配置したときに表⽰が崩れてしまう
  47. 48 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 しばらく運⽤して、パターンがある程度出揃った段階で すべてのスタイルをScoped CSSでカプセル化 どこで使⽤しても同じように表⽰できることが保証できる スタイルの影響範囲が明確で、⻑期的に保守しやすい コンポーネントのアップデートの周知がうまくされないと 似たようなコンポーネントがつくられてしまう 細かいコンポーネントが増えるとテストの⼿間も増える 3rd STEP
  48. 49 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. 理想と現実 どの⽅法を選択するか? • 唯⼀の正しい⽅法は存在しない • プロジェクトの規模・フェーズ・個々の事情による • 外部のCSSに依存する場合も、 リセットやベーススタイルは極⼒ブラウザデフォルトに合わせておくと吉
  49. まとめ 50 ɹɹʛɹɹ© 2019 PLAID Inc. 2019.11.26 ʛ Mercari x

    Merpay Frontend Tech Talk vol.3 ʛɹ より良いCSSのゴール ≒ コンポーネント 予測しやすい 再利⽤しやすい 保守しやすい 拡張しやすい
  50. 51 2019.11.26 ʛ Mercari x Merpay Frontend Tech Talk vol.3

    ʛɹ ɹɹʛɹɹ© 2019 PLAID Inc. まとめ CSS設計 コンポーネント設計
  51. ɹɹʛɹɹ© 2019 PLAID Inc. 52 2019.11.26 ʛ Mercari x Merpay

    Frontend Tech Talk vol.3 ʛɹ 株式会社プレイドについて 株式会社プレイド 東京都中央区銀座6-10-1 GINZA SIX 10F 設⽴:2011年10⽉ 従業員:130名 資本⾦:1億円 ※資本準備⾦含む 株式会社プレイドでは KARTEのコンポーネント化を ⼀緒に推し進めてくれる CSS設計の得意な仲間を募集中です! https://www.wantedly.com/projects/299653