我們都知道,外部引入 CSS 有2種方式,link標(biāo)簽和@import。
它們有何本質(zhì)區(qū)別,有何使用建議,在考察外部引入 CSS 這部分內(nèi)容時(shí),經(jīng)常被提起。
如今,很多學(xué)者本著知其然不欲知其所以然的學(xué)習(xí)態(tài)度,不求甚解,只求結(jié)論。
所以,本文遵循 css hack 的漸進(jìn)識(shí)別原則,
結(jié)論 → 區(qū)別 → 爭議 → 細(xì)節(jié) → 祖墳 → 感想,逐漸加深理論層級(jí),
力爭每個(gè) level 的讀者,都能 get 到自己想要的內(nèi)容,不必繼續(xù)閱讀下去。
結(jié)論
就結(jié)論而言,強(qiáng)烈建議使用link標(biāo)簽,慎用@import方式。
這樣可以避免考慮@import的語法規(guī)則和注意事項(xiàng),避免產(chǎn)生資源文件下載順序混亂和http請(qǐng)求過多的煩惱。
區(qū)別
1.從屬關(guān)系區(qū)別
@import是 CSS 提供的語法規(guī)則,只有導(dǎo)入樣式表的作用;link是HTML提供的標(biāo)簽,不僅可以加載 CSS 文件,還可以定義 RSS、rel 連接屬性等。
2.加載順序區(qū)別
加載頁面時(shí),link標(biāo)簽引入的 CSS 被同時(shí)加載;@import引入的 CSS 將在頁面加載完畢后被加載。
3.兼容性區(qū)別
@import是 CSS2.1 才有的語法,故只可在 IE5+ 才能識(shí)別;link標(biāo)簽作為 HTML 元素,不存在兼容性問題。
4.DOM可控性區(qū)別
可以通過 JS 操作 DOM ,插入link標(biāo)簽來改變樣式;由于 DOM 方法是基于文檔的,無法使用@import的方式插入樣式。
5.權(quán)重區(qū)別(該項(xiàng)有爭議,下文將詳解)
link引入的樣式權(quán)重大于@import引入的樣式。
爭議
不知從什么時(shí)候開始,當(dāng)你在網(wǎng)上搜索link和@import的區(qū)別時(shí),千篇一律的答案里就悄悄的多了一句“link引入的樣式權(quán)重大于@import引入的樣式”。
但是并沒有一份答案,附帶著對(duì)這句話的任何解釋或?qū)嵗?/p>
這句話究竟是什么意思,該怎么理解呢?
發(fā)揚(yáng)探索精神,我們不妨繼續(xù)查閱資料。后來發(fā)現(xiàn),還是有不少文章和帖子,對(duì)這句話表示質(zhì)疑,進(jìn)而自己寫了 demo 去驗(yàn)證,驗(yàn)證的結(jié)果,確實(shí)無法與這句話相吻合。
而且,筆者也并未發(fā)現(xiàn)能清楚、正確、有理有據(jù)的解釋這個(gè)結(jié)論到底對(duì),還是不對(duì)的文章。
那么這個(gè)結(jié)論,最初是從哪里來的,可能已經(jīng)無從考證了。
換個(gè)思維方式,不去爭辯它的對(duì)錯(cuò)了,探索未果,我們就從這個(gè)結(jié)論的核心關(guān)鍵詞“權(quán)重”出發(fā),去研究它。
說到“權(quán)重”,有必要再解釋一下:CSS 中的權(quán)重,指的是選擇器的優(yōu)先級(jí)。
CSS 選擇器的權(quán)重高,即選擇器的優(yōu)先級(jí)高。
CSS 的優(yōu)先級(jí)特性表現(xiàn)為,對(duì)同一 HTML 元素設(shè)置樣式時(shí),不同選擇器的優(yōu)先級(jí)不同,優(yōu)先級(jí)低的樣式將被高優(yōu)先級(jí)的樣式層疊掉。
CSS 權(quán)重優(yōu)先級(jí)順序簡單表示為:
!important > 行內(nèi)樣式 > ID > 類、偽類、屬性 > 標(biāo)簽名 > 繼承 > 通配符
為了便于理解權(quán)重的計(jì)算方式,我們按以下方式進(jìn)行數(shù)值假設(shè)分析:
| 選擇器 | 權(quán)重 |
|---|---|
| 通配符 | 0 |
| 標(biāo)簽 | 1 |
| 類/偽類/屬性 | 10 |
| ID | 100 |
| 行內(nèi)樣式 | 1000 |
| important | 1/0(無窮大) |
再舉實(shí)例:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<style type="text/css">
#myid { /* id選擇器權(quán)重為100 */
background-color: pink;
}
#divid .myspan input { /* 權(quán)重為 100 + 10 + 1 = 111 */
background-color: yellow;
}
input[type="button"] { /* 權(quán)重為 10 */
color: white !important; /* !important權(quán)重為無窮大 */
}
input.myclass { /* 此為標(biāo)簽指定式選擇器,權(quán)重為 1 + 10 = 11 */
color: black;
}
</style>
</head>
<body>
<div id="divid">
<span class="myspan">
<input type="button" id="myid" class="myclass" name="myname"
value="點(diǎn)我" style=" color: green;">
<!-- style樣式的權(quán)重為1000 -->
</span>
</div>
</body>
</html>
每個(gè)樣式的權(quán)重值,都在實(shí)例中,以注釋的形式標(biāo)明。
根據(jù)權(quán)重值可知,最終,這個(gè)按鈕的樣式一定是,藍(lán)色背景,白色字,結(jié)果如下圖:

存在!important時(shí),不作他想,一定是權(quán)重最大的樣式。
既然我們了解了,CSS 中的權(quán)重是怎么回事,那回到主題,“link引入的樣式權(quán)重大于@import引入的樣式”,
難道 CSS 的引入方式也有權(quán)重嗎?其實(shí)我們不必糾結(jié)它是否有權(quán)重之說,我們只需理論結(jié)合實(shí)際的去分析,各種情況下,結(jié)果如何即可。
現(xiàn)有如下3個(gè)css文件:
/* green.css */
div {
background-color: green;
border: 3px solid red;
}
/* yellow.css */
div {
background-color: yellow;
border: 3px solid black;
}
/* blue.css */
@import url("green.css");
div{
background-color: blue;
}
實(shí)例1:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 實(shí)例1\. link標(biāo)簽引入yellow.css,內(nèi)聯(lián)樣式引入green.css -->
<link rel="stylesheet" href="yellow.css">
<style type="text/css">
@import url("green.css");
</style>
</head>
<body>
<div style="width: 50px; height: 50px;"></div>
<!-- 盒子為,綠色背景,紅色邊框,即green.css生效 -->
</body>
</html>
實(shí)例1結(jié)果如下圖:

實(shí)例2:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 實(shí)例2\. 內(nèi)聯(lián)樣式引入green.css,link標(biāo)簽引入yellow.css -->
<style type="text/css">
@import url("green.css");
</style>
<link rel="stylesheet" href="yellow.css">
</head>
<body>
<div style="width: 50px; height: 50px;"></div>
<!-- 盒子為黃色背景,黑色邊框,即yellow.css生效 -->
</body>
</html>
實(shí)例2結(jié)果如下圖:

對(duì)比實(shí)例1和實(shí)例2這兩個(gè)正好相反的結(jié)果可知,link和@import并沒有產(chǎn)生類似權(quán)重的效果,只是單純的體現(xiàn)了CSS的層疊性,寫在后面的樣式,覆蓋前面的樣式。
實(shí)例3:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 實(shí)例3\. 內(nèi)聯(lián)樣式引入green.css,內(nèi)聯(lián)樣式中設(shè)置粉色背景 -->
<style type="text/css">
@import url("green.css");
div {
background-color: pink;
}
</style>
</head>
<body>
<div style="width: 50px; height: 50px;"></div>
<!-- 盒子為粉色背景,紅色邊框,即green.css已生效,但背景色被內(nèi)聯(lián)樣式層疊為粉色 -->
</body>
</html>
實(shí)例3結(jié)果如下圖:

實(shí)例4:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Document</title>
<!-- 實(shí)例4\. link標(biāo)簽引入blue.css,blue.css中引入green.css -->
<link rel="stylesheet" href="blue.css">
</head>
<body>
<div style="width: 50px; height: 50px;"></div>
<!-- 盒子為藍(lán)色背景,紅色邊框,即green.css已生效,但背景色被blue.css層疊為藍(lán)色 -->
</body>
</html>
實(shí)例4結(jié)果如下圖:

分析實(shí)例3和實(shí)例4的結(jié)果可知:
對(duì)于實(shí)例3,我們看到紅色邊框,證明內(nèi)聯(lián)樣式中使用@import引入的green.css已經(jīng)生效,但其背景樣式被內(nèi)聯(lián)樣式中的粉色背景層疊掉,這個(gè)現(xiàn)象表明,@import不只是如我們看到的那樣,處于內(nèi)聯(lián)樣式頂部,其被引入的樣式,在結(jié)構(gòu)上,也確實(shí)是被置于內(nèi)聯(lián)樣式之前,所以內(nèi)聯(lián)樣式才能夠?qū)盈B掉它。
同理,實(shí)例4中,在link標(biāo)簽引入的blue.css文件內(nèi),頂部同樣存在@import引入的green.css,紅色邊框依然可以證明,green.css已經(jīng)生效,但其背景樣式被blue.css本身的藍(lán)色背景層疊掉,@import引入的樣式在blue.css中也是被置于它本身樣式之前的。
到此為止,我展開了大膽的猜想,“link引入的樣式權(quán)重大于@import引入的樣式”,這個(gè)結(jié)論的給出者,是想告訴大家:
在link標(biāo)簽引入的 CSS 文件中,使用@import時(shí)需注意,如果已經(jīng)存在相同樣式,@import引入的這個(gè)樣式將被該 CSS 文件本身的樣式層疊掉,表現(xiàn)出link標(biāo)簽引入的樣式權(quán)重大于@import引入的樣式這樣的直觀效果。
對(duì)于我設(shè)想的結(jié)論,似乎挺能說通的,畢竟這是實(shí)踐出的結(jié)果。
那些驗(yàn)證過此結(jié)論的前人,他們都是在一個(gè) HTML 頁面中,一前一后分別使用link和內(nèi)聯(lián)樣式的@import去比較的,我在實(shí)例1和實(shí)例2中也是如此做的,并不能反推出“link引入的樣式權(quán)重大于@import引入的樣式”這個(gè)結(jié)論,所以,我不自量力的認(rèn)為,這個(gè)結(jié)論其實(shí)最初只是丟了個(gè)已知條件而已。
那么我們一起把這個(gè)結(jié)論重新梳理一下:在link標(biāo)簽引入的 CSS 文件中使用@import時(shí),相同樣式將被該 CSS 文件本身的樣式層疊。
Ps.首先感謝各種看官的閱讀。筆者屬于學(xué)習(xí)階段,學(xué)識(shí)尚淺,雖然本文結(jié)論已得到筆者編碼驗(yàn)證,但不排除筆者大腦短路、措辭有誤的可能,有緣閱讀到此處的都是真愛,希望諸位大拿、大牛、大仙、大圣、大神們不吝賜教,及時(shí)指正,避免誘導(dǎo)萌新誤入歧途,再次向你們表達(dá)筆者的謝意!
細(xì)節(jié)
既然已經(jīng)說了這么多,就順便提一下關(guān)于@import使用時(shí)的其它細(xì)節(jié)。
在《CSS權(quán)威指南》中寫道:
@import一定要寫在除@charset外的其他任何 CSS 規(guī)則之前,如果置于其它位置將會(huì)被瀏覽器忽略,而且,在@import之后如果存在其它樣式,則@import之后的分號(hào)是必須書寫,不可省略的。
到此為止,似乎事情都弄清楚了,但是突然又有個(gè)疑點(diǎn)浮現(xiàn)出來:
在討論區(qū)別的時(shí)候,不是說加載頁面時(shí),link標(biāo)簽引入的 CSS 先于@import引入的 CSS 加載嗎,那link標(biāo)簽引入的樣式又怎會(huì)把@import引入的樣式層疊掉呢?
要回答這個(gè)問題,首先我們要一起明確一些有關(guān)瀏覽器的概念:
瀏覽器執(zhí)行過程可以簡單分為加載、解析、渲染,這三個(gè)步驟。
加載:根據(jù)請(qǐng)求的URL進(jìn)行域名解析,向服務(wù)器發(fā)送請(qǐng)求,接收響應(yīng)文件(如 HTML、JS、CSS、圖片等)。
解析:對(duì)加載到的資源(HTML、JS、CSS等)進(jìn)行語法解析,構(gòu)建相應(yīng)的內(nèi)部數(shù)據(jù)結(jié)構(gòu)(比如HTML的DOM樹,JS對(duì)象的屬性表,CSS的樣式規(guī)則等)。
渲染:構(gòu)建渲染樹,對(duì)各個(gè)元素進(jìn)行位置計(jì)算、樣式計(jì)算等,然后根據(jù)渲染樹完成頁面布局及繪制的過程(可以理解為“畫”頁面元素)。
這幾個(gè)過程不是完全孤立的,會(huì)有交叉,比如HTML加載后就會(huì)進(jìn)行解析,然后拉取HTML中指定的CSS、JS等。`
現(xiàn)在,我們應(yīng)該已經(jīng)了解了加載和渲染的概念,明白它們是兩個(gè)不同的過程,那么對(duì)上文中拋出的疑問繼續(xù)追問:
link先于@import加載,是不是也先于@import渲染呢?
實(shí)際上,渲染的動(dòng)作一般都會(huì)執(zhí)行多次,最后一次渲染,一定是依據(jù)之前加載過的所有樣式整合后的渲染樹進(jìn)行繪制頁面的,已經(jīng)被渲染過的頁面元素,也會(huì)被重新渲染。
那么我們就可以把@import這種導(dǎo)入 CSS 文件的方式理解成一種替換,CSS 解析引擎在對(duì)一個(gè) CSS 文件進(jìn)行解析時(shí),如在文件頂部遇到@import,將被替換為該@import導(dǎo)入的 CSS 文件中的全部樣式。
峰回路轉(zhuǎn),柳暗花明,終于弄明白為何@import引入的樣式,會(huì)被層疊掉了。其雖然后被加載,卻會(huì)在加載完畢后置于樣式表頂部,最終渲染時(shí)自然會(huì)被下面的同名樣式層疊。
至此為止,“link引入的樣式權(quán)重大于@import引入的樣式”這個(gè)結(jié)論,我終于為它圓了場。但愿此結(jié)論的作者,本意真如我的猜測(cè),否則若是我多心而跑偏了的話,不敢想象這背后究竟隱藏著多大的秘密。
祖墳
有些細(xì)心而專業(yè)的讀者可能已經(jīng)發(fā)現(xiàn)了,我用自己的思路和措辭,粗略的解釋了有關(guān) CSS 加載和渲染的知識(shí),有些涉世未深的前端愛好者可能會(huì)一頭霧水,無法作為系統(tǒng)學(xué)習(xí)的依據(jù)。這不打緊,出來混,祖墳總是要刨的,想要透徹的學(xué)習(xí)相關(guān)內(nèi)容,進(jìn)一步了解底層原理的學(xué)者,我早已為你備下了豐厚的見面禮~
有關(guān)link和@import在性能分析方面的比較,國外的高手早在多年前就曾執(zhí)過筆:
愛好母語的請(qǐng)戳我:https://www.stevesouders.com/blog/2009/04/09/dont-use-import/
閱讀英語的請(qǐng)戳我:https://www.qianduan.net/high-performance-web-site-do-not-use-import/
有關(guān)瀏覽器內(nèi)部工作原理的神作,也是幾年前出自歪果仁大牛:
1 為普通話:https://kb.cnblogs.com/page/129756/
2 for English:http://taligarsiel.com/Projects/howbrowserswork1.htm
對(duì)知識(shí)有所渴望的你,趕快點(diǎn)擊上面的傳送門,相信你一定會(huì)有豐富的收獲。
感想
我喜歡讀別人探究類的技術(shù)文章,不只是可以學(xué)習(xí)技術(shù)上的知識(shí),更是可以學(xué)習(xí)別人探索的精神和思考問題的邏輯?;蛟S已經(jīng)有過不少前人如我一樣,對(duì)本文的爭議結(jié)論,有所質(zhì)疑,前思后想過一番,得出了自己的見解,只是沒有成文,或者沒有發(fā)表而已。
那么其他人呢,就只是單純的復(fù)制結(jié)論或記憶結(jié)論么。
其實(shí)我覺得,在咱們這個(gè)領(lǐng)域,不求甚解可以,人云亦云不行。一句含有信息量的句子,進(jìn)入大腦時(shí),總得自己考慮考慮吧,不要讓我們的大腦充當(dāng)轉(zhuǎn)發(fā)器,最不濟(jì),也得是個(gè)解析器吧。