WebPack1.x 經常使用功能介紹

注意:本文描述的配置只適用webpack1.x;因爲webpack已經推出2.x並有大量更改,特此申明javascript

概述

Webpack是一款用戶打包前端模塊的工具。主要是用來打包在瀏覽器端使用的javascript的。同時也能轉換、捆綁、打包其餘的靜態資源,包括css、image、font file、template等。這裏就儘可能詳細的來介紹下一些基本功能的使用。css

安裝

npm install webpack -g

運行webpack

webpack須要編寫一個config文件,而後根據這個文件來執行須要的打包功能。咱們如今來編寫一個最簡單的config。新建一個文件,命名爲webpack-config.js。config文件實際上就是一個Commonjs的模塊。內容以下:html

var path = require('path');
var buildPath = path.resolve(__dirname,"build");
var nodemodulesPath = path.resolve(__dirname,'node_modules');

var config = {
    //入口文件配置
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]//當requrie的模塊找不到時,添加這些後綴
    },
    //文件導出的配置
    output:{
        path:buildPath,
        filename:"app.js"
    }
}

module.exports = config;

個人目錄結構是這樣的:前端

webpack
    |---index.html
    |---webpack-config.js
    |---src
         |---main.js
         |---js
              |---a.js

main.js文件內容以下:java

var a = require('./js/a');
a();
console.log('hello world');
document.getElementById("container").innerHTML = "<p>hello world</p>";

a.js文件內容以下:node

module.exports = function(){
    console.log('it is a ');
}

而後咱們執行以下的命令:react

webpack --config webpack-config.js --colors

這樣咱們就能在目錄裏面看到一個新生成的目錄build,目錄結構以下:webpack

webpack
    |---index.html
    |---webpack-config.js
    |---build
         |---app.js

而後引用app.js就Ok啦。main.js和模塊a.js的內容就都打包到app.js中了。這就演示了一個最簡單的把模塊的js打包到一個文件的過程了。git

介紹webpack config文件

webpack是根據config裏面描述的內容對一個項目進行打包的。接着咱們來解釋下config文件中的節點分別表明什麼意思。一個config文件,基本都是由如下幾個配置項組成的。es6

entry

配置要打包的文件的入口;能夠配置多個入口文件,下面會有介紹。

resolve

配置文件後綴名(extensions),除了js,還有jsx、coffee等等。
alias配置項,能夠爲經常使用模塊配置改屬性,能夠節省編譯的搜索時間。例如:

resolve:{
        extensions:['.js','.jsx'],
        alias:{
            'react':path.join(nodeModulesPath,'react/react.js')
        }
    }

除了這個功能還能夠配置其餘有用的功能,因爲我還不徹底瞭解,有知道的朋友歡迎指教。

output

配置輸出文件的路徑,文件名等。

module(loaders)

配置要使用的loader。把資源文件(css、圖片、html等非js模塊)處理成相應的js模塊,而後其它的plugins才能對這些資源進行下一步處理。好比babel-loader能夠把es6的文件轉換成es5。
大部分的對文件的處理的功能都是經過loader實現的。loader能夠用來處理在入口文件中require的和其餘方式引用進來的文件。loader通常是一個獨立的node模塊,要單獨安裝。

loader配置項:

test: /\.(js|jsx)$/,           //注意是正則表達式,不要加引號,匹配要處理的文件
loader: 'eslint-loader',       //要使用的loader,"-loader"能夠省略
include: [path.resolve(__dirname, "src/app")],   //把要處理的目錄包括進來
exclude: [nodeModulesPath]     //排除不處理的目錄

目前已有的loader列表:https://webpack.github.io/docs/list-of-loaders.html

一個module的例子:

module: {
    preLoaders: [
      {
        test: /\.(js|jsx)$/,
        loader: 'eslint-loader',
        include: [path.resolve(__dirname, "src/app")],
        exclude: [nodeModulesPath]
      },
    ],
    loaders: [
      {
        test: /\.(js|jsx)$/, //正則表達式匹配 .js 和 .jsx 文件
        loader: 'babel-loader?optional=runtime&stage=0',//對匹配的文件進行處理的loader 
        exclude: [nodeModulesPath]//排除node module中的文件
      }
    ]
}

plugins

顧名思義,就是配置要使用的插件。plugin是比loader功能更強大的插件,能使用更多的wepack api。來看一個使用plugin的例子:

plugins: [
    //壓縮打包的文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        //supresses warnings, usually from module minification
        warnings: false
      }
    }),
    //容許錯誤不打斷程序
    new webpack.NoErrorsPlugin(),
    //把指定文件夾xia的文件複製到指定的目錄
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ]

目前已有的plugins列表:http://webpack.github.io/docs/list-of-plugins.html

如何壓縮輸出的文件

plugins: [
    //壓縮打包的文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        //supresses warnings, usually from module minification
        warnings: false
      }
    })]

如何copy目錄下的文件到輸出目錄

copy文件須要經過插件"transfer-webpack-plugin"來完成。

安裝:

npm install transfer-webpack-plugin  -save

配置:

var TransferWebpackPlugin = require('transfer-webpack-plugin');
//其餘節點省略    
plugins: [
    //把指定文件夾下的文件複製到指定的目錄
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ]

打包javascript模塊

支持的js模塊化方案包括:

  • ES6 模塊

    import MyModule from './MyModule.js';
  • CommonJS

    var MyModule = require('./MyModule.js');
  • AMD

    define(['./MyModule.js'], function (MyModule) {
    });

上面已經演示了打包js模塊,這裏再也不重複。ES6的模塊須要配置babel-loader來先把處理一下js文件。
下面展現下打包ES模塊的配置文件:

var webpack = require('webpack');
var path = require('path');
var buildPath = path.resolve(__dirname, 'build');
var nodeModulesPath = path.resolve(__dirname, 'node_modules');
var TransferWebpackPlugin = require('transfer-webpack-plugin');

var config = {
  entry: [path.join(__dirname, 'src/main.js')],
  resolve: {
    extensions: ["", ".js", ".jsx"]
    //node_modules: ["web_modules", "node_modules"]  (Default Settings)
  },
  output: {
    path: buildPath,    
    filename: 'app.js'  
  },
  plugins: [
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        warnings: false
      }
    }),
    new webpack.NoErrorsPlugin(),
    new TransferWebpackPlugin([
      {from: 'www'}
    ], path.resolve(__dirname,"src"))
  ],
  module: {
    preLoaders: [
      {
        test: /\.(js|jsx)$/,
        loader: 'eslint-loader',
        include: [path.resolve(__dirname, "src/app")],
        exclude: [nodeModulesPath]
      },
    ],
    loaders: [
      {
        test: /\.js$/, //注意是正則表達式,不要加引號
        loader: 'babel-loader?optional=runtime&stage=0',//babel模塊相關的功能請自查,這裏不作介紹
        exclude: [nodeModulesPath]
      }
    ]
  },
  //Eslint config
  eslint: {
    configFile: '.eslintrc' //Rules for eslint
  },
};

module.exports = config;

打包靜態資源

css/sass/less

安裝css-loader和style-loader

npm install css-loader --save -dev
npm install style-loader --save -dev

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',//sass配置:style!css!sass 執行順序:左<--右
            exclude:nodemodulesPath
        }]
    }
}

style-loader會把css文件嵌入到html的style標籤裏,css-loader會把css按字符串導出,這兩個基本都是組合使用的。打包完成的文件,引用執行後,會發現css的內容都插入到了head裏的一個style標籤裏。
若是是sass或less配置方式與上面相似。

images

能夠經過url-loader把較小的圖片轉換成base64的字符串內嵌在生成的文件裏。
安裝:

npm install url-loader --save -dev

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',//
            exclude:nodemodulesPath
        },
        { test:/\.png$/,loader:'url-loader?limit=10000'}//限制大小小於10k的
        ]
    }
}

css文件內容:

#container{
    color: #f00;
    background:url(images/logo-201305.png);
    /*生成完圖片會被處理成base64的字符串 注意:不要寫'/images/logo-201305.png',不然圖片不被處理*/
}

iconfont

內嵌iconfont的使用方法其實和上述處理png圖片的方法一致。經過url-loader來處理。

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',//
            exclude:nodemodulesPath
        },
        { test:/\.(png|woff|svg|ttf|eot)$/,loader:'url-loader?limit=10000'}//限制大小小於10k的
        ]
    }
}

css文件內容:

@font-face {font-family: 'iconfont';
src: url('fonts/iconfont.eot'); /* IE9*/
src: url('fonts/iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */
url('fonts/iconfont.woff') format('woff'), /* chrome、firefox */
url('fonts/iconfont.ttf') format('truetype'), /* chrome、firefox、opera、Safari, Android, iOS 4.2+*/
url('fonts/iconfont.svg#iconfont') format('svg'); /* iOS 4.1- */
}

執行打包後會把字體文件都轉換成base64字符串內容到文件裏.
這裏有個頭疼的問題,就是每一個瀏覽器支持的字體格式不同,因爲把所有格式的字體打包進去,形成沒必要要的資源浪費。

打包template

我以打包handlebars的模塊爲例,來演示下打包模塊的過程。有的模板對應的loader,有可能沒有現成的,恐怕要本身實現loader。

先安裝必須的node模塊

npm install handlebars-loader --save -dev
npm install handlebars -save//是必須的

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[
        { test: /\.html$/, loader: "handlebars-loader" }
        ]
    }
}

新建一個模板文件tb.html,目錄結構:

webpack
    |---index.html
    |---webpack-config.js
    |---src
         |---template
         |         |---tb.html
         |---main.js

main.js中調用模塊的代碼以下:

var template = require("./template/tp.html");
var data={say_hello:"it is handlebars"};
var html = template(data);
document.getElementById('tmpl_container').innerHTML = html;

公用的模塊分開打包

這須要經過插件「CommonsChunkPlugin」來實現。這個插件不須要安裝,由於webpack已經把他包含進去了。
接着咱們來看配置文件:

var config = {
    entry:{app:path.resolve(__dirname,'src/main.js'),
            vendor: ["./src/js/common"]},//【1】注意這裏
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js"
    },
    module:{
        loaders:[{
            test:/\.css$/,
            loader:'style!css',
            exclude:nodemodulesPath
        }
        ]
    },
    plugins:[
        new webpack.optimize.UglifyJsPlugin({
             compress: {
                warnings: false
             }
        }),
        //【2】注意這裏  這兩個地方市用來配置common.js模塊單獨打包的
        new webpack.optimize.CommonsChunkPlugin({
            name: "vendor",//和上面配置的入口對應
            filename: "vendor.js"//導出的文件的名稱
        })
    ]
}

目錄結構如今是這樣的:

webpack
  |---index.html
  |---webpack-config.js
  |---src
     |---main.js
     |---js
          |---a.js    //a裏面require了common
          |---common.js

執行webpack會生成app.js和common.js兩個文件.

code split(模塊分離,按需加載)

有些場景,咱們可能但願模塊在須要的時候再加載,而不是一古腦兒打包到一塊兒。從而加速首屏的加載速度。舉個例子,在作單頁應用的時候,每一個場景對應一個模塊。若是場景不少,把模塊打包到一塊兒,最後的bundle文件必然很臃腫,加載很慢。那麼只要在每一個場景須要展現的時候,再加載對應的js模塊。就能夠優化這個問題了。webpack支持模塊按需加載,這個功能叫code split。下面介紹怎麼使用這個功能。

目錄結構:

webpack
  |---index.html
  |---webpack-config.js
  |---src
     |---main.js
     |---js
          |---codeSplit.js

codeSplit.js:

//就是普通的模塊 沒什麼特殊的
console.log('code split');

module.exports = {
    name:'cplll'
}

main.js:

var cp = function(resolve){
     require.ensure(['./js/codeSplit.js'],function(){//注意這裏哦,就是用require.ensure來按需加載的,這是webpack特有的
        resolve(require('./js/codeSplit.js'));//加載好 require模塊
    });
}

var getModule = function(){
    return new Promise((resolve,reject)=>{
        cp(resolve);
    });
}

getModule().then((cl)=>{
    console.log(cl.name);
});

config配置:

//...省略
var buildPath = path.resolve(__dirname,"build");
var config = {    
    entry:{
        m1:path.resolve(__dirname,'src/main.js')
    },//注意在這裏添加文件的入口
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"app.js",
        publicPath:'build/' //注意這裏哦,分離出來的模塊會按這個路徑來加載
    }    
}

執行命令:

webpack --config webpack-config.js --colors

生成結果:

webpack
  |---index.html
  |---webpack-config.js
  |---build //生成結果
  |    |---app.js
  |    |---1.app.js
  |---src
     |---main.js
     |---js
          |---codeSplit.js

頁面裏引用

<script type="text/javascript" src="./build/app.js"></script>

打開頁面就是發現,app.js和1.app.js(在cp函數調用的時候加載)分開加載了。
圖片描述

最後須要特別注意,配置output裏的publicPath。這裏容易有坑。由於不配置加載路徑是這樣的:

http://localhost:9527/1.app.js

配置之後(publicPath:'build/'):

http://localhost:9527/build/1.app.js

多個入口

config配置:

var config = {    
    entry:{
        m1:path.resolve(__dirname,'src/main.js'),
         m2:path.resolve(__dirname,'src/main1.js')
    },//注意在這裏添加文件的入口
    resolve:{
        extentions:["","js"]
    },
    output:{
        path:buildPath,
        filename:"[name].js"//注意這裏使用了name變量
    }    
}

webpack-dev-server

在開發的過程當中個,咱們確定不但願,每次修改完都手動執行webpack命令來調試程序。因此咱們能夠用webpack-dev-server這個模塊來取代煩人的執行命令。它會監聽文件,在文件修改後,自動編譯、刷新瀏覽器的頁面。另外,編譯的結果是保存在內存中的,而不是實體的文件,因此是看不到的,由於這樣會編譯的更快。它就想到與一個輕量的express服務器。
安裝:

npm install webpack-dev-server --save -dev

config配置:

var config = {
    entry:path.resolve(__dirname,'src/main.js'),
    resolve:{
        extentions:["","js"]
    },
    //Server Configuration options
    devServer:{
        contentBase: '',  //靜態資源的目錄 相對路徑,相對於當前路徑 默認爲當前config所在的目錄
        devtool: 'eval',
        hot: true,        //自動刷新
        inline: true,    
        port: 3005        
    },
    devtool: 'eval',
    output:{
        path:buildPath,
        filename:"app.js"
    },
    plugins: [
        new webpack.HotModuleReplacementPlugin(),//開啓熱替換插件
        new webpack.NoErrorsPlugin()
    ]
}

個人目錄結構:

webpack
  |---index.html
  |---webpack-config.js//我把靜態資源目錄配置在了這裏
  |---src
     |---main.js
     |---js
          |---a.js
          |---common.js

執行命令:

webpack-dev-server --config webpack-dev-config.js  --inline --colors

默認訪問地址:

http://localhost:3000/index.html(根據配置會不同)

有一點須要聲明,在index.html(引用導出結果的html文件)裏直接引用「app.js」,不要加父級目錄,由於此時app.js在內存裏與output配置的目錄無關:

<script type="text/javascript" src="app.js"></script>

詳細文檔在這裏查看:http://webpack.github.io/docs/webpack-dev-server.html

Hot Module Replacement

熱替換是指在應用運行時候替換、添加、移除某個模塊而不須要所有模塊從新編譯、整個頁面從新加載。在web應用變的愈來愈複雜的今天,webpack的編譯速度會愈來愈慢。使用熱替換能大大提升webpack的編譯速度,提高開發效率。下面介紹如何基於webpack-dev-server配置熱替換。

config配置:

var config = {
    entry:[
        'webpack/hot/dev-server',//注意點1:熱替換配置點1
        path.resolve(__dirname,'src/main1.js')
    ],
    // entry:{m1:path.resolve(__dirname,'src/main.js'),
    //     m2:path.resolve(__dirname,'src/main1.js')},
    resolve:{
        extentions:["","js"]
    },
    // target: 'node',
    //Server Configuration options
    devServer:{
        contentBase: '',  //Relative directory for base of server
        devtool: 'eval',
        hot: true,        //注意點2:熱替換配置點2
        inline: true,
        port: 3005        //Port Number
    },
    devtool: 'eval',
    output:{
        path:buildPath,
        filename:"app.js"
    },
    plugins: [
        //Enables Hot Modules Replacement
        new webpack.HotModuleReplacementPlugin(),//注意點3:熱替換配置點3
        //Allows error warnings but does not stop compiling. Will remove when eslint is added
        new webpack.NoErrorsPlugin()
    ],
}

配置文件裏添加3個配置點

  1. entry 節點裏添加 'webpack/hot/dev-server'

  2. devServer節點裏添加 hot: true

  3. plugins 節點裏 new webpack.HotModuleReplacementPlugin()

這樣配置文件就配置好了。接下來在代碼文件裏添加熱替換要監聽的模塊。代碼以下:

var h1 = require('./hot1');
if(module.hot){//判斷是否開啓了熱替換
    module.hot.accept('./hot1',function(){//在hot1模塊更新時,執行替換
        h1 = require('./hot1');
    });
}

更多信息參考:webpack-dev-server和熱替換介紹

如何區分開發及生產環境

在webpack.config.js使用process.env.NODE_ENV進行判斷
在package.json裏面的script設置環境變量,注意mac與windows的設置方式不同

"scripts": {
    "publish-mac": "export NODE_ENV=prod&&webpack -p --progress --colors",
    "publish-win":  "set NODE_ENV=prod&&webpack -p --progress --colors",
    "dev-mac": "export NODE_ENV=dev&&webpack-dev-server",
    "dev-win":  "set NODE_ENV=dev&&webpack-dev-server
}

config配置:

//其餘代碼省略...
var NODE_ENV = process.env.NODE_ENV;

var config = {
    //入口文件配置
    entry: path.resolve(__dirname, 'src/main.js'),
    resolve: {
        extentions: ["", "js"]//當requrie的模塊找不到時,添加這些後綴
    },
    devtool: 'eval',
    //文件導出的配置
    output: {
        path: buildPath,
        filename: "app.js"
    },
    
    module: {
        loaders: [
            {
                test: /\.html$/, 
                loader: 'tmodjs',//對匹配的文件進行處理的loader 
                exclude: [nodemodulesPath]//排除node module中的文件
            }
        ]
    }
}

if(NODE_ENV === "prod"){//判斷是生產環境執行生產配置
    delete config.devtool;
    config.plugins = [
    //壓縮打包的文件
    new webpack.optimize.UglifyJsPlugin({
      compress: {
        //supresses warnings, usually from module minification
        warnings: false
      }
    })];
}

以後dev環境執行命令:npm run dev-win
生產環境執行命令:npm run publish-win

經常使用plugins

  • 代碼熱替換, HotModuleReplacementPlugin

  • 將css成生文件,而非內聯,ExtractTextPlugin

  • 報錯但不退出webpack進程,NoErrorsPlugin

  • 多個 html共用一個js文件(chunk),可用CommonsChunkPlugin

  • 清理文件夾,Clean

  • 調用模塊的別名ProvidePlugin,例如想在js中用$,若是經過webpack加載,須要將$與jQuery對應起來

參考文章

webpack使用優化

相關文章
相關標籤/搜索