剛開(kāi)始使用vue的時(shí)候容易被里面的樣式搞懵:
樣式可以在main.js中引入,在模塊js文件中引入,在組件中的style標(biāo)簽引入,在組件中的script標(biāo)簽引入,還可以在index.html的body中引入。
我不禁要問(wèn):
1.從不同位置引入的樣式到底是什么關(guān)系?
2.在實(shí)際定義樣式時(shí)應(yīng)該定義在哪個(gè)位置,以避免樣式產(chǎn)生的沖突?
筆者之前有份工作是一名進(jìn)口電子產(chǎn)品的測(cè)試員,不僅要閱讀英文電子產(chǎn)品的說(shuō)明書(shū),還要對(duì)產(chǎn)品功能進(jìn)行測(cè)試,搞清楚使用方法,編寫中文產(chǎn)品說(shuō)明書(shū)。
得益于這份測(cè)試工作,我發(fā)現(xiàn)測(cè)試的過(guò)程其實(shí)就是在學(xué)習(xí):
看文檔是“學(xué)”,而測(cè)試是“習(xí)”。
紙上得來(lái)終覺(jué)淺,絕知此事要躬行。看十次文檔,不如做一個(gè)測(cè)試。做十個(gè)測(cè)試,不如分析一次源代碼。分析十遍不如你自己寫一個(gè)。
我們不妨新建一個(gè)vue項(xiàng)目來(lái)做個(gè)測(cè)試:
新建項(xiàng)目并運(yùn)行:
vue init webpack vue-style-test
npm install
npm run start
如果你人品沒(méi)問(wèn)題,會(huì)看到彈出的瀏覽器中,head標(biāo)簽內(nèi)有這樣的style

在項(xiàng)目src目錄中找找看:
第一個(gè)style在\src\App.vue文件的style中
第二個(gè)style在\src\components\HelloWorld.vue文件中
App.vue包含HelloWorld.vue,因此頁(yè)面先加載App.vue的style,再加載HelloWorld.vue的style??雌饋?lái)很簡(jiǎn)單,先加載的會(huì)先出現(xiàn),根據(jù)頁(yè)面樣式的規(guī)則,如果有相同的類定義,后者會(huì)覆蓋前者。
但是我想就開(kāi)頭出現(xiàn)的問(wèn)題了解得更深入。
為避免多個(gè)問(wèn)題糾纏在一起,我逐個(gè)去分析:
一、加載順序的問(wèn)題
為方便看到測(cè)試的結(jié)果,我改造一下項(xiàng)目:


我只須關(guān)注main.js,App.vue,ComA.vue這三個(gè)文件的樣式加載,因此刪除了其它無(wú)關(guān)的內(nèi)容,再來(lái)看瀏覽器:

結(jié)果有點(diǎn)奇怪,先加載app.css,再加載com_a.css,再加載main.css,為什么呢?我們來(lái)看看main.js是怎么定義的:

原來(lái)main.js里先引入了App.vue文件,所以它先執(zhí)行App.vue里的script,從而引入了app.css
隨后引入router,router里引入了ComA.vue,繼而加載com_a.css
加載完App.vue后才按順序加載main.css,于是有了上面的結(jié)果。
而所有組件的style都是script里的代碼執(zhí)行完后再加載。
所以我大致可以得出結(jié)論:
樣式從main.js入口開(kāi)始順序加載
遇到組件則加載組件中script所引入的樣式
先處理完所有scipt中的引入,再處理style
style按包含關(guān)系從外到內(nèi)加載
但是問(wèn)題還沒(méi)完,我又得出另外的問(wèn)題:
這些頁(yè)面都是固定的,如果是異步加載頁(yè)面,當(dāng)路由切換到另一個(gè)頁(yè)面時(shí),此頁(yè)面原來(lái)的樣式是否會(huì)刪除,當(dāng)前頁(yè)面的樣式會(huì)添加到哪里?
為此我又再路由中添加了ComB.vue,并且使用異步加載該組件。

再查看瀏覽器:

可以看到,瀏覽器先加載1.js組件文件,然后執(zhí)行script加載com_b.css,再加載ComB.vue的style,
而當(dāng)我切換回ComA頁(yè)面時(shí),head標(biāo)簽內(nèi)的樣式不再有變化。
另外,當(dāng)我更改路由,使ComA不再在根路徑顯示,ComA就只加載com_a.css,而不會(huì)加載ComA的style,只有ComA頁(yè)面顯示出來(lái)時(shí)才加載。

如果ComA和ComB都是異步組件,則先打開(kāi)哪一頁(yè)就加載哪一頁(yè)的script和style

再定義一個(gè)ComAA,在ComA中引入,但不顯示,則ComAA只顯示com_a_a.css,但不顯示style

至此,加載順序應(yīng)再加兩條規(guī)則:
異步組件先顯示的先加載
沒(méi)有顯示的頁(yè)面或者組件不加載style
下面再關(guān)注另一個(gè)問(wèn)題
二、scoped的處理
使用scoped定義的樣式與不使用有什么區(qū)別?
如果父組件和子組件的樣式重疊,后出現(xiàn)的會(huì)覆蓋先出現(xiàn)的。如圖,最后結(jié)果背景色為黃色。

而如果加了scoped,打包時(shí)會(huì)在該類中增加一個(gè)data-v-xxxxxx的編碼作為限定屬性,通過(guò)這個(gè)屬性屏蔽父組件的相同類。但如果父組件中定義的樣式,子組件沒(méi)有覆蓋,則子組件會(huì)繼承該樣式,如圖中App.vue的.common-s1的color樣式。

如果父組件和子組件都用scoped,則子組件會(huì)有父組件的限定屬性,從而將樣式的層次結(jié)構(gòu)區(qū)分開(kāi)。

另外,一個(gè)組件內(nèi)可以定義不止一個(gè)style,并且有scoped的style和沒(méi)有scoped的style可以并存,它們會(huì)按照所定義的順序加載。

三、寫在body內(nèi)的樣式
筆者發(fā)現(xiàn),通過(guò)main.js控制的樣式都在head標(biāo)簽中顯示,由此就可以知道,如果我在body內(nèi)定義樣式,是可以把head標(biāo)簽內(nèi)的同名樣式覆蓋掉的。
但要注意的是,在body內(nèi)引入的樣式,因?yàn)橐呀?jīng)不在main.js控制范圍內(nèi),也就是不參與打包,所以必須定義在static靜態(tài)資源目錄內(nèi)。
同時(shí)要注意,在body引入的樣式不在src文件夾內(nèi),沒(méi)有熱更新的功能,所以每次更改后需要手動(dòng)刷新頁(yè)面。
例如:

總結(jié)
經(jīng)過(guò)以上的測(cè)試,可以得知style出現(xiàn)的順序跟你定義的位置,是否異步組件,初始狀態(tài)是否顯示有關(guān)。而樣式的覆蓋又可以通過(guò)添加scoped和在body內(nèi)添加樣式文件來(lái)控制。相信單頁(yè)應(yīng)用要精準(zhǔn)控制樣式絕對(duì)不是難事。