webpack對樣式的處理


 

原文地址:https://github.com/zhengweikeng/blog/issues/9

我們可以在js中引入樣式文件

require('myStyle.css')

這時我們便需要引入相應的webpack loader來幫助我們解析這段代碼。

一般來說需要引入css-loader和style-loader,其中css-loader用於解析,而style-loader則將解析后的樣式嵌入js代碼。

// webpack配置如下
{
  module: { loaders: [ { test: /\.$/, loader: "style-loader!css-loader" } ] } }

可以發現,webpack的loader的配置是從右往左的,從上面代碼看的話,就是先使用css-loader之后使用style-loader。

同理,如果你使用less來寫樣式的話,則需要先用less-loader來編譯樣式文件為css文件,再繼續使用css-loader與style-loader。

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

我們知道,webpack配置loader時是可以不寫loader的后綴明-loader,因此css-loader可以寫為css。

將樣式抽取出來為獨立的文件

將require引入的樣式嵌入js文件中,有好處也有壞處。好處是減少了請求數,壞處也很明顯,就是當你的樣式文件很大時,造成編譯的js文件也很大。

我們可以使用插件的方式,將樣式抽取成獨立的文件。使用的插件就是extract-text-webpack-plugin

基本用法如下

var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { module: { loaders: [ { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") } ] }, plugins: [ new ExtractTextPlugin("styles.css") ] }

根據插件在github上的解釋,ExtractTextPlugin.extract可以有三個參數。

第一個參數是可選參數,傳入一個loader,當css樣式沒有被抽取的時候可以使用該loader。

第二個參數則是用於編譯解析的css文件loader,很明顯這個是必須傳入的,就像上述例子的css-loader。

第三個參數是一些額外的備選項,貌似目前只有傳入publicPath,用於當前loader的路徑。

那什么時候需要傳入第一個參數呢,那就得明白什么時候樣式不會被抽取出來。

了解過code splittiog的同學便會知道,我們有些代碼在加載頁面的時候不會被使用時,使用code splitting,可以實現將這部分不會使用的代碼分離出去,獨立成一個單獨的文件,實現按需加載。

那么如果在這些分離出去的代碼中如果有使用require引入樣式文件,那么使用ExtractTextPlugin這部分樣式代碼是不會被抽取出來的。

這部分不會抽取出來的代碼,可以使用loader做一些處理,這就是ExtractTextPlugin.extract第一個參數的作用。

根據上面的案例,ExtractTextPlugin需要配合plugin使用。

new ExtractTextPlugin([id: string], filename: string, [options])
  1. 該插件實例的唯一標志,一般是不會傳的,其自己會生成。
  2. 文件名。可以是[name]、[id]、[contenthash]
    [name]:將會和entry中的chunk的名字一致
    [id]:將會和entry中的chunk的id一致
    [contenthash]:根據內容生成hash值
  3. options
    allchunk: 是否將所有額外的chunk都壓縮成一個文件
    disable:禁止使用插件

這里的參數filename里如何理解呢?上述案例指定了一個固定的名字,因此便會生成一個styles.css文件。

那么像[name]、[id]這些如何理解。這個在你有多個entry的時候,便需要使用這種方式來命名。

var ExtractTextPlugin = require("extract-text-webpack-plugin"); module.exports = { entry: { "script": "./src/entry.js", "bundle": "./src/entry2.js", }, ... module: { loaders: [ { test: /\.css$/, loader: ExtractTextPlugin.extract("style-loader", "css-loader") } ] }, plugins: [ new ExtractTextPlugin("[name].css") ] }

這時候便會生成兩個css文件,一個是script.css,另一個便是bundle.css。那些[id]、[contenthash]也是一個道理。

只要明白,在你有多個entry是,一定要使用這種方式來命名css文件。

最后還有那個allchunks又是什么呢?很簡單,還記得前面提到的code splitting么?將該參數配置為true,那么所有分離文件的樣式也會全部壓縮到一個文件上。

plugins: [
  new ExtractTextPlugin("[name].css", {allChunks: true})
]

postcss

以前我們寫樣式時,有些樣式不同瀏覽器需要加不同的前綴,如-webkit-。現在有了構建工具,我們便不需要再去關注這些前綴了,構建工具會自動幫我們加上這些前綴。

對於webpack我們自然想到需要使用loader或者plugin來幫助我們做這些事情,查了下發現autoprefixer-loader已經廢棄不再維護了,推薦使用posscss

postcss是用於在js中轉換css樣式的js插件,需要搭配其他插件一起使用,這點和babel6一樣,本身只是個轉換器,並不提供代碼解析功能。

這里我們需要autoprefixer插件來為我們的樣式添加前綴。首先下載該模塊。

npm install autoprefixer --save-dev

接着便可以配置webpack了

var autoprefixer = require('autoprefixer') module.exports = { ... module: { loaders: [ ... { { test: /\.css$/, loader: ExtractTextPlugin.extract(["css-loader", "postcss-loader"]) }, } ] }, postcss: [autoprefixer()], ... }

查看一下抽取出來的樣式文件便可以發現已經加上了前綴

a {
    display: flex; } /*compiles to:*/ a { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex }

另外autoprefixer還可以根據目標瀏覽器版本生成不同的前綴個數,例如你的應用的使用用戶如果大多數是使用比較新版本的瀏覽器,那么便可以做如下配置。

postcss: [autoprefixer({ browsers: ['last 2 versions'] })]

這是生成的樣式便會有些不一樣,還是上面的例子

a {
    display: flex; } /*compiles to:*/ a { display: -webkit-flex; display: -ms-flexbox; display: flex; }

postcss后記

這里再說一個問題,有些童鞋可能會在css文件中使用@import引入其他樣式文件,但是使用autoprefixer發現,import進來的樣式沒有處理,如下面所示:

/*myStyle.css:*/
body { background-color: gray; } .flex { display: flex; } /*myStyle2.css:*/ @import "./myStyle.css"; .div { color: red; } /*autoprefixer之后*/ body { background-color: gray; } .flex { display: -webkit-box; display: -webkit-flex; display: -ms-flexbox; display: flex; } body { background-color: gray; } .flex { display: flex; } .div { color: red; }

要解決這個問題,postcss有個解釋,它讓我們使用postcss-import插件,再配合autoprefixer

postcss: function(webpack) { return [ postcssImport({ addDependencyTo: webpack }), autoprefixer ] },

其實我們是不推薦使用@import的,心細的童鞋可以看到最后生成的樣式文件有樣式是重復的。

所以一般我們應該是在js中使用require來引入樣式文件。可以參考的說法這里

樣式壓縮

壓縮代碼我們可以使用webpack的內置插件UglifyJsPlugin來做,它既可以壓縮js代碼也可以壓縮css代碼。

plugins: [
  ... new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), ... ]

其實並不能說是在壓縮css代碼,本質來說還是壓縮js代碼,再將這塊代碼輸出到css文件中。

使用CommonsChunkPlugin抽取公共代碼

首先要明確一點CommonsChunkPlugin是在有多個entry時使用的,即在有多個入口文件時,這些入口文件可能會有一些共同的代碼,我們便可以將這些共同的代碼抽取出來成獨立的文件。明白這一點非常重要。(搞了很久才明白的一點,唉~~~~)

如果在多個entry中require了相同的css文件,我們便可以使用CommonsChunkPlugin來將這些共同的樣式文件抽取出來為獨立的樣式文件。

module.exports = { entry: { "A": "./src/entry.js", "B": "./src/entry2.js" }, ... plugins: [ new webpack.optimize.CommonsChunkPlugin({name: "commons", filename: "commons.js"}), ... ] }

當然,這里不止會抽取共同的css,如果有共同的js代碼,也會抽取成為commons.js。

這里有個有趣的現象,抽取出來的css文件的命名將會是參數中name的值,而js文件名則會是filename的值。

CommonsChunkPlugin好像只會將所有chunk中都共有的模塊抽取出來,如果存在如下的依賴

// entry1.js
var style1 = require('./style/myStyle.css') var style2 = require('./style/style.css') // entry2.js require("./style/myStyle.css") require("./style/myStyle2.css") // entry3.js require("./style/myStyle2.css")

使用插件后會發現,根本沒有生成commons.css文件。

如果我們只需要取前兩個chunk的共同代碼,我們可以這么做

module.exports = { entry: { "A": "./src/entry.js", "B": "./src/entry2.js", "C": "./src/entry3.js" }, ... plugins: [ new webpack.optimize.CommonsChunkPlugin({name: "commons", filename: "commons.js", chunks: ['A', 'B']}), ... ] }

補充一下

根據webpack官網中關於stylesheet的說法,建議是不要將allChunks設為true,即只是將樣式嵌入到分離文件中。

這個可能還是需要具體問題具體分析了。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM