您好,登錄后才能下訂單哦!
本篇文章給大家分享的是有關Vue中CSS與CSS模塊有什么區別,小編覺得挺實用的,因此分享給大家學習,希望大家閱讀完這篇文章后可以有所收獲,話不多說,跟著小編一起來看看吧。
現代Web開發中的CSS離完美還差得遠,這并不奇怪。現在,項目通常是相當的復雜的,而CSS樣式又是全局性的,所以到最后總是極容易地發生樣式沖突: 樣式相互覆蓋 或 隱式地級聯到我們未考慮到的元素 。
為了減輕CSS存在的主要痛點,我們在項目中普遍采用 BEM 的方法來。不過這只能解決CSS問題中的一小部分。
對我們來說是幸運的,社區已經開發出了可以幫助我們更徹底地解決問題的解決方案。你可能已經聽說過 CSS Modules 、 Styled Componetns 、 Glamorous 或 JSS 。這些只是我們今天可以添加到項目中的一些最流行的工具。如果你對這個話題感興趣,你可以查看這篇文章: @Indrek Lasn 詳細介紹了 CSS in JS的全部思想 。
使用Vue-cli構建的Vue應用程序提供了兩個很棒的內置解決方案: 作用域CSS 和 CSS Modules 。它們都有一些優點和缺點,所以讓我們仔細看看哪種解決方案更適合你。
作用域CSS
在Vue中引入了CSS作用域 scoped 這個概念, scoped 的設計思想就是讓當前組件的樣式不會影響到其他地方的樣式,編譯出來的選擇器將會帶上 data-v-hash 的方式來應用到對應的組件中,這樣一來,CSS也不需要添加額外的選擇器。也將解決CSS中選擇器作用域和選擇器權重的問題。
在Vue中,為了讓作用域樣式工作,只需要在 <style> 標簽添加 scoped 屬性:
<!-- Button.vue --> <template> <button class="btn"> <slot></slot> </button> </template> <style scoped> .btn { color: red; } </style>
通過使用PostCSS并將上面的示例轉換為以下內容,它僅將我們的樣式應用于相同的組件中的元素:
就像你看到的一樣,整個過程不需要做什么就可以達到很好的效果: 作用域樣式 (CSS中一直以來令人頭痛的問題之一)。
現在假設你需要調整 Button 組件的寬度,你可以像平常使用一樣,在調用這個組件的地方添加一個額外的 class 來設置其樣式:
<!-- App.vue --> <template> <div id="app"> <Button class="btn-lg">click</Button> </div> </template> <script> import Button from "./components/Button"; export default { name: "App", components: { Button } }; </script> <style scoped> .btn-lg { padding: 10px 30px; } </style>
轉換后就像下面這樣:
這次還是一樣,不需要做什么就可以很好的控制樣式。
不過請注意:這個特性存在一個缺陷,即如果你子組件的元素上有一個類已經在這個父組件中定義過了,那么這個父組件的樣式就也會應用到子組件上。只不過其權重沒有子組件同類名的重。比如下面這個示例:
<!-- Button.vue --> <template> <button class="btn btn-lg"> <slot></slot> </button> </template> <style scoped> .btn { color: red; } .btn-lg { padding: 10px 20px; border: 2px solid red; } </style> <!-- App.vue --> <template> <div id="app"> <Button class="btn-lg">click</Button> </div> </template> <script> import Button from "./components/Button"; export default { name: "App", components: { Button } }; </script> <style scoped> .btn-lg { padding: 30px; border: 5px solid green; } </style>
編譯出來的效果如下:
還有一些情況是我們需要對子組件的深層次結構設置樣式。雖然這種做法并不受推薦,而且應該盡量去避免。比如下面這個示例, Button 組件下有一個 <span> 標簽,而在調用 Button 組件的父組件 App 中設置 span 樣式:
<!-- Button.vue --> <template> <button class="btn"> <span> <slot></slot> </span> </button> </template> <style scoped> .btn { color: red; } </style> <!-- App.vue --> <template> <div id="app"> <Button class="btn-lg">click</Button> </div> </template> <script> import Button from "./components/Button"; export default { name: "App", components: { Button } }; </script> <style scoped> .btn span { color: green; font-weight: bold; border: 1px solid green; padding: 10px; } </style>
編譯出來的結果如下:
從上面的結果可以看出來,在父組件 App.vue 中的樣式:
.btn span { color: green; font-weight: bold; border: 1px solid green; padding: 10px; }
上面這段樣式并沒有編譯出來,運用到子組件 Button.vue 中的 span 中。
在 scoped 樣式中,這種情況可以使用 >>> 連接符或者 /deep/ 來解決:
<!-- App.vue --> <style scoped> .btn >>> span { color: green; font-weight: bold; border: 1px solid green; padding: 10px; } </style>
此時雖然依舊是在 App.vue 中 scoped 控制 Button.vue 組件中 span ,但上面不同的是,這次樣式生效。編譯出來的結果如下:
另外使用作用域樣式還存在一個問題。那就是對 v-html 中內在的標簽樣式不生效。比如下面這個示例:
<!-- Button.vue --> <template> <button class="btn"> <slot></slot> </button> </template> <style scoped> .btn { color: red; } </style> <!-- App.vue --> <template> <div id="app"> <Button class="btn-lg" v-html="vhtml"></Button> </div> </template> <script> import Button from "./components/Button"; export default { name: "App", data () { return { vhtml: 'Click <strong>7</strong>' } }, components: { Button } }; </script> <style scoped> strong { color: green; border: 1px solid green; padding: 10px; } </style>
編譯出來的結果如下:
從上圖可以看出來, v-html 中的 strong 標簽樣式并未生效。和前面在父組件的 scoped 中設置子組件內部標簽未生效一樣。當然,其解決方案也是同樣的, 使用 >>> 連接符或 /deep/ 可以讓 v-html 中的標簽樣式生效。比如上面的示例,可以將代碼修改為:
<!-- App.vue --> <style scoped> .btn /deep/ strong { color: green; border: 1px solid green; padding: 10px; } </style>
這個時候 v-html 中的 strong 樣式生效了,如下圖所示:
話又說回來,雖然 >>> 或 /deep/ 可以幫助我們穿透已封裝好的組件中的樣式,但這也失去了組件封裝的效果。再次回到以前CSS中令人頭痛的問題: CSS作用域 。
簡單的小結一下,在Vue中 scoped 屬性的渲染規則:
給DOM節點添加一個不重復的 data 屬性(比如 data-v-7ba5bd90 )來表示他的唯一性
在每個CSS選擇器末尾(編譯后生成的CSS)加一個當前組件的 data 屬性選擇器(如 [data-v-7ba5bd90] )來私有化樣式。選擇器末尾的 data 屬性和其對應的DOM中的 data 屬性相匹配
如果組件內部包含有其他組件,只會給其他組件的最外層標簽加上當前組件的 data 屬性
上面我們看到的是Vue機制內作用域CSS的使用。在Vue中,除了作用域CSS之外,還有另外一種機制,那就是 CSS Modules ,即 模塊化CSS 。
CSS Modules
CSS Modules的流行起源于React社區,它獲得了社區的迅速的采用。Vue更甚之,其強大,簡便的特性在加上Vue-cli對其開箱即用的支持,將其發展到另一個高度。
在Vue中使用CSS Modules和作用域CSS同樣的簡單。和作用域CSS類似,在 <style> 標簽中添加 module 屬性。比如像下面這樣:
<style module> .btn { color: red; } </style>
然后在 <template> 里這樣寫:
<template> <button :class="$style.btn">{{msg}}</button> </template>
這個時候編譯出來的效果如下:
正如上圖所示, :class="$style.btn"
會被 vue-template-compiler
編譯成為 .Button_btn_3ykLd 這個類名,并且樣式的選擇器也自動發生了相應的變化。
但在這里有一點需要注意,我們平時有可能在類名中會使用分隔線,比如:
<style module> .btn-lg { border: 1px solid red; padding: 10px 30px; } </style>
如果通過 $style 調用該類名時要是寫成 $style.btn-lg ,這樣寫是一個不合法的JavaScript變量名。此時在編譯的時候,會報一個錯話信息:
按鈕的樣式也不會生效。如果要生效,我們需要通過下面這樣的方式來寫:
<template> <button :class="$style['btn-lg']">{{msg}}</button> </template>
編譯出來的結果如下:
除了$style.btn-lg
這種方式會報錯之外,寫在駝峰($style.btnLg
)的也會報錯。
上面說的 module 屬性會經由Vue-loader編譯后,在我們的 component 產生一個叫 $style 的隱藏的 computed 屬性。也就是說,我們甚至可以在Vue生命周期的 created 鉤子中取得由CSS Modules生成的 class 類名:
<script> export default { created () { console.log(this.$style['btn-lg']) } } </script>
在瀏覽器的 console 中可以看到 modules 編譯出來對應的類名:
利用這樣的特性,在 <template> 也可以這樣寫:
<!-- App.vue --> <template> <div id="app"> <Button msg="Default Button" /> <Button :class="{[$style['btn-lg']]: isLg}" msg="Larger Button" /> <Button :class="{[$style['btn-sm']]: isSm}" msg="Smaller Button" /> </div> </template> <script> import Button from './components/Button' export default { name: 'app', components: { Button }, data () { return { isLg: true, isSm: false } } } </script> <style module> .btn-lg { padding: 15px 30px; } .btn-sm { padding: 5px; } </style>
這個時候編譯出來的結果如下:
如上圖所示,當 data 中的 isLg 屬性值為 true 時, Larger Button 按鈕的 padding 變了,按鈕也同時變大了。除此之外,我們還可以通過 props 將 class 傳到子組件中。比如像下面這樣使用:
<!-- Button.vue --> <template> <button :class="[$style.btn, primaryClass]">{{msg}}</button> </template> <script> export default { name: 'Button', props: { msg: String, primaryClass: '' } } </script> <style module> .btn { border: 1px solid #ccc; border-radius: 3px; padding: 5px 15px; background: #fefefe; margin: 5px; } </style> <!-- App.vue --> <template> <div id="app"> <Button msg="Default Button" /> <Button :class="{[$style['btn-lg']]: isLg}" msg="Larger Button" /> <Button :class="{[$style['btn-sm']]: isSm}" msg="Smaller Button" /> <Button msg="Primary Button" :primaryClass="$style['btn-primary']" /> </div> </template> <script> import Button from './components/Button' export default { name: 'app', components: { Button }, data () { return { isLg: true, isSm: false } } } </script> <style module> .btn-lg { padding: 15px 30px; } .btn-sm { padding: 5px; } .btn-primary { background: rgb(54, 152, 244); border-color: rgb(32, 108, 221); color: #fff; } </style>
編譯出來的效果如下圖所示:
如果我們想要在JavaScript里面將獨立的CSS文件作為CSS模塊來加載的話,需要在 .css 文件名前添加 .module 前綴,比如:
<script> import barStyle from './src/style/bar.module.css' </script>
如果你是在項目中引入的是處理器文件也是如此,比如 .scss 文件:
<script> import fooSassStyle from './src/scss/foo.module.scss' </script>
如果你覺得這樣比較麻煩,可以在 vue.config.js 文件中 css.modules 設為 true :
// vue.config.js module.exports = { css: { modules: true } }
注意,上面的示例創建的項目是使用Vue-cli 3創建的。如果是使用Webpack的話,需要根據Webpack的相關機制進行配制。
從上面的示例中我們可以看出。使用 module 和 scoped 不一樣的地方就是在于所有創建的類可以通過 $style 對象獲取。因此類要應用到元素上,就需要通過 :class 來綁定 $style 這個對象。它的好處是,當我們在HTML中查看這個元素時,我們可以立刻知道它所屬的是哪個組件。如果你夠細心的話,可以看到編譯出來的類名,都會以組件名為前綴,比如:
除了這個好處之外,還有另一個好處,即: 一切都變成顯式的了,我們擁有了徹底的控制權 。
總結
不管是CSS Modules還是作用域CSS,這兩種方案都非常簡單,易用。在某種程度上解決的是同樣的痛點(CSS的痛)。那么你應該選擇哪種呢?
scoped 樣式的使用不需要額外的知識,給人舒適的感覺。它所存在的局限,也正它的使用簡單的原因。它可以用于支持小型到中型的Web應用程序。在更大的Web應用程序或更復雜的場景中,對于CSS的運用,我們更希望它是顯式的,更具有控制權。比如說,你的樣式可以在多組件中重用時,那么 scoped 的局限性就更為明顯了。反之,CSS Modules的出現,正好解決了這些問題,不過也要付出一定的代價,那就是需要通過 $style 來引用。雖然在 <template> 中大量使用 $style ,讓人看起來很蛋疼,但它會讓你的樣式更加安全和靈活,更易于控制。CSS Modules還有一個好處就是可以使用JavaScript獲取到我們定義的一些變量,這樣我們就不需要手動保持其在多個文件中同步。
以上就是Vue中CSS與CSS模塊有什么區別,小編相信有部分知識點可能是我們日常工作會見到或用到的。希望你能通過這篇文章學到更多知識。更多詳情敬請關注億速云行業資訊頻道。
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。