Vue 元件間的樣式汙染

小蟲巨蟹發表於2019-03-03

一、汙染是如何產生的?

得益於 Vue-loader,在 Vue 中可以使用類似於 Web Component 的元件化寫法,<template></template><style></style><script></script>,在大多數情況下,我們希望元件間定義的樣式是相互隔離的,在 Weex 當中的確如此,元件天生隔離,可是在 Vue 當中,執行的載體還是瀏覽器,所有的樣式類還是會通過 style 標籤插入頭部,影響全域性,交叉汙染

二、增加 Scoped 標識

依然是 Vue-loader,通過為元件中的 style 標籤增加一個 scoped 標識,Vue-loader 在編譯的過程中會為元件每一個元素節點增加 scopeId 作為屬性,同時為所有的樣式類加上屬性選擇器 scopeId,從而達到隔離的效果,大概是下面的樣子:

每個元件有唯一的 scopeId,按理說,這樣應該能夠做到樣式隔離了,實際上,這種方式其實表現已經足夠好了,除了以下這種情況~~

三、ScopeId 的繼承

我們把上面的例子再完善下:

// 父元件
<template>
    <div>
        <div class="bg"></div>
        <Sub></Sub>
    </div>
</template>
<script>
    import Sub from `./sub`;
    export default {
        components: { Sub }
    };
</script>
<style scoped>
.bg {
    background-color: #000;
    width: 100px;
    height: 100px;
}
</style>

// 子元件
<template>
    <div class="bg">
    </div>
</template>
<script>
    export default {
    };
</script>
<style scoped>
.bg {
    width: 300px;
    height: 300px;
    margin-top: 5px;
}
</style>複製程式碼

由於我們使用了 scoped 標識進行樣式隔離,子元件的 div 不應該有任何背景顏色,可是現實總在狠狠的打臉~~

實力打臉
實力打臉

不知道你的媚眼看到問題的所在了沒:
子元素的根元素會繼承父元素的 ScopeId!
子元素的根元素會繼承父元素的 ScopeId!
子元素的根元素會繼承父元素的 ScopeId!(說了三遍的話,肯定很重要)
由於子元素的根元素除了擁有自己的 ScopeId 屬性,還繼承了父元素的 ScopeId 屬性,所以父元素的樣式類 bg 對其依然有效

四、怎麼破?

破解的方式也很簡單,為每一個元件的根元素提供一個另類一點的樣式名(如果有的話),例如就不要每個元件都命名為:wrap,根據業務名為:b1-wrap、b2-wrap 等

元件中的非根元素,類名不管怎麼命名,怎麼重名,都是不會發生汙染的,這個自己領悟~~

五、【更新】這不是bug

慚愧,文件查得不夠詳實,實際上官網已經有:Be careful with descendant selectors in recursive components! For a CSS rule with the selector .a .b, if the element that matches .a contains a recursive child component, then all .b in that child component will be matched by the rule.

感謝 @WayneZheng、@花褲衩的提醒
也許有同學跟我一樣不夠細心,沒有注意,此文的意義用於提醒了

此文還在公眾號菲麥前端中釋出:

相關文章