【CSS模塊化】(1) webpack之Local Scope

CSS是一門15分鐘就能入門,但是卻需要很長(zhǎng)很長(zhǎng)的時(shí)間才能掌握好的語言只估。它有著它自身的一些復(fù)雜性與局限性。其中非常重要的一點(diǎn)就是着绷,本身不具備模塊化的能力蛔钙。

面臨的問題

你可能會(huì)說,CSS有@import功能荠医。然而吁脱,我們都知道,這里的@import僅僅是表示引入相應(yīng)的CSS文件彬向,但其模塊化核心問題并未解決——CSS文件中的任何一個(gè)選擇器都會(huì)作用在整個(gè)文檔范圍里兼贡。

因此,其實(shí)我們面臨的最大問題就是——所有的選擇器都是在一個(gè)全局作用域內(nèi)的娃胆。一旦引入一個(gè)新的CSS文件紧显,就有著與預(yù)期不符的樣式表現(xiàn)的風(fēng)險(xiǎn)(因?yàn)橐恍┎豢深A(yù)測(cè)的選擇器)。

而如今的前端項(xiàng)目規(guī)模越來越大缕棵,已經(jīng)不是過去隨便幾個(gè)css、js文件就可以搞定的時(shí)代涉兽。與此同時(shí)的招驴,對(duì)于一個(gè)大型的應(yīng)用,前端開發(fā)團(tuán)隊(duì)往往也不再是一兩個(gè)人枷畏。隨著項(xiàng)目與團(tuán)隊(duì)規(guī)模的擴(kuò)大别厘,甚至是項(xiàng)目過程中人員的變動(dòng),如何更好進(jìn)行代碼開發(fā)的管理已經(jīng)成為了一個(gè)重要問題拥诡。

回想一下触趴,有多少次:

  • 我們討論著如何對(duì)class進(jìn)行有效的命名氮发,以避免協(xié)作開發(fā)時(shí)的沖突;
  • 我們面對(duì)一段別人寫的css冗懦、html代碼爽冕,想要去修改,然后瘋狂查找披蕉、猜測(cè)每個(gè)類都是什么作用颈畸,哪些是可以去掉的,哪些是可以修改的——到最后我們選擇重新添加一個(gè)新的class没讲;
  • 我們準(zhǔn)備重構(gòu)代碼時(shí)眯娱,重構(gòu)也就成了重寫
  • ……

寫一段CSS往往并不是困難所在,難得確實(shí)團(tuán)隊(duì)的合作與后續(xù)的維護(hù)爬凑。

針對(duì)這些實(shí)際項(xiàng)目中的問題徙缴,一直以來,開發(fā)者們都在探索解決方案嘁信。這一篇文章主要介紹了于样,如何在webpack中使用一種類似“CSS模塊化”的解決方案———Local Scope,來規(guī)避一些開發(fā)中的問題吱抚。

什么是Local Scope

通常來說百宇,CSS中的所有選擇器可以算是“全局作用域”。而“Local Scope”顧名思義秘豹,使CSS具有類似于局部作用域的能力携御,同時(shí)搭配類似JavaScript中模塊化的寫法,到達(dá)CSS模塊化的效果既绕。

這么說可能有些抽象啄刹,我們可以來看一個(gè)例子。

在webpack中引入css往往是這樣的:

// index.css
.title {
    font-size: 30px;
    color: #333;
}

// index.js
import './index.css';

funciont createTitle(str) {
    var title = document.createElement('h1');
    title.appendChild(document.createTextNode(str));
    title.setAttribute('class', 'title');
    document.body.appendChild(title);
}

createTitle('Hi!');

由于凄贩,webpack中將js誓军、css、png等這些資源都視為模塊疲扎,所以可以通過import導(dǎo)入昵时。但是,實(shí)際上椒丧,對(duì)于導(dǎo)入的所有css壹甥,其“地位”都是平等的,都是在全局有效的壶熏。例如:

// index.css
.title {
    font-size: 30px;
    color: #333;
}

// other.css
.title {
    font-size: 15px;
    color: #999;
}

// index.js
import './index.css';
import './other.css';

funciont createTitle(str) {
    var title = document.createElement('h1');
    title.appendChild(document.createTextNode(str));
    title.setAttribute('class', 'title');
    document.body.appendChild(title);
}

createTitle('Hi!');

當(dāng)我們引入了新的CSS文件other.css后句柠,其中的.title和index.css中的.title有著同樣的“作用域”——全局玻靡。

回想一下在JavaScript中:

// a.js
var a = 1;

// other.js
var a = 2;

// index.html
<script src="./a.js"></script>
<script src="./other.js"></script>
<script>
    console.log(a); // 2
</script>

如果某個(gè)html頁面通過script標(biāo)簽引入這兩js文件木人,那么a的值必定會(huì)有沖突条辟,其中一個(gè)會(huì)被覆蓋媒熊。如果使用模塊化的方式,可以變成:

// a.js
export var a = 1;

// other.js
export var a = 2;

// app.js
import {a} from './a';
import {a as other} from './other';

console.log(a); // 1
console.log(a); // 2

而所謂的Local Scope就是webpack中在CSS上針對(duì)這種問題的一個(gè)解決方案谜酒。類似JavaScript的模塊化叹俏,通過對(duì)CSS文件進(jìn)行模塊引用與導(dǎo)出的方式,能夠在開發(fā)時(shí)甚带,更有效得控制各個(gè)class的作用范圍她肯。

使用方法

首先,需要在webpack中對(duì)css-loader進(jìn)行一定的配置鹰贵。

// loader: 'css-loader',
// options: {
//     modules: true,
//     localIdentName: '[local]__[name]--[hash:base64:5]'
// }
const config = {
    entry: './src/index.js',
    output: {
        path: path.resolve(__dirname, 'dist'),
        filename: 'bundle.js'
    },
    module: {
        rules: [{
            test: /\.css$/,
            use: [{
                loader: 'style-loader'
            }, {
                loader: 'css-loader',
                options: {
                    modules: true,
                    localIdentName: '[local]__[name]--[hash:base64:5]'
                }
            }]
        }]
    }
};

然后晴氨,我們還是使用前一節(jié)例子中的那個(gè)場(chǎng)景:

// index.css
:local .title {
    font-size: 30px;
    color: #333;
}

// other.css
:local .title {
    font-size: 15px;
    color: #999;
}

// index.js
import styles from './index.css';
import others from './other.css';

funciont createTitle(str) {
    var title = document.createElement('h1');
    title.appendChild(document.createTextNode(str));
    // styles.title  font-size: 30px;color: #333;
    title.setAttribute('class', styles.title);
    document.body.appendChild(title);
}

createTitle('Hi!');

其中需要注意的有三個(gè)地方:

  • 第一個(gè)是在CSS文件中的,類選擇器前多了:local這個(gè)語法碉输。通過添加:local就可以指示webpack籽前,這不是一個(gè)“全局”的選擇器(當(dāng)然,實(shí)際上也是全局的敷钾,后面會(huì)簡(jiǎn)單解釋)枝哄。
  • 第二個(gè)地方是在js文件中,將import 'index.css'變?yōu)榱?code>import styles from './index.css'阻荒。是不是看著很熟悉挠锥,沒錯(cuò),和JavaScript中的模塊化方案用法一樣侨赡。
  • 第三個(gè)地方蓖租,在使用到該class的地方,由原來的title.setAttribute('class', ‘title’)變?yōu)榱?code>title.setAttribute('class', styles.title)羊壹。這樣我們可以選擇在一部分dom元素上使用styles.title蓖宦,即index.css的樣式;在另一部分dom元素上使用other.css的樣式油猫。

這樣就解決了我們之前提到的問題稠茂。

當(dāng)然,有些時(shí)候情妖,我們希望類選擇器中的某一部分仍然是“全局”的睬关,那么我們可以這么寫:

:local .title :global(.sub-title) { color: #666; }

關(guān)于Local Scope

雖然我們上面說了這么多次的“模塊化”、“作用域”毡证、“全局”电爹,然而,實(shí)際上情竹,對(duì)于CSS這門語言來說,它在自己本身的邏輯上是不具備這些特點(diǎn)的。而webpack中Local Scope的相關(guān)方案秦效,其實(shí)也并不是(CSS本身邏輯支持的)真正意義上所謂的模塊化雏蛮。所以很多地方我都打上了引號(hào)。

如果對(duì)著打包后的頁面阱州,打開chrome控制臺(tái)挑秉,會(huì)發(fā)現(xiàn),我們的html是這個(gè)樣子的

<body>
    <h1 class="title__index--330EV">Hi!</h1>
</body>

h1標(biāo)簽的class并不是我們?cè)贑SS中所寫的title苔货,而是一串奇怪的字符串title__index--330EV犀概。

當(dāng)使用webpack進(jìn)行打包時(shí),由于檢查到:local這個(gè)語法夜惭,因此會(huì)為.title這個(gè)class生成一個(gè)新的class名稱姻灶,而我們?cè)趈s文件中所使用的styles.title對(duì)應(yīng)的就是這個(gè)新的classname。

所以可以理解诈茧,其實(shí)當(dāng)前的CSS語法邏輯中中并沒有實(shí)際意義上所謂的local scope产喉,但是,通過webpack打包時(shí)的操作敢会,我們會(huì)為每個(gè):local的class生成一個(gè)唯一的名稱曾沈,而我們使用樣式實(shí)際是指向了這個(gè)classname。這就實(shí)現(xiàn)了兩個(gè)CSS文件中鸥昏,相同名稱的class在使用時(shí)就不會(huì)有沖突了塞俱,相當(dāng)于避開了“全局作用域”。

如果打開打包后的bundle.js,我們可以發(fā)現(xiàn)一段很有趣的代碼

// ……其余省略
// exports
exports.locals = {
    "title": "title__index--330EV"
};

// ……其余省略
// exports
exports.locals = {
    "title": "title__other--3vRzX"
};

這是在兩個(gè)不同的模塊內(nèi)的部分吏垮。上面一個(gè)就是導(dǎo)出的index.css中對(duì)應(yīng)的classname障涯,下面一個(gè)就是other.css的。通過styles.title就可以引用到title__index--330EV這個(gè)實(shí)際值惫皱。

最后像樊,再來說一下title__index--330EV這個(gè)值得由來。在上一節(jié)的一開始旅敷,我們對(duì)webpack進(jìn)行了配置生棍,其中有一行

localIdentName: '[local]__[name]--[hash:base64:5]'

其實(shí)就是指示了唯一標(biāo)識(shí)的命名方式:local是class的名稱,name是文件的名稱媳谁,而最后加上hash值涂滴。當(dāng)然,你完全可以使用其他你喜歡的方式晴音。默認(rèn)是使用[hash:base64]柔纵。

最后

其實(shí)webpack中的CSS模塊化方案Local Scope,粗淺的來說也是通過生成唯一的classname來避免沖突锤躁,控制作用范圍搁料。只是和BEM不同,BEM是一個(gè)建議標(biāo)準(zhǔn),更多的還是人為的操控郭计,而webpack中的Local Scope則提供了一個(gè)完整的模塊化與打包方案霸琴。在一定程度上提高了開發(fā)的效率,降低了錯(cuò)誤率昭伸。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末梧乘,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子庐杨,更是在濱河造成了極大的恐慌选调,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,941評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件灵份,死亡現(xiàn)場(chǎng)離奇詭異仁堪,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)各吨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,397評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門枝笨,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人揭蜒,你說我怎么就攤上這事横浑。” “怎么了屉更?”我有些...
    開封第一講書人閱讀 165,345評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵徙融,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我瑰谜,道長(zhǎng)欺冀,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,851評(píng)論 1 295
  • 正文 為了忘掉前任萨脑,我火速辦了婚禮隐轩,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘渤早。我一直安慰自己职车,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,868評(píng)論 6 392
  • 文/花漫 我一把揭開白布鹊杖。 她就那樣靜靜地躺著悴灵,像睡著了一般。 火紅的嫁衣襯著肌膚如雪骂蓖。 梳的紋絲不亂的頭發(fā)上积瞒,一...
    開封第一講書人閱讀 51,688評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音登下,去河邊找鬼茫孔。 笑死叮喳,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的缰贝。 我是一名探鬼主播嘲更,決...
    沈念sama閱讀 40,414評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼揩瞪!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起篓冲,我...
    開封第一講書人閱讀 39,319評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤李破,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后壹将,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體嗤攻,經(jīng)...
    沈念sama閱讀 45,775評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,945評(píng)論 3 336
  • 正文 我和宋清朗相戀三年诽俯,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了妇菱。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,096評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡暴区,死狀恐怖闯团,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情仙粱,我是刑警寧澤房交,帶...
    沈念sama閱讀 35,789評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站伐割,受9級(jí)特大地震影響候味,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜隔心,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,437評(píng)論 3 331
  • 文/蒙蒙 一白群、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧硬霍,春花似錦帜慢、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,993評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至耐床,卻和暖如春密幔,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背撩轰。 一陣腳步聲響...
    開封第一講書人閱讀 33,107評(píng)論 1 271
  • 我被黑心中介騙來泰國打工胯甩, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留昧廷,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,308評(píng)論 3 372
  • 正文 我出身青樓偎箫,卻偏偏與公主長(zhǎng)得像木柬,于是被迫代替她去往敵國和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子淹办,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,037評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容

  • 無意中看到zhangwnag大佬分享的webpack教程感覺受益匪淺眉枕,特此分享以備自己日后查看,也希望更多的人看到...
    小小字符閱讀 8,171評(píng)論 7 35
  • 版權(quán)聲明:本文為博主原創(chuàng)文章姥宝,未經(jīng)博主允許不得轉(zhuǎn)載。 webpack介紹和使用 一恐疲、webpack介紹 1腊满、由來 ...
    it筱竹閱讀 11,137評(píng)論 0 21
  • 前言 WebPack 是什么? WebPack 是什么培己,WebPack 可以看做是模塊打包機(jī):它做的事情是碳蛋,分析你...
    Promise__閱讀 1,128評(píng)論 3 12
  • 前兩部分我們已經(jīng)完成了博客頁面的展示和后臺(tái)頁面的展示: React技術(shù)棧+Express+Mongodb實(shí)現(xiàn)個(gè)人博...
    SamDing閱讀 5,466評(píng)論 1 12
  • 猶記得自小表達(dá)欲望低的我,中學(xué)時(shí)代無數(shù)次為800字的作文困擾省咨,心中無所觸動(dòng)疮蹦,筆下難成文章。最終只能套用總-分-總似...
    小猴梨梨閱讀 255評(píng)論 0 2