Vue 中作用域 CSS 的那點事

前言

在這裏插入圖片描述

首先,我們來回憶一下「CSS 作用域」這一概念,它的本質是通過讓每一個選擇器成爲一個「unique」的存在,這樣就自然而然地形成了作用域。

而提到「Vue」中「作用域 CSS」,我想大家應該立即想到以 scoped 的方式形成的帶有作用域的 css。但是,值得一提的是,在「Vue」中還支持了一種「作用域 CSS」,即「CSS Module」。

提及 「CSS Module」,想必大家會有點陌生,相信有很多同學在平常開發中都是用 scoped 來實現「Vue」組件中的「作用域CSS」。所以,今天我們就來詳細認知一下這兩者。

一、scoped 作用域

「scoped 作用域」是「Vue」通過「postcss」來實現對每一個在 scoped 標籤中定義的選擇器的特殊作用域標識,例如:

<template>
  <div id="app">
    <div class="out-box">
    </div>
  </div>
</template>

<style lang="scss" scoped>
#app {
  .out-box {
    width: 200px;
    height: 200px;
    background-color: #faa;
  }
}
</style>

標識後展示在頁面上的:

<div data-v-7ba5bd90 id="app">
    <div data-v-7ba5bd90 class="out-box">
    </div>
</div>

<style>
#app .out-box[data-v-7ba5bd90] {
    width: 200px;
    height: 200px;
    background-color: #faa;
}
</style>

可以看到,本質上是在原有的「選擇器」的基礎上通過「postcss」加上了一串 attr

並且,在我們平常開發中,很常見的場景就是我們在使用一些已有的組件或第三方組件時,我們需要對原有組件的樣式進行一些微小的改動。那麼,這個時候就需要使用穿透來實現樣式的改動,例如:

<style>
div >>> .out-box {
    background-color: #aaf;
}
</style>

這裏需要注意的是 >>> 只是一種穿透方式,並不是所有場景下都是可以用 >>> 實現。例如,「iView」需要使用 /deep/ 的方式,「ElementUI」需要使用 ::v-deep 的方式。

二、CSS Module 作用域

相比較「scoped 作用域」,「CSS Modeul 作用域」它所具備的能力更強,所以內容也相對較多。

什麼是 CSS Module

「CSS Module」指的是可以將一個定義好的「CSS」文件以變量的形式導入,然後通過使用這個變量對「HTML」中的元素進行樣式的修飾,例如:

a.css

.box {
    width: 100%;
    height: 100%;
    background-color: #afa;
}

b.js

import style from './a.css'

const boxElem = document.createElement('div');
boxElem.className = style.box

然後,渲染到頁面的時候,它對應的 HTML 看起來會是這樣:

<div class="a-box_jlpNx"></div>

可以看出,此時的「類選擇器」同樣是隨機生成的,這也是「CSS Module」形成作用域的所在。

值得一提的是,「CSS Module」還具備其他的能力,例如可以定義全局的「選擇器」,寫起來會是這樣:

:global(.title) {
  color: green;
}

接下來的使用和局部的一樣。至於,其他用法,有興趣的同學可以去 GitHub 上自行閱讀

CSS Module 在 Vue 中的應用

回到本文所說的,在「Vue」中也對「CSS Module」做了相應的支持,當我們在 style 標籤上添加 module 屬性時,「Vue」 會在當前「組件實例」上注入一個計算屬性 $style,然後我們可以通過 $style 來使用我們定義好的「選擇器」,例如:

<template>
  <div>
    <div :class="$style['inner-box']"></div>
  </div>
</template>

<style lang="scss" module>
.inner-box {
  width: 100px;
  height: 100px;
  background-color: #aaf;
}
</style>

然後,它渲染到頁面時,對於的 HTML 會是這樣:

<div class="HelloWorld_inner-box_jlpNx"></div>

那麼,這個時候,又回到和「scoped 作用域」一樣的問題,使用了「CSS Module」來定義組件的樣式,那麼我在使用它的時候,如何進行覆蓋?

標準的答案,對於「CSS Module」並沒有覆蓋的說法,有的只是爲一個組件設置不同的主題。

但是,如果真的需要弄,那隻能通過對該模塊對應的 style 標籤中定義你需要的樣式,然後根據傳入組件的 props 來動態綁定 class

那麼,爲組件設置主題,我們需要在設計組件的時候,對這個組件預留 props ,並將該 props 添加到 $style 中,然後在這個組件中的相應元素中使用,例如:

Box.vue 組件

<template>
  <div>
    <div :class="$style[themeColor]"></div>
  </div>
</template>

<script>
export default {
  props: {
    themeColor: {
        type: String,
        default: 'line'
    }
  }
};
</script>

<style lang="scss" module>
.line {
  width: 100px;
  height: 100px;
  background-color: #aaf;
}
.card {
  background-color: #aaf;
}
</style>

然後,我們在使用該組件的時候通過 props 傳入 line 或者 card,從而實現切換組件不同的背景色。

三、兩種方式的優劣勢

「scoped 作用域」:

  • 對組件沒有硬性要求
  • 不易於管理組件樣式,需要藉助第三方變量定義支持
  • 易於覆蓋組件樣式,即通過穿透來實現對樣式的覆蓋

「CSS Module 作用域」:

  • 適合於高度沉澱下的組件使用
  • 易於管理組件樣式,即可以通過 style 管理組件中的選擇器
  • 組件樣式無法通過外部直接覆蓋

這裏所說的管理,是指通過JavaScript便捷地控制組件樣式。

寫在最後

其實,對比「scoped」和「CSS Module」兩者,各有千秋。至於,要用哪一着得看具體需求,建議大項目中可以使用「CSS Module」,小項目的話用用「scoped」應該綽綽有餘。

寫作不易,如果你覺得有收穫的話,可以帥氣三連擊!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章