在Angular1.X中使用CSS Modules

在Angular1.5中,增長了一個Component方法,而且定義了組件的若干生命週期hook,在代碼規範中也是推崇組件化開發,可是很遺憾的是,CSS模塊化組件化的問題並無獲得解決,大部分項目的打包方式仍是將全部CSS打包成一個完整的CSS文件,而後插入到html中,這樣作的壞處顯而易見,若是團隊沒有良好的CSS代碼規範,很容易引發CSS的衝突,本文使用CSS Modules來解決Angular1.X中存在的CSS 衝突問題。css

爲了便於讀者查看並動手操做,我將全部的代碼打包成了一個庫,首先在本地clone這個庫html

git clone https://github.com/myzhibie/ng1-css-modules-demo.git

接着安裝所須要的依賴node

npm install

上述過程若是成功,就能夠運行了webpack

gulp serve

在瀏覽器中查看結果http://localhost:3000git

首先查看整個項目目錄github

client文件夾表示客戶端代碼,common目錄下是一些公用組件,components目錄下是非公用的業務組件,generator目錄下是生成組件的模板文件,webpack.config.js是項目基礎的webpack配置文件,根據開發環境會執行添加到dev或者production配置當中去。web

CSS Modules是什麼,怎麼用npm

關於CSS Modules是什麼,這裏很少說,只闡述如何在項目中使用,好處也是顯而易見的,就是完全隔離了組件的CSS和全局的CSS,防止衝突。若是須要對CSS Modules有更深的概念上的認識,請查看官網gulp

配置webpack中的css-loader,啓用css-modules.bootstrap

在項目根目錄下webpack.config.js中,查看以下代碼

 1 module.exports = {
 2   devtool: 'sourcemap',
 3   entry: {},
 4   module: {
 5     loaders: [
 6        { test: /\.js$/, exclude: [/app\/lib/, /node_modules/], loader: 'ng-annotate!babel' },
 7        { test: /\.html$/, loader: 'raw' },
 8        { test: /\.scss$/, loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]!sass' },
 9        { test: /\.css$/, loader: 'style!css-loader?modules&importLoaders=1&localIdentName=[name]__[local]___[hash:base64:5]' }
10     ]
11   },
12   // resolve:{
13   //   modulesDirectories:[bootstrapPath]
14   // },
15   plugins: [
16     // Injects bundles in your index.html instead of wiring all manually.
17     // It also adds hash to all injected assets so we don't have problems
18     // with cache purging during deployment.
19     new HtmlWebpackPlugin({
20       template: 'client/index.html',
21       inject: 'body',
22       hash: true
23     }),
24 
25     // Automatically move all modules defined outside of application directory to vendor bundle.
26     // If you are using more complicated project structure, consider to specify common chunks manually.
27     new webpack.optimize.CommonsChunkPlugin({
28       name: 'vendor',
29       minChunks: function (module, count) {
30         return module.resource && module.resource.indexOf(path.resolve(__dirname, 'client')) === -1;
31       }
32     })
33   ]
34 };

注意查看第8和第9行,對於css和scss文件的處理,使用css-loader並啓用了css-modules,importLoaders=1代碼該文件被CSS-loader處理以後還會被Style-loader再次處理一次,localIdentName指定了scope css名字生成的規則,這裏是[name]表明組件所在目錄名,[local]表示原本定義的css名稱,[hash:base64:5]表明由css-loader生成的哈希值。這樣定義以後,每次遇到css或者sass文件,css-loader都會按照咱們指定的方式將組件的css名稱更名並引用到html中。

添加Scoped css(Sass)

所謂的scoped css,就是指局部css,在咱們的項目中能夠理解爲單個組件本身的css,不與全局css相混淆。在client/app/components目錄下,選定attrs組件,添加attrs.scss,內容以下:

1 //local scope css 定義 
2 .attrs{
3   color:red;
4 } 
5 .header{
6   color:green;
7 }

接着在attrs.controller.js中引入這個sass文件,以下:

1 import styles from "./attrs.scss";
2 class AttrsController {
3   constructor() {
4     this.name = 'Directives利用attrs通訊';
5     this.styles=styles;
6   }
7 }
8 
9 export default AttrsController;

第一行import引入進來咱們剛纔寫的css,而後將它賦值給該controller的一個屬性叫styles,接着在html中引用咱們定義的styles,以下:

1 <navbar></navbar>
2 <div>
3   <h2 class="{{$ctrl.styles.header}}">{{ $ctrl.name }}</h2>
4   <directive-b directive-a></directive-b>
5 </div>

注意第3行,咱們直接使用controller的styles對象加上咱們定義的類名(header)來使用綁定類名,結果以下:

能夠看到css-loader將咱們定義的類名改爲了咱們指定的模式,並在html中引用了該類,能夠查看咱們引入的styles對象以下:

基本原理就是css-loader按照咱們指定的模式修改每一個組件的類名,並在咱們import的時候將其打包到js中的一個對象,這個對象的key值是咱們定義的原始類名,鍵值是修改後的類名。

經過查看原始的HTML代碼,咱們能夠發現,css-loader最後將更名後的每一個組件的css,所有以<style>的方式插入到html的header中,便於頁面的引用,以下

以上就是CSS Modules的基本原理

驗證是否存在CSS衝突

前面咱們在attrs組件中添加了一個.header類,值爲color:green,接着咱們以一樣方式在about組件中添加一個.header類,值爲 color: red;結果以下:

attrs組件

about組件

能夠看到儘管咱們定義的原始類名都是.header,可是被css-loader修改後的類名是不同的,因此在每一個組件下定義的類即便同名也是不會發生衝突的,這就是scoped css的原理。

global css

咱們實現了scoped css,又會問若是我想給全局添加一css怎麼辦呢?其實很簡單,global css的實現引入方式和scoped css並無區別,只是在類的定義上有區別,以下,在app目錄下定義一個app.scss文件,內容以下:

 1 body {
 2     font-size: 20px;
 3 }
 4 
 5 :global {
 6     .index {
 7       display:inline-block;
 8       margin-top: 20px;
 9     }
10 }

注意第五行,使用:global定義的就是一個全局類,在項目任何地方均可以使用,好比咱們在about組件中定義了一個directive,就能夠在這個directive中使用index類,以下:

 1 function aDirective(homeService,$rootScope) {
 2     "ngInject";
 3     return {
 4         restrict: 'E',
 5         template: `name:<input type='text' ng-model='showValue' class='about'>
 6   <br><button ng-click="addName()" class="index">addName</button>`,
 7         link: (scope, element, attrs) => {
 8             scope.addName = () => {
 9                 if(scope.showValue){
10                     $rootScope.$broadcast('addName',scope.showValue);
11                 }        
12             }
13         }
14     };
15 }
16 
17 export default aDirective;

注意第六行,就是直接指定類名爲index,結果以下:

 

在css modules中使用sass嵌套定義類名

對於大型項目來說,不多有直接使用css的,一般都是使用一些CSS預處理器,好比說Sass,在Sass中,一般類之間能夠嵌套定義,配合CSS Modules,也是可以靈活使用,好比在about組件中,定義about.scss嵌套類名以下:

 1 //嵌套定義,只須要寫類名便可,不須要嵌套調用
 2 .sec {
 3     .about {
 4         display: inline-block;
 5         margin-bottom: 10px;
 6     }
 7 }
 8 
 9 .header {
10     color: red;
11 }

咱們定義了一個.sec類,而且在該類下定義了一個嵌套的about類,在html中使用以下:

1 <navbar></navbar>
2 <h2 class="{{$ctrl.styles.header}}">{{ $ctrl.name }}</h2>
3 <section class="{{$ctrl.styles.sec}}">
4   <event-adirective class="{{$ctrl.styles.about}}"></event-adirective>
5   <event-bdirective></event-bdirective>
6 </section>

注意第4行,咱們並無在使用about類的時候,在前面加上sec,這說明CSS Modules 在處理styles對象的時候認爲全部類的類名都是平級關係,儘管它們在定義的時候是嵌套定義的,可是要讓這些嵌套類起做用,必須按照嵌套的結構來添加類,結果以下:

 

使用CSS Modules的多個css文件合併功能

因爲CSS Modules的主要思想是使用js來處理CSS,那麼對於不一樣的CSS咱們能夠將其合併成一個styles對象,例如在ctrls組件中,定義兩個sass文件,

a.scss

1 //多個scss文件合併
2 .aCtrls{
3   color: purple;
4 }

ctrls.scss

.ctrls {
    button {
        display: inline-block;
        margin: 20px;
    }
}

在ctrls.controller.js中進行合併

 1 let styles={};
 2 import aStyle from './a.scss';
 3 import bStyle from './ctrls.scss';
 4 Object.assign(styles,aStyle,bStyle);
 5 class CtrlsController {
 6   constructor() {
 7     this.name = 'directive經過Controllers通訊';
 8     this.styles=styles;
 9   }
10 }
11 
12 export default CtrlsController;

在1到4行,經過引入不一樣的styles文件,並將它們合併爲一個對象,而後在ctrls.html中使用

 1 <navbar></navbar>
 2 <div>
 3   <h1 class='{{$ctrl.styles.aCtrls}}'>{{ $ctrl.name }}</h1>
 4   <a-ctrl-directive>
 5   <div>
 6     <div>
 7       <b-ctrl-directive class='{{$ctrl.styles.ctrls}}'></b-ctrl-directive>
 8     </div>
 9     </div>
10   </a-ctrl-directive>
11 
12 </div>

和使用一個sass文件定義的在使用上徹底沒有任何區別。

以上就是CSS Modules在Angular1.X項目中的使用,咱們還能夠結合PostCSS讓它在功能上更增強大,進一步使用能夠參考這裏

相關文章
相關標籤/搜索