身為 Vue (X) 眼藥水 (O) 的初學者,偶爾送送 PR 也是很合理的。
Vue-loader 提供了樣式、樣版的組合功能,基本上沒什麼好詬病的地方。但是,科技始終來自於惰性(無誤,所以,有些地方還是覺得不太夠用。
Why scoped
通常我們自己撰寫元件時,有的時候不希望樣式放在全域中被使用,只希望在這個元件(或是其子元件內發生,這個時候,就會在樣式加入 scoped
來確保樣式只會在子元件被渲染。
好處是樣式維護上比較不會那麼複雜,也不會發生樣式互相蓋版的問題。
壞處是,樣式好像比較不好維護(欸
Style scoped 問題
通常我們寫一個 Vue
會用到一些子元件,例如這樣,
<template>
<div class="parent-component">
<h1>我是老爸</h1>
<child-component></child-component>
</div>
</template>
<style scoped>
// 我是老爸,我兒子的 h1 要紅色
.parent-component .child-component h1 {
color: red;
}
</style>
子元件例如這樣,
<template>
<div class="child-component">
<h1>我是兒子</h1>
</div>
</template>
<style>
// 我是兒子,我的 h1 是藍色
.child-component h1 {
color: blue;
}
</style>
然後,你會發現,老爸這樣的寫法,無法干涉兒子的 h1
的顏色,基於 vue-loader
當中 style-rewrite
的作法,老爸 最終樣式輸出的結果會是,
.parent-component .child-component h1[_v-d5346ea8] {
color: red;
}
而 兒子 的樣式輸出結果會是,
.child-component h1 {
color: blue;
}
根據 CSS 選擇器原則,老爸 的規則在 兒子 的 DOM Tree 當中並不符合規則,所以無論 老爸 怎麼改,基本上對於 兒子 的樣式是無法改變的。
Style scoped Hack
所以,我們換個方式想,只要老爸的樣式規則,符合兒子的 DOM Tree 即可,所以,理想的狀態下,我們只要這樣輸出,就能干涉 兒子 的顯示樣式,
.parent-component[_v-d5346ea8] .child-component h1 {
color: red;
}
所以,只需要把 scope id
寫在 老爸 的樣式後方,而不是統一加在所有選擇器的最後面,就能解決這樣的問題。
所以我送了一個 PR 給 vue-loader
基本上的寫法就是,
<style scoped>
// 我是老爸,我兒子的 h1 要紅色,兒子就要是紅色
.parent-component[scoped] .child-component h1 {
color: red;
}
div[scoped] .images[scoped] {
// 只要加上 scoped 就會被替換成正確的 scope id
max-width: 100%;
}
</style>
只要在任何一個選擇器後方,加上 [scoped]
這樣的屬性選擇器,就能渲染出符合我們需要的樣式規則,
.parent-component[_v-d5346ea8] .child-component h1 {
color: red;
}
div[_v-d5346ea8] .images[_v-d5346ea8] {
max-width: 100%;
}
這樣一來,只要 CSS 權重 正確,就能夠覆蓋子元件的樣式表,對於使用了第三方元件的人來說,永遠只能使用 Global CSS 來改寫樣式這樣的情況應該可以完全消失。
只要計算 DOM Tree 跟 CSS 權重,就能對第三方元件的樣式進行覆蓋,即便你的元件使用了 scoped
屬性,也依然能正常覆蓋。
小結
這需求只是自己爽所以改了一下(欸