webpack與SPA實踐之管理CSS等資源

前言

在學習使用webpack時,咱們須要明白不管它怎麼設計,它的工做原理、流程是什麼,最根本的它處理的仍是HTML文檔中的HTML標籤、JavaScript、CSS、圖片等資源,並且最終的處理結果依然必須是一個HTML文檔,包括DOM、JavaScript、CSS,而CSS在文檔中的存在方式,有三種:行內樣式,內聯樣式,外鏈樣式,行內樣式使用方式早已不推薦,因此webpack處理CSS方式也就兩種:css

  • 內聯樣式: 以<style>標籤方式在HTML文檔中嵌入樣式;
  • 外鏈樣式: 打包生成CSS文件,經過<link>標籤引入樣式;

webpack與CSS

咱們知道,webpack本質是隻能處理JavaScript的,而對於其餘資源,須要使用加載器和插件將其處理成JavaScript模塊,而後進行模塊依賴管理。webpack提供style-loadercss-loader兩個加載器支持咱們模塊化CSS,所以能夠在其餘模塊內直接引入。html

  • 安裝
npm install --save-dev style-loader css-loader
  • 配置

在webpack配置文件的模塊加載器選項中添加以下配置:前端

module: {
        loaders: [
            { test: /\.css$/, loader: "style-loader!css-loader" }
        ]
    }

固然爲了方便使用引用路徑,還能夠配置路徑片斷別名:node

alias: {
        styles: path.resolve(__dirname, 'src/styles/')
    }

此時,import 'styles/index.css';等同於使用相對路徑,如import '../src/styles/indx.css';webpack

  • 使用

配置好之後,假如咱們在styles目錄下建立了一個index.css文件,如今能夠在JavaScript文件中直接引入該CSS: import 'styles/index.css';require('styles/index.css');git

css內容以下:github

html, body {
   	 	width: 100%;
    	height: 100%;
	}
	.container {
   	 	color: red;
	}

頁面展現如圖:web

style

內聯樣式

前面提到了webpack處理CSS的方式有兩種,第一種是之內聯方式在頁面<head>標籤內動態插入<style>內聯樣式,這種方式也是webpack的默認處理方式,只須要簡單配置以下加載器:npm

{
        test: /\.css$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader'
		// or 
		// loaders: ['style-loader', 'css-loader']
    }

webpack加載器解析順序

如上面代碼所示,不管是字符串語法style-loader!css-loader,亦或是數組語法['style-loader', 'css-loader'],webpack解析規則都是從右至左,依次解析並執行加載器處理文件,前一加載器處理的輸出就是下一加載器處理的輸入,直到最後加載器處理完成;此處即webpack先調用css-loader加載器處理css文件,而後將處理結果傳遞給style-loader加載器,style-loader接受該輸入繼續處理。json

css-loader

咱們已經反覆強調,webpack只能處理JavaScript,因此對於其餘諸如css或圖片等資源須要使用加載器將其轉換輸出爲JavaScript模塊,webpack才能繼續處理。

css-loader加載器的做用就是支持咱們像使用JavaScript模塊同樣在JavaScript文件中引用CSS文件,如require ('./index.css'),因此你能夠認爲其做用是將CSS文件轉換成JavaScript模塊,因而咱們能夠直接經過引入JavaScript模塊的方式直接引用。

參數

css-loader有兩個經常使用參數:

  • modules: {boolean}指定是否使用CSS模塊(如:local和:global設置局部或全局樣式規則),默認是false,開啓設置如css-loader?modules;
  • importLoaders: {number}指定css-loader加載器以前使用的加載器數量,默認是0,設置如css-loader?importLoaders=1

style-loader

不管webpack怎麼處理CSS文件,最終都須要將其輸出到頁面,才能實際使用該CSS規則,style-loader加載器就是將CSS之內聯方式插入到頁面文檔,即:針對每個輸入(經過require引入,使用css-loader轉換爲JavaScript模塊,傳遞給style-loader做爲輸入),style-loader在頁面<head>標籤內插入一個<style>標籤,該標籤內樣式規則即此輸入內容,以下實例:

內聯樣式

外鏈樣式

固然,咱們並不老是但願全部樣式都之內聯方式存在頁面中,不少時候咱們也但願經過外鏈方式使用樣式表,特別是樣式規則較多的時候,webpack開發者們固然考慮了這樣的需求。

webpack提供的style-loader加載器默認是之內聯方式將樣式插入文檔,咱們須要使用webpack extract-text-webpack-plugin插件以實現輸出單獨CSS文件。

Extract Text Plugin

  • 安裝

首先安裝該插件:

npm install --save-dev extract-text-webpack-plugin
  • 配置

而後添加以下配置:

var ExtractTextPlugin = require('extract-text-webpack-plugin');
	...
	module: {
		loaders: [
			{
                test: /\.css$/,
                exclude: /node_modules/,
                // 老版本 loader: ExtractTextPlugin.extract('style-loader', 'css-loader')
                loader: ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use: 'css-loader'
                })
            }
		]
	},
	plugins: [
		// 生成獨立css文件
        new ExtractTextPlugin({
            filename: 'css/[name].css'
        })
	]

運行webpack命令,咱們會看到在dist/css/文件夾下生成相應的CSS文件。

  • 參數

    • filename {String | Function}

      Extract Text Plugin爲每一個入口生成一個CSS文件,因此對於多入口項目須要指定filename參數[name]或[id]或[contenthash]生成惟一識別文件名;

    • disable {Boolean}

      禁用此插件;

    • allChunks {Boolean}

      allChunks: true;時指定從全部模塊中抽取CSS輸出至單獨CSS文件,包括異步引入的額外模塊;此插件默認是隻抽取初始模塊內引入的CSS;

  • extract方法

該方法能夠以參數指定加載器,而後接受該加載器的輸出,進行處理。須要在加載器和插件配置中同時聲明相關配置,才能實現效果;在加載器配置中調用其extract方法傳入一般如下兩個參數:

1. use: 將CSS轉換成模塊的加載器;
2. fallback: 對於不被抽取輸出至單獨CSS文件的CSS模塊使用的加載器,上例中`style-loader`即說明之內聯方式使用,該加載器一般在`allChunks: false`時處理額外的模塊;

filename與output

在上一篇介紹了輸出文件配置output相關內容,其中:

  • output.path是webpack處理文件後輸出的路徑,對於CSS文件輸出依然適用,即CSS文件也將輸出至該目錄;
  • output.publicPath是指瀏覽器訪問資源時的相對基礎路徑,規則是: output.publicPath + output.filename;

你能夠看到在本系列文章實例中filename都添加了前綴目錄,如cssscripts,你可能看到不少項目是不添加的,但文件入口較多時建議分類型目錄輸出,並且須要記得在瀏覽器訪問資源時也須要添加該目錄層級。

CSS預處理器

一般在開發較複雜的應用時,咱們都會選擇一種CSS的強化輔助工具,以更高效,更方便的使用CSS開發應用樣式,這些拓展工具就是所說的CSS預處理器.

**CSS預處理器(preprocessors)**在CSS語法的基礎上增長了變量 (variables)、嵌套 (nested rules)、混合 (mixins)、導入 (inline imports) 等高級功能,令CSS更增強大與優雅,有助於更好地組織管理樣式文件,以及更高效地開發項目。

目前最多見的CSS預處理器有LESS,SASS,Stylus,我的用過的是前兩種,使用SASS的仍是居多。

SASS

  • 安裝
npm install --save-dev sass-loader

安裝sass-loader之後會發現,package.json中多了一個node-sass依賴,這是使用SASS必須的。

  • 配置

而後添加如下配置:

{
        test: /\.s[ac]ss$/,
        exclude: /node_modules/,
        loader: 'style-loader!css-loader!sass-loader'
    }

如上,配置中傳遞了三個加載器,相對於前文處理CSS文件的加載器,在最後面多了一個sass-loader,首先加載sass-loader加載器處理SASS文件成CSS代碼,而後繼續按照前文描述流程處理CSS文件。

Extract Text Plugin

和處理CSS文件同樣,上述配置最終經過style-loader將轉換後的CSS代碼內聯到頁面,咱們須要使用Extract Text Plugin生成單獨CSS文件,之外鏈方式引用:

{
         test: /\.s[ac]ss$/,
         exclude: /node_modules/,
         loader: ExtractTextPlugin.extract({
         fallback:'style-loader',
             use: [
                  'css-loader',
                  'sass-loader'
             ]
         })
    }

	...

	// 生成獨立css文件
        new ExtractTextPlugin({
            filename: 'css/[name].css'
        })

CSS後處理器

前面講到CSS預處理器,如SASS,他們提供對CSS的拓展,包括語法拓展,高級特性拓展,如嵌套,變量,自動處理添加屬性前綴等,使得咱們能夠以其定義的語法與模板方式更高效的編寫CSS,然而這些預處理器都是另外對CSS進行拓展,各自定義了語法和模板,其處理流程是對代碼進行解析處理,而後轉換成CSS代碼。

不一樣預處理器有各自的定義和規範,假如你須要從LESS轉到SASS,源代碼轉換成本和學習成本頗高,而接下來要介紹的CSS後處理器並無這個問題。

不一樣於預處理器預約義好一個語法和模板,而後對按照該語法和模板編寫的代碼進行處理轉換成CSS,其輸入是自定義語法文件,輸出是CSS代碼;**後處理器(postprocessor)**是對原生CSS代碼根據配置進行處理,其輸入輸出依然是CSS代碼。

postcss

如今最受歡迎的CSS後處理器,就是postcss:

PostCSS is a tool for transforming styles with JS plugins. These plugins can lint your CSS, support variables and mixins, transpile future CSS syntax, inline images, and more. PostCSS是一個使用Js插件轉換樣式的根據,插件支持拓展CSS,如變量,混合,CSS屬性語法兼容,行內圖片等等功能。

特性

不一樣於SASS提供一個功能性拓展工具,postcss更多的是提供一個CSS高效開發工具解決方式,其自己只包含CSS解析器只能將CSS處理成一棵抽象語法樹(AST),同時提供一個豐富的CSS節點樹API,能夠對語法樹進行操做,另外它有一個高拓展性的插件系統支持咱們經過引入不一樣插件對CSS進行處理,一個插件的輸出還能夠是下一個插件的輸入,更值得一提的是,這些插件都是JavaScript插件,前端開發者們很容易就能根據項目需求定製本身的插件,因此能夠總結幾點一如下特性:

  1. postcss只處理CSS,概念簡潔;
  2. 提供高拓展性的插件系統支持按需引入不一樣插件,實現不一樣處理;
  3. 使用JavaScript插件,開發者能夠很方便定製項目插件;
  4. 提供CSS節點樹API,能夠高效的進行CSS處理;
  • 安裝

在webpack中使用,須要先安裝對應加載器:

npm install --save-dev postcss-loader
插件

postcss目前有200+插件,足夠知足絕大部分項目開發需求,能夠查看postcss插件,咱們介紹幾個主要使用的插件。

Autoprefixer

回顧一下在預處理器中,若是咱們須要爲CSS代碼添加屬性前綴,須要這麼實現呢?對於Sass,咱們一般使用mixin,即混合宏,處理CSS屬性前綴,如:

// 定義
	@mixin prefix-animation($animation-name){
    	animation:$animation-name;
    	-webkit-animation:$animation-name;
	}

	// 使用
	body{
    	@include prefix-animation(loading .5s linear infinite);
	}

如上,咱們須要按照定義的語法和模板:先定義一個mixin,而後經過@include方式使用,最後才能輸出添加前綴的CSS代碼,當代碼愈來愈多時,咱們須要定義的mixin也會愈來愈多,並且不一樣預處理器定義的語法和模板都有差別,學習成本、轉換成本都極可能使人難以接受。

那麼postcss插件怎麼處理的呢?postcss提供了Autoprefixer插件處理CSS屬性前綴:

Autoprefixer插件基於Can I Use的數據,對CSS規則進行前綴處理。

  • 安裝

首先仍是要安裝Autoprefixer:

npm install --save-dev autoprefixer
  • 配置

添加以下配置:

module: {
		loaders: [
			{
         		test: /\.css$/,
         		exclude: /node_modules/,
         		loaders: [
					'style-loader',
					'css-loader',
					{ 
                        loader: 'postcss-loader', 
                        options: {
                            plugins: [
                                require('autoprefixer')({
                                    browsers: ['last 2 versions']
                                })
                            ]  
                        } 
                    }
				]
    		}
		]
	}

如上,咱們知道postcss是一個樣式開發解決方案,其特定功能須要引入插件實現,上例中在指定postcss-loader加載器時爲其設置了插件配置autoprefixer;固然webpack還支持直接設置一個postcss配置文件,而後在項目根目錄建立postcss.config.js配置文件,內容格式以下:

module.exports = {
  		plugins: [
    		require('autoprefixer')({
            	browsers: ['last 2 versions']
        	})
			// or just require('autoprefixer')
  		]
	}

使用autoprefixer插件時可選傳入browsers參數,能夠設置添加前綴的適配範圍,詳細可查閱browsers配置說明

混合使用CSS預處理器與後處理器 - PreCSS

也許你火燒眉毛想在項目中引入postcss,又但願能繼續使用CSS預處理器語法,並且須要保證之前按照某預處理器預約語法和模板(如SASS)編寫的源代碼繼續穩定使用,不須要太多的遷移和學習成本,能夠作到嗎?固然能夠,可使用預處理器PreCSS插件包,另外咱們須要安裝一個postcss的scss解析器,由於postcss默認只包含一個CSS解析器,postcss配置文件更新以下:

module.exports = {
    	parser: require('postcss-scss'),
    	plugins: [
        	require('autoprefixer')({
            	browsers: ['last 2 versions']
        	}),
        	require('precss')
    	]
	}

webpack配置文件更新配置:

modules: {
		loaders: [
			{
                test: /\.s?[ac]ss$/,
                exclude: /node_modules/,
                // or 內聯方式 loader: 'style-loader!css-loader!postcss-loader'
                loader: ExtractTextPlugin.extract({
                    fallback:'style-loader',
                    use: [
                        'css-loader',
                        'postcss-loader'
                    ]
                })
             }
		]
	}

能夠看到文件匹配規則,修改成/\.s?[ac]ss$/,能夠匹配包括.sass, .scss, .css樣式文件;在css-loader加載器以前添加了postcss-loader加載器(webpack加載器解析順序爲從右至左)。

固然你能夠不使用precss,依然使用sass-loader,則只須要修改配置:

loader: 'style-loader!css-loader!postcss-loader!sass-loader'

對於以下SCSS代碼:

$column: 200px;
	.menu {
		display: flex;
    	width: calc(4 * $column);
	}

轉換生成以下CSS代碼:

.menu {
		display: -webkit-box;
    	display: -ms-flexbox;
    	display: flex; 
    	width: calc(4 * 200px);
	}

處理圖片與字體文件

對於一個應用而言,除了須要開發HTML、CSS、JavaScript,一般還好使用到圖片,字體文件,配置文件等諸多資源,那麼前端工程化流程也就必然須要對這些資源進行處理與優化,最典型的說處理圖片和字體文件。

在Grunt或Gulp中,咱們對圖片和字體文件的處理一般是將其從源目錄壓縮優化處理後輸出至輸出目錄,一般是以文件目錄總體進行處理,每次構建時,對全部資源,包括未使用的圖片均進行處理,效率是有侷限的;而webpack中一切資源文件均可以處理成模塊,而後在編譯時管理模塊依賴,能夠作到只處理存在依賴的資源(即,使用了的資源)。

圖片與字體

當咱們在Js模塊中引入CSS文件時,其中樣式規則中的背景圖片,字體文件如何處理呢?webpack只能管理模塊化的東西,須要將其模塊化,而後使用webpack管理依賴,webpack提供了file-loader加載器:

File Loader

Instructs webpack to emit the required object as file and to return its public url. 通知webpack將引入的對象輸出爲文件並返回其公開資源路徑。

  • 配置
module: {
    	loaders: [
      		{
        		test: /\.(png|svg|jpe?g|gif)$/,
        		loader: [
          			'file-loader'
        		]
      		}
    	]
  	}
  • 說明

當咱們在js文件中import Image from '../images/test.png'或在CSS文件中url('../images/test.png')時,file-loader將處理該圖片並在output.path目錄下輸出文件,而後將../images/test.png路徑替換成該輸出文件路徑。

注,對於html中引用的圖片,須要使用[html-loader]加載器處理(http://npm.taobao.org/package/html-loader)。

  • 參數

    1. emitFile: 是否輸出文件;
    2. name: 指定輸出文件的文件名,有幾個可用內置變量:
  • 實例

在配置時能夠指定參數:file-loader?name=[name].[ext]?[hash:8]或者以配置對象方式:

{
        test: /\.(png|svg|jpe?g|gif)$/,
        loaders: [
            // 'file-loader?name=[path][name].[ext]?[hash:8]'
            {
                loader: 'file-loader',
                query: {
                    name: '[path][name].[ext]?[hash:8]'
                }
            }
        ]
    }

對於CSS源代碼:

.wrapper {
        font-size: 18px;
        background: url('../images/test.png') no-repeat 0 0;
    }

輸出CSS代碼以下:

.wrapper {
    	font-size: 18px;
    	background: url(assets/images/test.png?59427321) no-repeat 0 0;
	}

assetsoutput.publicPath指定值,images/test.png?59427321爲配置文件中指定的name模板,在output.path目錄下輸出images/test.png,區別是,不會攜帶?後的參數。

另外,你也能夠在js模板中這樣使用:

<img src={imgSrc} />

	...
	import imgSrc from 'path/xxx.png';
Url Loader

你可能會發現前面並無安裝file-loader,由於有更好用的加載器url-loaderurl-loader加載器是file-loader的升級版,他們惟一的不一樣之處在於:

url-loader能夠經過limit參數指定一個尺寸值,加載器會對小於該值的資源處理返回一個Data URL,以base64的方式嵌入HTML或CSS,如url-loader?limit=65000;對於大於該尺寸的資源將使用file-loader處理而且傳遞全部參數。

  • mimetype

還能夠設置mimetype對處理文件進行過濾,如url-loader?mimetype=image/png將只處理png類型的資源。

  • 安裝
npm install --save-dev url-loader
  • 配置

該加載器對於圖片和字體文件資源都適用:

{
         test: /\.(png|svg|jpe?g|gif)$/,
         loaders: [
             // 'url-loader?name=[path][name].[ext]?[hash:8]'
             {
                 loader: 'url-loader',
                 query: {
                     limit: 6000,
                     name: '[path][name].[ext]?[hash:8]'
                 }
             }
          ]
    }, {
         test: /\.(woff|woff2|eot|ttf|otf)$/,
         loaders: [{
             loader: 'url-loader',
             query: {
                 limit: 10000,
                 name: '[path][name].[ext]?[hash:8]'
             }
         }]
    }

資源優化

完成以上配置後,已經能夠在項目中很方便的引用各自資源了,可是一般咱們還須要對圖片字體等文件進行壓縮優化處理,如Grunt中使用的imagemin插件同樣壓縮資源,webpack則提供了相關加載器img-loader

  • 安裝
npm install --save-dev img-loader
  • 配置
{
        test: /\.(jpe?g|png|gif|svg)$/i,
        loaders: [
            'url-loader?name=[path][name].[ext]?[hash:8]',
            {
            	loader: 'img-loader',
            	options: {
					// 根據環境判斷是否啓用資源壓縮
              		enabled: process.env.NODE_ENV === 'production', 
              		gifsicle: {
                		interlaced: false // 替換使用漸進式渲染
              		},
              		mozjpeg: {
                		progressive: true, // 建立基準jpeg文件
              		},
              		optipng: {
                        optimizationLevel: 4, // 優化級別,0-7,值越大,壓縮越多
                    }, 
              		pngquant: {
						quality: '75-90', // 壓縮質量,0-100,值越高,質量越高
                		speed: 3 // 執行速度,0-10,速度太高質量受損,不建議太高
              		},
              		svgo: {
                  		plugins: [
                        	{ removeTitle: true }, // 去除標題信息
                  			{ convertPathData: false } // 轉換路徑爲更短的相對或決定路徑
                		]
              		}
            	}
          	}
        ]
    }

以上爲常見使用配置,更多詳細配置信息請查看對應說明imagemin文檔,特別注意的是上面使用了process.env.NODE_ENV當前環境變量,只有在生產環境啓用圖片壓縮,由於壓縮過程比較比較耗時,可能會下降開發、調試效率。

數據資源

對於數據類型文件資源,webpack內置支持加載解析.json文件,而其餘類型則須要安裝配置相應加載器,如.xml文件,須要安裝並配置xml-loader

資源管理的思考

在傳統或稍早一點的應用中,咱們一般會將全部的圖片,字體等資源放在一個基礎目錄下,如assets/images,可是對於那些在多項目間重複的插件代碼或資源來講,每一次遷移,咱們都得在一大堆圖片,字體資源裏尋找出咱們須要遷移的資源,這對代碼可重用和其獨立性有必定限制,並且與如今提倡的組件化開發模式也不相符。

webpack對於資源的處理方式給組件化開發提供了很大便利,使得咱們以組件爲單位,能夠在某一組件目錄下存放全部相關的js,css,圖片,字體等資源文件;組件的遷移公用成本很低。不過組件化開發並非說不須要資源目錄了,一些公用的資源依然放在項目的基礎目錄下。

說明

終於能夠鬆口氣,對於webpack管理CSS、圖片、字體、數據資源的實踐基本總結完成,其實感受要介紹的還有不少,可是要儘可能保證文章思路清晰,語句流暢,並且篇幅不能太長,水平有限,花費較多時間經歷,但願能對讀者有所幫助,後續篇章也會繼續穿插介紹,力爭本系列能較完整、較清晰地描述如何使用webpack開發SPA應用。

原創文章,轉載請註明: 轉載自 熊建剛的博客

本文連接地址: webpack與SPA實踐之管理CSS等資源

相關文章
相關標籤/搜索