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

CSS Architecture on Vue.js

Avatar for tacamy tacamy
November 26, 2019

CSS Architecture on Vue.js

Avatar for tacamy

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