Quantcast
Channel: CSSタグが付けられた新着記事 - Qiita
Viewing all articles
Browse latest Browse all 8960

【Vue.js】Scoped CSSのscoped具合を検証してみた

$
0
0

Vue.jsのScoped CSSは完全にはscopedじゃない、という話を聞いたので検証してみました。
結果、コンポーネント最上位の要素と、slotについては気をつける必要があることが分かりました。

参考記事

親コンポーネントのstyleが子コンポーネントに影響を与える仕組みは、以下が詳しいです。

検証環境

  • OS: macOS Catalina 10.15.5
  • ブラウザ: Google Chrome 83.0.4103.116(Official Build)(64bit)
  • Vue.js: 2.6.11


検証1: 素朴にstyleを当てた場合

親、子、孫コンポーネントにそれぞれ違った色を割り当てました。
すると、各コンポーネントの最上位の要素には親コンポーネントのスタイルが適用されてしまいました。
1.png

Parent.vue
<template><divclass="root-div">
    this is root div of parent
    <h1class="my-title">this is h1 of parent</h1><divclass="h2-container"><h2>this is h2 of parent</h2></div><Child/></div></template><scriptlang="ts">importVuefrom"vue";importChildfrom"@/components/Child.vue";exportdefaultVue.extend({name:"Parent",components:{Child,},});</script><stylescopedlang="scss">.root-div{color:red;}.my-title{color:red;}h2{color:red;}</style>
Child.vue
<template><divclass="root-div">
    this is root div of child
    <h1class="my-title">this is h1 of child</h1><divclass="h2-container"><h2>this is h2 of child</h2></div><GrandChild/></div></template><scriptlang="ts">importVuefrom"vue";importGrandChildfrom"@/components/GrandChild.vue";exportdefaultVue.extend({name:"Child",components:{GrandChild,},});</script><stylescopedlang="scss">.root-div{color:blue;// こいつが効いていない}.my-title{color:blue;}h2{color:blue;}</style>
GrandChild.vue
<template><divclass="root-div">
    this is root div of grandchild
    <h1class="my-title">this is h1 of grandchild</h1><divclass="h2-container"><h2>this is h2 of grandchild</h2></div></div></template><scriptlang="ts">importVuefrom"vue";exportdefaultVue.extend({name:"GrandChild",});</script><stylescopedlang="scss">.root-div{color:green;}.my-title{color:green;}h2{color:green;}</style>


検証2: 全称セレクタ(*)を使った場合

*を使って乱暴にstyleを指定した場合どうなるかを見てみました。
結果、検証1と同じく、各コンポーネント最上位の要素だけは親コンポーネントのstyleの影響が出ました。
※検証1のコードから、Child.vueだけ変更しました。
1.png

Child.vue
<template><divclass="root-div">
    this is root div of child
    <h1class="my-title">this is h1 of child</h1><divclass="h2-container"><h2>this is h2 of child</h2></div><GrandChild/></div></template><scriptlang="ts">importVuefrom"vue";importGrandChildfrom"@/components/GrandChild.vue";exportdefaultVue.extend({name:"Child",components:{GrandChild,},});</script><stylescopedlang="scss">.root-div*{color:blue;}*{color:blue;}**{color:blue;}</style>


検証3: globalのcssがある場合

当たり前ですが、ページ全体に適用されているcssがある場合、競合します。
例えば、ページ全体に* { color: black!important }を当てたら、すべて黒になります。
1.png

App.vue
<stylelang="scss">*{color:black!important;// プロダクトコードで書いたら殴られるやつ}</style>



もちろん、!importantを付けなければ、コンポーネント内のcssが優先的に適用されます。
(この辺はScoped CSSというよりはCSSそのものの話)
1.png

App.vue
<stylelang="scss">*{color:black;}</style>


検証4: slotを使った場合

slotを使って、親から子へタグを渡した場合、渡したタグには親コンポーネントのstyleが適用されました。
1.png

Parent.vue
<template><divclass="root-div">
    this is root div of parent
    <h1class="my-title">this is h1 of parent</h1><divclass="h2-container"><h2>this is h2 of parent</h2></div><Child><!-- 親からタグごとslotを挟むと、親のstyleが適用される --><template#my-slot><p>slot: this is p of parent</p><div><h2>slot: this is h2 of parent</h2></div></template></Child></div></template><scriptlang="ts">importVuefrom"vue";importChildfrom"@/components/Child.vue";exportdefaultVue.extend({name:"Parent",components:{Child,},});</script><stylescopedlang="scss">.root-div{color:red;}.my-title{color:red;}h2{color:red;}p{color:red;}</style>
Child.vue
<template><divclass="root-div">
    this is root div of child
    <h1class="my-title">this is h1 of child</h1><divclass="h2-container"><h2>this is h2 of child</h2></div><slotname="my-slot"><p>slot: this is p of child</p><div><h2>slot: this is h2 of child</h2></div></slot><GrandChild/></div></template><scriptlang="ts">importVuefrom"vue";importGrandChildfrom"@/components/GrandChild.vue";exportdefaultVue.extend({name:"Child",components:{GrandChild,},});</script><stylescopedlang="scss">.root-div{color:blue;}.my-title{color:blue;}h2{color:blue;}p{color:blue;}</style>



一方、親から子へ文字列だけを渡した場合、子のstyleが適用されました。
1.png

Parent.vue
<template><divclass="root-div">
    this is root div of parent
    <h1class="my-title">this is h1 of parent</h1><divclass="h2-container"><h2>this is h2 of parent</h2></div><Child><!-- 親から文字列だけを渡すようにすれば、親のstyleは適用されない --><template#my-slot-p>slot: this is p of parent</template><template#my-slot-h2>slot: this is h2 of parent</template></Child></div></template><scriptlang="ts">importVuefrom"vue";importChildfrom"@/components/Child.vue";exportdefaultVue.extend({name:"Parent",components:{Child,},});</script><stylescopedlang="scss">.root-div{color:red;}.my-title{color:red;}h2{color:red;}p{color:red;}</style>
Child.vue
<template><divclass="root-div">
    this is root div of child
    <h1class="my-title">this is h1 of child</h1><divclass="h2-container"><h2>this is h2 of child</h2></div><p><slotname="my-slot-p">slot: this is p of child</slot></p><div><h2><slotname="my-slot-h2">slot: this is h2 of child</slot></h2></div><GrandChild/></div></template><scriptlang="ts">importVuefrom"vue";importGrandChildfrom"@/components/GrandChild.vue";exportdefaultVue.extend({name:"Child",components:{GrandChild,},});</script><stylescopedlang="scss">.root-div{color:blue;}.my-title{color:blue;}h2{color:blue;}p{color:blue;}</style>


で、結局何に気をつければいいのか

  • コンポーネント最上位の要素になるべくstyleを当てない
  • 当てざるを得ない場合、.component-name-root { display: flex }のように、重複しないクラス名を付けて、styleを指定する
  • 自前でslotを作る場合、極力DOM構造は子コンポーネント側で定義する
  • デザインフレームワーク(vuetifyとか)の制約上slotにDOMを渡さざるを得ない場合は、予期せぬstyleが適用されないよう気をつける

所感

完全でないとはいえ、知らずに使っていてもそんなに気にならない程度にはscopedにできていると感じました。
個人的にはslotを使わないため、各コンポーネントの最上位のクラス名だけ気をつければ問題なし! という印象です。
Scoped CSS便利。


Viewing all articles
Browse latest Browse all 8960

Trending Articles



<script src="https://jsc.adskeeper.com/r/s/rssing.com.1596347.js" async> </script>