Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
42
Help us understand the problem. What is going on with this article?

More than 3 years have passed since last update.

@yassh

vue-loaderのScoped CSSのスタイルが子コンポーネントのルート要素に効いてしまって辛い

vue-loaderのScoped CSSには、1つ辛い点がある。それは、スタイルが子コンポーネントのルート要素に効いてしまうことである。だから、意図せず子コンポーネントのスタイルを崩してしまう危険性がある。

次のような場合、page.vueの.containerのスタイルは、page-header.vueの<div class="container">...</div>にも効いてしまう。

page.vue
<template>
  <div class="container">
    <page-header date="2017-12-07" heading="Hello!" />
    <p>Lorem ipsum dolor sit amet, consectetur adipiscing elit.</p>
  </div>
</template>

<script>
import PageHeader from './page-header.vue'

export default {
  components: {
    'page-header': PageHeader
  }
}
</script>

<style scoped>
.container {
  ...
}
</style>
page-header.vue
<template>
  <div class="container">
    <div class="date">{{ date }}</div>
    <h1 class="heading">{{ heading }}</h1>
  </div>
</template>

<script>
export default {
  props: {
    date: String,
    heading: String
  }
}
</script>

<style scoped>
.container {
  ...
}
.date {
  ...
}
.heading {
  ...
}
</style>

バグ?いえ、仕様です

最初、これはバグなのではないかと思ったが、どうやら仕様らしい。

With scoped, the parent component's styles will not leak into child components. However, a child component's root node will be affected by both the parent's scoped CSS and the child's scoped CSS. This is by design so that the parent can style the child root element for layout purposes.

Child Component Root Elements

回避策

回避策はいくつかある。

無名のラッパーで囲む

子コンポーネントのルート要素のみが親コンポーネントのスタイルの影響を受けるので、子コンポーネントを無名のラッパーで囲めば問題を回避できる。

page-header.vue
<template>
  <div><!-- 無名のラッパー -->
    <div class="container">
      <div class="date">{{ date }}</div>
      <h1 class="heading">{{ heading }}</h1>
    </div>
  </div>
</template>

<script>
export default {
  props: {
    date: String,
    heading: String
  }
}
</script>

<style scoped>
.container {
  ...
}
.date {
  ...
}
.heading {
  ...
}
</style>

コンポーネントの一番外側の要素のクラス名をプロジェクト内でユニークにする

コンポーネントの一番外側の要素のクラス名をプロジェクト内でユニークにする(たとえばコンポーネント名を接頭辞として付ける)ことでも、問題を回避できる。

page-header.vue
<template>
  <div class="page-header-container">
    <div class="date">{{ date }}</div>
    <h1 class="heading">{{ heading }}</h1>
  </div>
</template>

<script>
export default {
  props: {
    date: String,
    heading: String
  }
}
</script>

<style scoped>
.page-header-container {
  ...
}
.date {
  ...
}
.heading {
  ...
}
</style>

しかし、これは命名規則で解決するということなので、前時代的な感がある。

Scoped CSSの代わりにCSS Modulesを使う

Scoped CSSの代わりにCSS Modulesを使うのが最も安全だと思われる。

page-header.vue
<template>
  <div :class="$style.container">
    <div :class="$style.date">{{ date }}</div>
    <h1 :class="$style.heading">{{ heading }}</h1>
  </div>
</template>

<script>
export default {
  props: {
    date: String,
    heading: String
  }
}
</script>

<style module>
.container {
  ...
}
.date {
  ...
}
.heading {
  ...
}
</style>
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
42
Help us understand the problem. What is going on with this article?