Webpack3.0小案例躺坑css處理與ES6編譯

寫在前面

webpack3.0小案例webpack初體驗 一文中,咱們從總體瞭解了webpack相關特性並手動初步實現了一個可執行編譯的webpack環境,這一節,將在此基礎上繼續探討如下功能的實現:css

  • css的編譯與加載,基於style-loader、css-loader、postcss-loader、autoprefixer以及css預處理(以less爲例)。
  • ES6的編譯與加載,babel-preset-env代替babel-preset-2015。
  • css與js靜態資源分割及注意事項,基於插件 extract-text-webpack-plugin。

css資源文件的編譯與加載

css最基本的編譯要依靠於style-loader、css-loader這兩個加載器,所謂最基本,就是在不考慮使用css預處理器以及css後處理器的狀況。預處理器包括less、scss、sass、stylus,後處理器如postcss的autoprefixer等。首先咱們先弄明白各加載器的做用:html

  • style-loader,將全部處理好的css樣式以行內元素style標籤的格式動態注入到界面的head標籤中去。
  • css-loader,用來處理css樣式,例如把css中相似@import()或者url()這樣的引用資源進行引入或者處理。
  • autoprefixer,postcss提供用來自動處理css在不一樣瀏覽器之間前綴等問題,從這裏咱們也能夠看出,使用autoprefixer須要先引入postcss。
  • 預處理-loader,全部預處理器都是以css爲終點生成文件的一種專門的編程語言,爲css增長了一些編程特性。

瞭解了這些加載器各自的職能,那咱們如今先小試牛刀,用代碼來體驗一下。前端

style-loader && css-loader

一、首先,咱們在src項目文件夾下新建css文件夾,並新增同樣式文件common.css,並寫一些全局的cssnode

html,
body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to bottom, #abcdef, #f3f5f7);
}複製代碼

二、安裝css-loader和style-loader,進入到命令行,執行webpack

cnpm i style-loader css-loader --save-dev複製代碼

三、如今咱們在入口文件app.js中去引入咱們的common.css:css3

//app.js
 import './css/common.css';
 ...複製代碼

四、webpack配置,打開webpack.config.js,新增module配置項:es6

let path = require('path');
...
module.exports = {
    entry: path.resolve(__dirname, './src/main.js'),//入口文件地址
    output: {
        path: path.resolve(__dirname, './dist'),
        filename: 'js/[name]-[chunkhash].js'
    },
    module: { 
        rules: [ {
            test: /\.css$/,//匹配全部css文件
            use: [
                { loader: "style-loader" },
                { loader: "css-loader" }
             ]//指定加載器
            exclude: /node_modules///排除對node_module文件夾下面的全部資源的匹配
        }]
    },
    ...
}複製代碼

須要注意,在指定加載器的時候,要注意各加載器的順序,webpack中loader的解析是從右往左的順序進行的,以當前爲例,css文件首先是要經過css-loader進行處理,在將處理好的css交由style-loader,所以在指定加載器的時候,首先應該是style-loader,其次是css-loader。web

如今,咱們再次執行webpack編譯命令,在瀏覽器中打開生成的index.html,發現頁面背景已經如咱們設置改變了,而且html文檔中css也已經以style模式注入到head中了。chrome

style-loader與css-loader加載
style-loader與css-loader加載


autoprefixer

上文咱們已經說過,autoprefixer是css後處理器postcss提供的一個對css3中個別屬性在不一樣瀏覽器下須要添加瀏覽器前綴的樣式處理工具,所以在使用autoprefixer以前,咱們須要安裝postcss-loader來加載它。npm

cnpm i postcss-loader autoprefixer --save-dev複製代碼

一、如今,咱們打開webpack.config.js配置文件,在module下配置autoprefixer

...
module: {
        rules: [{
            test: /\.css$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader" },
                { loader: "postcss-loader" }//指定postcss加載器
            ],
            exclude: /node_modules/
        }]
    }
    ...複製代碼

在配置postcss-loader以後,咱們須要注意,因爲css-loader處理文件導入的方式,所以加載器postcss-loader不能與CSS模塊一塊兒使用。 爲了使它們正常工做,能夠添加css-loader的importLoaders選項。

...
module: {
        rules: [{
            test: /\.css$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader",options: { importLoaders: 1 } },//importLoaders解決因爲css-loader處理文件導入的方式致使postcss-loader不能正常使用的問題
                { loader: "postcss-loader" }//指定postcss加載器
            ],
            exclude: /node_modules/
        }]
    }
    ...複製代碼

二、接下來,咱們要給postcss-loader指定加載autoprefixer的操做,在根目錄新建postcss.config.js

//postcss.config.js
module.exports = {
    plugins: [
        require("autoprefixer")()
    ]
}複製代碼

在postcss.config.js配置文件中,咱們給引入了autoprefixer,這樣一來,在postcss-loader加載的時候,就會自動去讀取該配置文件裏面的配置項,加載autoprefixer了。

三、全部配置項已經完成,如今咱們在原有的common.css文件中去添加樣式,用來測試autoprefixer

html,
body {
    margin: 0;
    padding: 0;
    width: 100%;
    height: 100%;
    background: linear-gradient(to bottom, #abcdef, #f3f5f7);
}

.flexbox {
    display: flex;
    background: linear-gradient(to bottom, #abcdef, #096)
}複製代碼

添加完成後,運行webpack命令進行編譯,並在瀏覽器中打開index.html,打開控制檯查看head標籤下生成的style標籤,已經能夠看到autoprefixer已經自動爲咱們添加了瀏覽器前綴

autoprefixer
autoprefixer

autoprefixer 爲咱們提供了能夠根據需求配置的參數,例如我麼可讓它最終生成的css兼容最近的N個版本就好,但這不是咱們本次討論的重點,有興趣的同窗能夠本身去官網查看。

//postcss.config.js 設置給最近5個版本的瀏覽器加前綴
module.exports = {
    plugins: [
        require("autoprefixer")({browsers:'last 5 version'})
    ]
}複製代碼

autoprefixer-set-version
autoprefixer-set-version


css預處理(less-loader)

一、在src/css文件夾下新建style.less文件,並添加樣式做爲測試,並在入口文件app.js中引用該less文件

@base: #f938ab;

.box-shadow(@style, @c) when (iscolor(@c)) {
  -webkit-box-shadow: @style @c;
  box-shadow:         @style @c;
}
.box-shadow(@style, @alpha: 50%) when (isnumber(@alpha)) {
  .box-shadow(@style, rgba(0, 0, 0, @alpha));
}
.box {
  color: saturate(@base, 5%);
  border-color: lighten(@base, 30%);
  div { .box-shadow(0 0 5px, 30%) }
}複製代碼
//app.js
import './css/common.css';//引入css
import './css/style.less';//引入less複製代碼

二、安裝less以及less-loader,並在webpack配置項中進行配置。

cnpm i less less-loader --save-dev複製代碼

在module配置項中新增一條對less文件處理的規則:

...
 module: {
        rules: [{
            test: /\.css$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" },
            ],
            exclude: /node_modules/
        }, {
            test: /\.less$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" },
                { loader: "less-loader" }//less放在最後,由於要最早加載(loader從右往左加載的規則)
            ]
        }]
    },
...複製代碼

配置完成後,再次運行webpack命令,並在瀏覽器中查看效果,能夠看到在head標籤內又新增了一個style標籤,而且已經將less編譯成css了

less-loader
less-loader


Babel編譯ES6

babel是一個js的轉碼器,將ES6或者ES7轉換成ES5或者ES3;babel編譯主要依靠於核心庫babel-core,也就是說,用ES6進行編程而不須要擔憂瀏覽器環境是否支持。

babel-preset-env代替babel-preset-ES2015

在此以前,我猜想有不少的同窗使用Babel的時候,preset必然會選擇ES2015,可是最近babel官方推出了babel-preset-env,並建議在使用的時候選擇env代替以前的ES20**。env爲咱們提供了更智能的編譯選擇,在此咱們就不展開,有興趣的同窗能夠去官網深刻了解。

babel-preset-env編譯ES6

一、首先,咱們安裝babel-loader、babel-core以及babel-preset-env,並在入口文件app.js中新增一段帶ES6語法的js。

cnpm install --save-dev babel-loader babel-core babel-preset-env複製代碼
import './css/common.css';
import './css/style.less';
//生成一個整數隨機值,數值大於4則返回成功的Promise對象,不然返回錯誤的promise對象
function getData() {
    let promise = new Promise((resolve, reject) => {
        let key = ~~(Math.random() * 10);
        let temp = ['es6','babel']
        if (key >= 5) {
            let obj = {
                msg: "ok",
                data: [key,...temp]
            };
            resolve.call(this, obj);
        } else {
            let obj = {
                msg: "error",
                data: [key,...temp]
            };
            reject.call(this, obj);
        }
    })
    return promise
}
//找到頁面中的Dom
let container = document.querySelector('#app');
//獲取返回的結果並打印到界面
getData().then((data) => {
    container.innerHTML = JSON.stringify(data)
}, (err) => {
    container.innerHTML = JSON.stringify(err)
})複製代碼

在這段代碼中,我麼使用了Promise來返回最後生成的結果,並將結果打印到界面中,其中運用了Promise、箭頭函數、解構賦值等ES6的語法

二、如今,咱們去webpack配置文件中新增一條規則:

...
  module: {
        rules: [{
            test: /\.css$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" }
            ],
            exclude: /node_modules/
        }, {
            test: /\.less$/,
            use: [
                { loader: "style-loader" },
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" },
                { loader: "less-loader" }
            ]
        },
        //新增規則,編譯js
        {
            test: /\.js$/,
            loader: 'babel-loader',
            exclude: /node_modules/
        }]
    },複製代碼

而且在根目錄下新增.babelrc文件,用來存放babel相關的配置:

//.babelrc
{
    "presets": [
        ["env",{
            "targets": {
                "chrome": 52,
                "browsers": ["last 2 versions", "safari 7"]
            }
        }]
    ]//設置編譯場景,並配置目標結果兼容到chrome52版本以上等等。
}複製代碼

三、而後咱們去根目錄下的index.html裏面,新增一個div,而且給這個div的id取名app。

...
<body>
    <div id="app"></div>
</body>
...複製代碼

如今咱們在命令行運行webpack命令,並在瀏覽器中刷新界面,能夠看到頁面輸出了咱們結果。

babel編譯es6
babel編譯es6

而且,在生成的js中發現,經過編譯後的js已經再也不是es6的寫法了,變異後的核心代碼以下:

...
//編譯後的js
function getData() {
    var _this = this;

    var promise = new Promise(function (resolve, reject) {
        var key = ~~(Math.random() * 10);
        var temp = ['es6', 'babel'];
        if (key >= 5) {
            var obj = {
                msg: "ok",
                data: [key].concat(temp)
            };
            resolve.call(_this, obj);
        } else {
            var _obj = {
                msg: "error",
                data: [key].concat(temp)
            };
            reject.call(_this, _obj);
        }
    });
    return promise;
}
var container = document.querySelector('#app');

getData().then(function (data) {
    container.innerHTML = JSON.stringify(data);
}, function (err) {
    container.innerHTML = JSON.stringify(err);
});
...複製代碼

到此,經過babel編譯js就基本完成,固然,babel提供了不少的插件和配置項,有興趣的同窗能夠深刻了解,本文暫不作深層次探討。


CSS與JS文件分離

經過上面的操做,咱們已經能夠成功的將css和js進行編譯打包,可是當咱們再次去查看打包後的js文件時,發現js體積很大(目前js大小22kb),咱們只是寫了幾行js而已啊,那麼這又是怎麼回事?

實際上,webpack在打包的時候,會把全部的模塊,最終打包在一個文件裏面,這也是爲何咱們的js文件會如此之大,但卻沒看到有相應的css文件的緣由。

靜態資源處理器extract-text-webpack-plugin

extract-text-webpack-plugin用來實現不一樣文件的分離,其用法也和以前插件的用法同樣。
一、安裝插件

cnpm i extract-text-webpack-plugin --save-dev複製代碼

二、在webpack配置項中對以前咱們的css編譯規則進行更改:

...
//引入插件
let extractTextPlugin = require('extract-text-webpack-plugin');
//初始化兩個實例用於兩處規則分別加載
let extractCSS = new extractTextPlugin('css/[name]-one.css');
let extractLESS = new extractTextPlugin('css/[name]-two.css');

 ...
  module: {
        rules: [{
            test: /\.css$/,
            exclude: /node_modules/,
            //extractCSS實例對css進行操做
            use: extractCSS.extract([
                // { loader: "style-loader" },//style-loader不能和插件一塊兒使用
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" }
            ])
        }, {
            test: /\.less$/,
            exclude: /node_modules/,
            //extractLESS實例對less進行操做
            use: extractLESS.extract([
                // { loader: "style-loader" },
                { loader: "css-loader", options: { importLoaders: 1 } },
                { loader: "postcss-loader" },
                { loader: "less-loader" }
            ])
        }, {
            test: /\.js$/,
            use: [{ loader: "babel-loader" }],
            exclude: /node_modules/
        }]
    },
    ...
     plugins: [
        //註冊插件
        extractCSS,
        extractLESS
    ]複製代碼

在配置中須要注意一下幾點:

  • 插件不能喝style-loader同時使用,緣由是style-loader的做用是獲取到js內部的樣式並一行內形式插入到head標籤中,可是extractTextPlugin插件是將js內部的代碼提取成css文件並添加外部引用的方式進行加載。
  • 在多處用到extractTextPlugin插件的時候,咱們須要對插件進行功能性的拆分,就好比咱們的項目中有兩處規則用到它,所以咱們首先對插件進行兩次實例化獲得兩個實例,並分別用到兩個處理規則中。
  • 別忘了在plugins下進行註冊。

最後,咱們再看咱們生成的js文件,已經只有4kb大小,減小了接近5倍的體積,可想而知,分離出css是頗有必要的。

靜態資源分離
靜態資源分離

寫在最後

本節咱們隊webpack的加載器模塊相關配置進行實戰演練,學習完這一節後,您應該瞭解一下幾點:

  • css的處理相關,包括各加載器的做用、執行順序、css預處理和後處理操做。
  • ES6的編譯與加載,包括babel-preset-env替換babel-preset-es2015的使用方法。
  • css資源與js資源的分割,包括插件 extract-text-webpack-plugin對靜態資源的處理方式和注意事項。

下一節,咱們將繼續探討webpack處理文件資源的方法,包括圖片加載、圖片轉base6四、icon及字體處理等等。

願各位早日成爲一名合格的前端高手,加油!

相關文章
相關標籤/搜索