🐈

【Vue3.0】Scoped CSSが改善された話

2 min read 10

更新内容

https://github.com/vuejs/rfcs/pull/119

ScopedCSSの改善

以下のissueで議論されている下記の問題がこの更新で改善されました。

https://github.com/vuejs/vue/issues/11245#issuecomment-604150460

詳細はコメント欄にて記載して議論しています。

scoped によって、親コンポーネントのスタイルは子コンポーネントに漏れません。ただし、子コンポーネントのルートノードは親スコープの CSS と子スコープの CSS と両方によって影響を受けます。これは、設計上、親はレイアウトが目的で子のルート要素をスタイルすることができます。
https://vue-loader-v14.vuejs.org/ja/features/scoped-css.html

以下議論の通りで、対応はされておらず当該issueのclose自体が不適切の可能性があるため訂正しました。
https://zenn.dev/karan_coron/articles/87597250de406018e351#comment-ed3a4a32ad03e3351752

上記、対応されているとの報告があったため、一旦保留にします。

v-slot, ::v-deep, scopedのスタイリング記述方法の改善

ScopedCSSの記述方法が改善されます。
ディープセレクタの記述方法は従来、以下の記述でしたが、

<style>
  .a ::v-deep b ...
  .a >>> b ...
  .a /deep/ b ...
</style>

または、

<style scoped>
  .a ::v-deep b ...
  .a >>> b ...
  .a /deep/ b ...
</style>

以下のような記述ができて、v-slot, ::v-deep, scopedの記述がより直感的にスタイリングできるようになりました。

<style> <!-- global -->
  /* deep selectors */
  .a ::v-deep(.b) ...

  /* slot */
  .a ::v-slotted(.b) ...

  /* global */
  .a ::v-global(.b) ...
</style>

<style scoped> <!-- scoped -->
</style>

<style deep> <!-- ::v-deep -->
</style>

<style slotted> <!-- ::v-slotted -->
</style>

おわりに

何か間違えや質問などあればコメントお願いします。

Discussion

「ScopedCSSの改善」の箇所ですが、RFC を読んでも解決してない気がしたので Vue 3 系で試してみましたが、特に仕様は変わっていないように思えました。

私も以前から気になっていた箇所なので、この仕様に関してはどうにかならないかと思っていたところではあります。

この件ですが、長らくVue2で議論されていた上記問題のissueが当該のPRでcloseされて、この問題は解決したと書かれていたため、対応されたものだと思っておりました。
こちらでも調べてみたところ対応されていないのにPRがcloseされているようでした…。

こちら適用されているという情報もあるため、一旦元に戻します。

一応ですが、私の環境ですと Vue CLI を使用してプロジェクトを新規作成し、以下のバージョンになっていました。

  • $ vue -V -> @vue/cli: 4.5.6
  • package.json -> vue: ^3.0.0-0
  • node_modules/vue/package.json -> version: 3.0.0, _from: vue@^3.0.0-0

確認方法としては src/App.vue の style を以下に変更して、HelloWorld コンポーネントの背景色が変わるのを確認できました。

<style scoped>
div {
  background-color: #f00;
}
</style>

クラス名で指定した場合はどうでしょうか?問題となっていたのは子コンポーネントで定義されたクラス名と親コンポーネントで定義されたクラス名のバッティングについてです。
ブロック要素そのものに、スタイルが当たるのは仕方ないかと思います。

それも試しましたが同じく背景色が HelloWorld コンポーネントに反映されいるようです。

src/App.vue を以下に設定。

<style scoped>
.hoge {
  background-color: #f00;
}
</style>

src/components/HelloWorld.vue を以下に設定。

<template>
  <div class="hoge">
<!-- 省略 -->

と変更をして確認をしてみました。

子コンポーネントで定義されたクラス名と親コンポーネントで定義されたクラス名のバッティング

こちらですと、現状の以下の仕様と違っているように思えましたので、もしかしたらですが認識そのものが違っている可能性もあるかなとも思いました。

子コンポーネントのルートノードは親スコープの CSS と子スコープの CSS と両方によって影響を受けます

子コンポーネントのスタイルを同じ名前で当てても親が優先されるのですか?それですと、認識が違うかもしれません。

追記
親コンポーネントで定義したクラス名に当てたスタイルと子コンポーネントで定義したクラス名に当てたスタイルで、親からの影響を受け、上書きされてしまう場合の話です。

試しに両方に設定してみました。

src/App.vue を以下に設定。

<style scoped>
.hoge {
  background-color: #f00;
}
</style>

src/components/HelloWorld.vue を以下に設定。

<template>
  <div class="hoge">
<!-- 省略 -->

<style scoped>
.hoge {
  background-color: #00f;
  padding: 100px;
}
</style>

すると両方がマージされた上で、同じプロパティは親コンポーネントの記述が優先されました。

ただ、お話はその点ではなくて「子コンポーネントのルートノード」が「親スコープの CSS と子スコープの CSS と両方」によって影響を受けることに違いないかなと思いまして。

issue の問題が解決されているかは、私の英語力が足りず判断はつかないのですが、引用されている仕様については変わっていなように思えましたので。

こちらでも試しました。
App.vue

<template>
  <h1>Hello Vue 3!</h1>
  <CoverStyled />
</template>

<script>
import CoverStyled from '@/components/CoverStyled.vue'
export default {
  components: {
    CoverStyled
  }
}
</script>
<style scoped>
.test {
  color: red
}
</style>

CoverStyled.vue

<template>
  <div class="test">てすとです</div>
</template>

<script>
export default {
}
</script>
<style scoped>
.test {
  color: blue
}
</style>

以下でハッシュ値が変わっていましたのでクラスの上書きはされていませんでしたね。
これ自体は今回の改修の(issue の問題が解決されている)旨であっていると思います。
html

<div class="test" data-v-67fa6c6a data-v-7ba5bd90>てすとです</div>

css

.test[data-v-7ba5bd90] {
    color: red;
}
.test[data-v-67fa6c6a] {
    color: blue;
}

ただ、おっしゃるっとおり、
スタイルの上書きはされそうです。

issue の内容はセレクタが同じ(スコープ)になる問題だったということですね。
理解できました。ありがとうございます。

ということは、issue については解決したが、引用されている仕様については変更はないということですね。

ログインするとコメントできます