Vue 2コンポーネントのデフォルトとカスタマイズ可能なスタイル


Live Demo
独自のVueコンポーネントライブラリ(自分自身や他の人に対して)を作成するか、既存のVueライブラリに対して、既定ではなくカスタマイズ可能なスタイルを実装することを計画している場合は、参考になります.
非常にシンプルなリストコンポーネントを構築するつもりです.
<template>
  <div>
    <ul v-if="items.length">
      <li v-for="item in items" :key="item">{{ item }}</li>
    </ul>

    <p v-else>No items to show</p>
  </div>
</template>

<script>
export default {
  name: "ListItem",
  props: {
    items: {
      type: Array,
      default: () => [];
    }
  }
};
</script>

デフォルトスタイル


既定のスタイルを設定するには、コンポーネント内のスタイルタグ内のコンポーネント固有のクラスを定義する必要があります.
<template>
  <div class="list-container">
    <ul class="list" v-if="items.length">
      <li class="list__item" v-for="item in items" :key="item">
        {{ item }}
      </li>
    </ul>

    <p v-else>No items to show</p>
  </div>
</template>

<script>...</script>

<style>
.list-container {
  padding: 0.5rem 1rem;
  background: #ef9a9a;
  color: #232429;
  line-height: 180%;
  font-size: 0.875rem;
}

.list {
  list-style: none;
  padding: 0;
  margin: 0;
}

.list__item {
  background: #e8eaf6;
  padding: 0.25rem;
  margin: 0.5rem;
}
</style>
また、外部スタイルのsrc属性にURLを指定することもできます.
<style src="path/to/external/style">...</style>

スコープスタイル


私たちは私たちのコンポーネントのスタイルを誤って他のスタイルに影響を与えたくないが、私たちはいくつかのスコープが必要です.
スコープ指定されたスタイルは、自分自身のコンポーネント固有のスタイルを制限します.また、セレクタの衝突や衝突を防ぐために、Vueコンポーネントを構築することを好みます.
あなた自身のコンポーネントを除いて要素に影響を与えるつもりでない限り.
<style scoped>...</style>
すごい!私たちのリストのコンポーネントは、デフォルトのスタイリングがあります.

カスタムスタイル


デフォルトのスタイルが実装されているので、リストコンポーネントのスタイルをカスタマイズできます.
クラスを親から定義することができ、子クラスを右オーバーライドすることができます.
残念ながら、これはうまくいきません.
Scopedスタイル属性セレクタのおかげで.some-class[data-v-xxxxxx] CSS特異性が高い.

スコープスタイルの理解


Vue Scopedスタイルは、動的にそのテンプレートの要素にデータ属性を追加し、CSS属性セレクタのデータ属性を使用します.これにより、コンポーネント固有のCSSセレクタがより高い特異性を与えます.
スコープスタイルは、自分自身のスタイルを自分自身に制限し、親スタイルがチャイルズを変更するのを防ぎます.
<style scoped>
.example {
 color: red;
}
</style>

<template>
  <div class="example">hi</div>
</template>

Will compile into the following:

<style scoped>
.example[data-v-f3f3eg9] {
  color: red;
}
</style>

<template>
  <div class="example" data-v-f3f3eg9>hi</div>
</template>

Scoped Style


どのようなCSSの特異性は何ですか?


Specificity 2つ以上の規則が同じ要素にある場合、どのスタイル規則が要素に適用されるかを決定する関連性の測定です.

デフォルトスタイルのオーバーライド


どのようなCSSの特異性とどのようにスコープスタイルの作品を知って、我々だけでカスタムのスタイルを確実にする必要がありますより高い特異性を右?
そう!以下のような子スコープスタイルをオーバーライドできます:
<style>
.some-parent-class .some-child-class {
  color: red;
}
</style>
セレクタの上には、子要素の属性セレクタよりも特異性が高い.
<style scoped>
.some-child-class[data-v-xxxxxx] {
  color: blue;
}
</style>
したがって、それは適用されます.

ディープセレクター


Vueも、これにより良い解決策を持っています>>> コンビナータ.
<style scoped>
.some-selector >>> .some-child-class { /* ... */ }
</style>
をコンパイルします.
<style scoped>
.some-selector[data-v-xxxxxx] .some-child-class { /* ... */ }
</style>

Some pre-processors, such as Sass, may not be able to parse >>> properly. In those cases you can use the /deep/ or ::v-deep combinator instead - both are aliases for >>> and work exactly the same.

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

or

<style scoped>
.a /deep/ .b { /* ... */ }
</style>

Deep Selectors


それはチャイルズをオーバーライドすることによってデフォルトスタイルをカスタマイズするすばらしい方法です、しかし、それはスケーラブルではありません.
サードパーティ製のスタイルやCSSフレームワークやスタイルを使用しない場合は、子スタイルをオーバーライドできません.

小道具の使用


このように、スタイルをオーバーライドすることは我々が望むものではありません.代わりに、私たちのリストコンポーネント要素でカスタムクラスをバインドし、リストコンポーネントスタイルをpropのデフォルトとして割り当てるつもりです.
ためには、カスタムクラスを渡すためにpropsオプションが必要です.
<template>
  <div :class="listClasses.listContainer">
    <ul :class="listClasses.list" v-if="items.length">
      <li
        :class="listClasses.listItem"
        v-for="item in items"
        :key="item">
        {{ item }}
      </li>
    </ul>
    ...
  </div>
</template>

<script>
export default {
  props: {
    ...
    listClasses: {
      type: Object,
      default() {
        listContainer: "list-container",
        list: "list",
        listItem: "list__item"
      }
    }
  }
}
</script>
私は定義するlistClasses つの宣言で複数の要素を対象とするオブジェクトとしてのProp.
側のメモとして、使用することができますString , Array , Object クラスprop型として.
  • String - これは、プレーンクラスを意図し、単一の要素を指す場合は、単一または複数のスペースseperatedクラスを渡すことができます"class-a class-b" .
  • Array - 単一の要素を指すプレーンクラスと条件付きクラスのためのものです["class-a", {"class-b": true}] .
  • Object - 複数の要素を指す複雑なクラスを対象とします.{"classA": "class-a", classB: {"class-b": true}, classC: ["classC", {"classD": true}]}
  • これは今ではlistClasses propはデフォルト値をオーバーライドし、一度に1つのスタイルシートを使用するように制限します.
    そのように完全にその罰金、しかし、我々はより多くの柔軟性を提供することができます.

    計算特性


    既定のスタイルを部分的に変更し、残りのコンポーネントのスタイル宣言をマージします.
    それは計算されたプロパティが入っているところですlistClasses プロップがまだ供給されないならば、デフォルト値を提供します.
    propが部分的に定義されていれば、デフォルトクラスをマージすることができます.
    <template>
      <div :class="obtainClasses.listContainer">
        <ul :class="obtainClasses.list" v-if="items.length">
          <li
            :class="obtainClasses.listItem"
            v-for="item in items"
            :key="item">
            {{ item }}
          </li>
        </ul>
        ...
      </div>
    </template>
    
    <script>
    export default {
      props: {
        ...
        listClasses: {
          type: Object,
          default: () => ({})
        }
      },
      computed: {
        obtainClasses() {
          const defaultClasses = {
            listContainer: "list-container",
            list: "list",
            listItem: "list__item"
          };
    
          return Object.assign(defaultClasses, this.listClasses);
        }
      }
    }
    </script>
    
    私たちがここでしていることは、propクラス(カスタムクラス)を優先し、デフォルトクラスをフォールバックとして持つことです.

    養豚場


    我々は、リストのコンポーネントで大きな進歩を行ったが、まだ提供する必要があります.

    追加設定


    我々は実装することができますmergeDefault プロップ設定で、デフォルトクラスをマージするかどうかを決定しますlistClasses 支柱は部分的に供給される.
    <script>
    export default {
      props: {
        ...
        mergeDefault: {
          type: Boolean,
          default: true
        }
      },
      computed: {
        obtainClasses() {
          const defaultClasses = {
            listContainer: "list-container",
            list: "list",
            listItem: "list__item"
          };
    
          if (this.mergeDefault)
            return Object.assign(defaultClasses, this.listClasses);
          return this.listClasses ? this.listClasses : defaultClasses;
        }
      }
    }
    </script>
    

    フィニッシュ


    渡すクラス名は、カスタマイズする子要素のクラスと一致しません.
    デフォルトクラスをオーバーライドしなかったので、デフォルトでカスタムクラスを優先します.
    Childと同じ名前のクラスを渡すのは、もしあればAccept CSS宣言を提供することとは別に何もしなかったようです.
    追加の対策については、コンポーネント内の固有の命名クラスを実装できます.
    <script>
    export default {
      ...
      computed: {
        obtainClasses() {
          const defaultClasses = {
            listContainer: "_list-container",
            list: "_list",
            listItem: "_list__item"
          };
          ...
        }
    }
    </script>
    
    <style scoped>
    /* here we name our classes with underscore in the beginning */
    ._list-container { /* */ }
    
    ._list { /* */ }
    
    ._list__item { /* */ }
    </style>
    
    よくできた!当社のリストコンポーネントは、デフォルトとカスタマイズ可能なスタイルの機能があります.