国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > JavaScript > 正文

Vue中的作用域CSS和CSS模塊的區(qū)別

2019-11-19 12:45:40
字體:
供稿:網(wǎng)友

現(xiàn)代Web開發(fā)中的CSS離完美還差得遠(yuǎn),這并不奇怪。現(xiàn)在,項(xiàng)目通常是相當(dāng)?shù)膹?fù)雜的,而CSS樣式又是全局性的,所以到最后總是極容易地發(fā)生樣式?jīng)_突: 樣式相互覆蓋 隱式地級(jí)聯(lián)到我們未考慮到的元素

為了減輕CSS存在的主要痛點(diǎn),我們?cè)陧?xiàng)目中普遍采用 BEM 的方法來。不過這只能解決CSS問題中的一小部分。

對(duì)我們來說是幸運(yùn)的,社區(qū)已經(jīng)開發(fā)出了可以幫助我們更徹底地解決問題的解決方案。你可能已經(jīng)聽說過 CSS Modules Styled Componetns Glamorous JSS 。這些只是我們今天可以添加到項(xiàng)目中的一些最流行的工具。如果你對(duì)這個(gè)話題感興趣,你可以查看這篇文章: @Indrek Lasn 詳細(xì)介紹了 CSS in JS的全部思想

使用Vue-cli構(gòu)建的Vue應(yīng)用程序提供了兩個(gè)很棒的內(nèi)置解決方案: 作用域CSSCSS Modules 。它們都有一些優(yōu)點(diǎn)和缺點(diǎn),所以讓我們仔細(xì)看看哪種解決方案更適合你。

作用域CSS

在Vue中引入了CSS作用域 scoped 這個(gè)概念, scoped 的設(shè)計(jì)思想就是讓當(dāng)前組件的樣式不會(huì)影響到其他地方的樣式,編譯出來的選擇器將會(huì)帶上 data-v-hash 的方式來應(yīng)用到對(duì)應(yīng)的組件中,這樣一來,CSS也不需要添加額外的選擇器。也將解決CSS中選擇器作用域和選擇器權(quán)重的問題。

在Vue中,為了讓作用域樣式工作,只需要在 <style> 標(biāo)簽添加 scoped 屬性:

<!-- Button.vue --><template> <button class="btn"> <slot></slot> </button></template><style scoped> .btn { color: red; }</style>

通過使用PostCSS并將上面的示例轉(zhuǎn)換為以下內(nèi)容,它僅將我們的樣式應(yīng)用于相同的組件中的元素:

就像你看到的一樣,整個(gè)過程不需要做什么就可以達(dá)到很好的效果: 作用域樣式 (CSS中一直以來令人頭痛的問題之一)。

現(xiàn)在假設(shè)你需要調(diào)整 Button 組件的寬度,你可以像平常使用一樣,在調(diào)用這個(gè)組件的地方添加一個(gè)額外的 class 來設(shè)置其樣式:

<!-- 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>

轉(zhuǎn)換后就像下面這樣:

這次還是一樣,不需要做什么就可以很好的控制樣式。

不過請(qǐng)注意:這個(gè)特性存在一個(gè)缺陷,即如果你子組件的元素上有一個(gè)類已經(jīng)在這個(gè)父組件中定義過了,那么這個(gè)父組件的樣式就也會(huì)應(yīng)用到子組件上。只不過其權(quán)重沒有子組件同類名的重。比如下面這個(gè)示例:

<!-- 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>

編譯出來的效果如下:

還有一些情況是我們需要對(duì)子組件的深層次結(jié)構(gòu)設(shè)置樣式。雖然這種做法并不受推薦,而且應(yīng)該盡量去避免。比如下面這個(gè)示例, Button 組件下有一個(gè) <span> 標(biāo)簽,而在調(diào)用 Button 組件的父組件 App 中設(shè)置 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>

編譯出來的結(jié)果如下:

從上面的結(jié)果可以看出來,在父組件 App.vue 中的樣式:

.btn span { color: green; font-weight: bold; border: 1px solid green; padding: 10px;}

上面這段樣式并沒有編譯出來,運(yùn)用到子組件 Button.vue 中的 span 中。

在 scoped 樣式中,這種情況可以使用 >>> 連接符或者 /deep/ 來解決:

<!-- App.vue --><style scoped> .btn >>> span { color: green; font-weight: bold; border: 1px solid green; padding: 10px; }</style>

此時(shí)雖然依舊是在 App.vue 中 scoped 控制 Button.vue 組件中 span ,但上面不同的是,這次樣式生效。編譯出來的結(jié)果如下:

另外使用作用域樣式還存在一個(gè)問題。那就是對(duì) v-html 中內(nèi)在的標(biāo)簽樣式不生效。比如下面這個(gè)示例:

<!-- 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>

編譯出來的結(jié)果如下:

從上圖可以看出來, v-html 中的 strong 標(biāo)簽樣式并未生效。和前面在父組件的 scoped 中設(shè)置子組件內(nèi)部標(biāo)簽未生效一樣。當(dāng)然,其解決方案也是同樣的, 使用 >>> 連接符或 /deep/ 可以讓 v-html 中的標(biāo)簽樣式生效。比如上面的示例,可以將代碼修改為:

<!-- App.vue --><style scoped> .btn /deep/ strong { color: green; border: 1px solid green; padding: 10px; }</style>

這個(gè)時(shí)候 v-html 中的 strong 樣式生效了,如下圖所示:

話又說回來,雖然 >>> 或 /deep/ 可以幫助我們穿透已封裝好的組件中的樣式,但這也失去了組件封裝的效果。再次回到以前CSS中令人頭痛的問題: CSS作用域 。

簡單的小結(jié)一下,在Vue中 scoped 屬性的渲染規(guī)則:

給DOM節(jié)點(diǎn)添加一個(gè)不重復(fù)的 data 屬性(比如 data-v-7ba5bd90 )來表示他的唯一性
在每個(gè)CSS選擇器末尾(編譯后生成的CSS)加一個(gè)當(dāng)前組件的 data 屬性選擇器(如 [data-v-7ba5bd90] )來私有化樣式。選擇器末尾的 data 屬性和其對(duì)應(yīng)的DOM中的 data 屬性相匹配
如果組件內(nèi)部包含有其他組件,只會(huì)給其他組件的最外層標(biāo)簽加上當(dāng)前組件的 data 屬性
上面我們看到的是Vue機(jī)制內(nèi)作用域CSS的使用。在Vue中,除了作用域CSS之外,還有另外一種機(jī)制,那就是 CSS Modules ,即 模塊化CSS 。

CSS Modules

CSS Modules的流行起源于React社區(qū),它獲得了社區(qū)的迅速的采用。Vue更甚之,其強(qiáng)大,簡便的特性在加上Vue-cli對(duì)其開箱即用的支持,將其發(fā)展到另一個(gè)高度。

在Vue中使用CSS Modules和作用域CSS同樣的簡單。和作用域CSS類似,在 <style> 標(biāo)簽中添加 module 屬性。比如像下面這樣:

<style module> .btn { color: red; }</style>

然后在 <template> 里這樣寫:

<template> <button :class="$style.btn">{{msg}}</button></template>

這個(gè)時(shí)候編譯出來的效果如下:

正如上圖所示, :class="$style.btn" 會(huì)被 vue-template-compiler 編譯成為 .Button_btn_3ykLd 這個(gè)類名,并且樣式的選擇器也自動(dòng)發(fā)生了相應(yīng)的變化。

但在這里有一點(diǎn)需要注意,我們平時(shí)有可能在類名中會(huì)使用分隔線,比如:

<style module> .btn-lg {  border: 1px solid red;  padding: 10px 30px; }</style>

如果通過 $style 調(diào)用該類名時(shí)要是寫成 $style.btn-lg ,這樣寫是一個(gè)不合法的JavaScript變量名。此時(shí)在編譯的時(shí)候,會(huì)報(bào)一個(gè)錯(cuò)話信息:

按鈕的樣式也不會(huì)生效。如果要生效,我們需要通過下面這樣的方式來寫:

<template> <button :class="$style['btn-lg']">{{msg}}</button></template>

編譯出來的結(jié)果如下:

除了$style.btn-lg這種方式會(huì)報(bào)錯(cuò)之外,寫在駝峰($style.btnLg)的也會(huì)報(bào)錯(cuò)。

上面說的 module 屬性會(huì)經(jīng)由Vue-loader編譯后,在我們的 component 產(chǎn)生一個(gè)叫 $style 的隱藏的 computed 屬性。也就是說,我們甚至可以在Vue生命周期的 created 鉤子中取得由CSS Modules生成的 class 類名:

<script>export default { created () {  console.log(this.$style['btn-lg']) }}</script>

在瀏覽器的 console 中可以看到 modules 編譯出來對(duì)應(yīng)的類名:

利用這樣的特性,在 <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>

這個(gè)時(shí)候編譯出來的結(jié)果如下:

如上圖所示,當(dāng) data 中的 isLg 屬性值為 true 時(shí), Larger Button 按鈕的 padding 變了,按鈕也同時(shí)變大了。除此之外,我們還可以通過 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里面將獨(dú)立的CSS文件作為CSS模塊來加載的話,需要在 .css 文件名前添加 .module 前綴,比如:

<script> import barStyle from './src/style/bar.module.css'</script>

如果你是在項(xiàng)目中引入的是處理器文件也是如此,比如 .scss 文件:

<script> import fooSassStyle from './src/scss/foo.module.scss'</script>

如果你覺得這樣比較麻煩,可以在 vue.config.js 文件中 css.modules 設(shè)為 true :

// vue.config.jsmodule.exports = { css: {  modules: true }}

注意,上面的示例創(chuàng)建的項(xiàng)目是使用Vue-cli 3創(chuàng)建的。如果是使用Webpack的話,需要根據(jù)Webpack的相關(guān)機(jī)制進(jìn)行配制。

從上面的示例中我們可以看出。使用 module 和 scoped 不一樣的地方就是在于所有創(chuàng)建的類可以通過 $style 對(duì)象獲取。因此類要應(yīng)用到元素上,就需要通過 :class 來綁定 $style 這個(gè)對(duì)象。它的好處是,當(dāng)我們?cè)贖TML中查看這個(gè)元素時(shí),我們可以立刻知道它所屬的是哪個(gè)組件。如果你夠細(xì)心的話,可以看到編譯出來的類名,都會(huì)以組件名為前綴,比如:

除了這個(gè)好處之外,還有另一個(gè)好處,即: 一切都變成顯式的了,我們擁有了徹底的控制權(quán) 。

總結(jié)

不管是CSS Modules還是作用域CSS,這兩種方案都非常簡單,易用。在某種程度上解決的是同樣的痛點(diǎn)(CSS的痛)。那么你應(yīng)該選擇哪種呢?

scoped 樣式的使用不需要額外的知識(shí),給人舒適的感覺。它所存在的局限,也正它的使用簡單的原因。它可以用于支持小型到中型的Web應(yīng)用程序。在更大的Web應(yīng)用程序或更復(fù)雜的場(chǎng)景中,對(duì)于CSS的運(yùn)用,我們更希望它是顯式的,更具有控制權(quán)。比如說,你的樣式可以在多組件中重用時(shí),那么 scoped 的局限性就更為明顯了。反之,CSS Modules的出現(xiàn),正好解決了這些問題,不過也要付出一定的代價(jià),那就是需要通過 $style 來引用。雖然在 <template> 中大量使用 $style ,讓人看起來很蛋疼,但它會(huì)讓你的樣式更加安全和靈活,更易于控制。CSS Modules還有一個(gè)好處就是可以使用JavaScript獲取到我們定義的一些變量,這樣我們就不需要手動(dòng)保持其在多個(gè)文件中同步。

最后還是那句話, 任何解決CSS的方案,沒有最好的,只有最合適的! 我們應(yīng)該根據(jù)自己的項(xiàng)目、場(chǎng)景和團(tuán)隊(duì)進(jìn)行選擇。當(dāng)然,不管選擇哪種方案,都是為了幫助我們更好的控制樣式,解決原生CSS中存在的痛點(diǎn)。最后希望這篇文章對(duì)大家有所幫助。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 阿图什市| 于都县| 达日县| 荣昌县| 额济纳旗| 如皋市| 陈巴尔虎旗| 河南省| 丰原市| 静乐县| 阜宁县| 曲阳县| 乐安县| 望谟县| 达尔| 亳州市| 淄博市| 手机| 磐石市| 泰安市| 平和县| 青浦区| 开原市| 祁连县| 鄄城县| 温泉县| 乐业县| 铁力市| 余庆县| 平昌县| 济阳县| 泾阳县| 二连浩特市| 山丹县| 娱乐| 宜君县| 荔浦县| 烟台市| 疏附县| 华阴市| 大丰市|