webpack進階構建專案(一)

龍恩0707發表於2016-06-11

webpack進階構建專案(一)

閱讀目錄

1.理解webpack載入器

webpack的設計理念,所有資源都是“模組”,webpack內部實現了一套資源載入機制,這與Requirejs、Sea.js、Browserify等實現有所不同.

Webpack提供了一套載入器,比如css-loader,less-loader,style-loader,url-loader等,用於將不同的檔案載入到js檔案中,比如url-loader用於在js中載入png/jpg格式的圖片檔案,css/style loader用於載入css檔案,less-loader載入器是將less編譯成css檔案;比如程式碼配置如下:

module.exports = {
  entry: "./src/main.js",
  output: {
    filename: "build.js",
    path: __dirname + '/assets/',
    publicPath: "/assets/"
  },
  module: {
    loaders: [
      {test: /.css$/, loader: 'style!css'},
      {test: /.(png|jpg)$/, loader: 'url-loader?limit=8192'}
    ]
  }
  resolve: {
    extensions: ['', '.js', '.jsx'],
    //模組別名定義,方便後續直接引用別名,無須多寫長長的地址
    alias: {
        a : 'js/assets/a.js',  // 後面直接引用 require(“a”)即可引用到模組
        b : 'js/assets/b.js',
        c : 'js/assets/c.js'
    }
  },
  plugins: [commonsPlugin, new ExtractTextPlugin("[name].css")]
}

module.loader: 其中test是正規表示式,對符合的檔名使用相應的載入器./.css$/會匹配 xx.css檔案,但是並不適用於xx.sass或者xx.css.zip檔案.
url-loader: 它會將樣式中引用到的圖片轉為模組來處理; 配置資訊的引數“?limit=8192”表示將所有小於8kb的圖片都轉為base64形式。
entry: 模組的入口檔案。依賴項陣列中所有的檔案會按順序打包,每個檔案進行依賴的遞迴查詢,直到所有模組都被打成包;
output:模組的輸出檔案,其中有如下引數:
filename: 打包後的檔名
path: 打包檔案存放的絕對路徑。
publicPath: 網站執行時的訪問路徑。
relolve.extensions: 自動擴充套件檔案的字尾名,比如我們在require模組的時候,可以不用寫字尾名的。
relolve.alias: 模組別名定義,方便後續直接引用別名,無須多寫長長的地址
plugins 是外掛項;

2.html-webpack-plugin學習

首先來看看專案的目錄結構如下:

package.json 如下:

{
  "name": "html-webpack-plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "html-webpack-plugin": "^2.19.0",
    "webpack": "^1.13.1"
  }
}

執行命令 npm install 把依賴包載入出來;

接著在 webpack.config.js配置如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
console.log(SRC_PATH)
module.exports = {
  
  entry: SRC_PATH + "/js/index.js",
  //輸出的檔名 合併以後的js會命名為index.js
  output: {
    path: BUILD_PATH,
    filename: 'index.js'
  },
  //新增我們的外掛 會自動生成一個html檔案
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app'
    })
  ]
};

在專案中的根目錄下 執行 webpack 就能生成buid資料夾了,裡面會自動生成 兩個檔案 index.html和index.js檔案;
index.html程式碼如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World app</title>
  </head>
  <body>
  <script type="text/javascript" src="index.js"></script></body>
</html>

標題title就是我們配置上的;
且合併了依賴的js檔案;我們可以直接在本地訪問index.html 可以看到能列印出依賴的檔案js程式碼了;可以看到可以解決依賴的問題;
html-webpack-plugin 還支援如下配置:
title: 用於生成的HTML檔案的標題。
filename: 用於生成的HTML檔案的名稱,預設是index.html。你可以在這裡指定子目錄。
template: 模板檔案路徑,支援載入器,比如 html!./index.html
inject: true | 'head' | 'body' | false ,注入所有的資源到特定的 template 或者 templateContent 中,如果設定為 true
或者 body,所有的 javascript 資源將被放置到 body 元素的底部,'head' 將放置到 head 元素中。
favicon: 新增特定的 favicon 路徑到輸出的 HTML 檔案中。
minify:{ //壓縮HTML檔案
removeComments:true, //移除HTML中的註釋
collapseWhitespace:true //刪除空白符與換行符
}
hash: true | false, 如果為 true, 將新增一個唯一的 webpack 編譯 hash 到所有包含的指令碼和 CSS 檔案,對於解除 cache 很有用。
cache: true | false,如果為 true, 這是預設值,僅僅在檔案修改之後才會釋出檔案。
showErrors: true | false, 如果為 true, 這是預設值,錯誤資訊會寫入到 HTML 頁面中
chunks: 允許只新增某些塊 (比如,僅僅 unit test 塊)
chunksSortMode: 允許控制塊在新增到頁面之前的排序方式,支援的值:'none' | 'default' | {function}-default:'auto'
excludeChunks: 允許跳過某些塊,(比如,跳過單元測試的塊)

比如我現在webpack.config.js配置改為如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
console.log(SRC_PATH)
module.exports = {
  
  entry: SRC_PATH + "/js/index.js",
  //輸出的檔名 合併以後的js會命名為index.js
  output: {
    path: BUILD_PATH,
    filename: 'index.js'
  },
  //新增我們的外掛 會自動生成一個html檔案
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: '1.0.0/home.html',
      inject: true,
      hash: true
    })
  ]
};

然後再在命令列中繼續執行webpack命令,可以看到在build下會生成2個目錄 第一個是build/1.0.1/home.html; 第二個是 build/index.js
再來看下home.html程式碼如下:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>Hello World app</title>
  </head>
  <body>
  <script type="text/javascript" src="../index.js?d03211ff5e0251af224d"></script></body>
</html>

可以看到設定 hash為true js的字尾會自動加一個hash編碼,對於頁面解決快取很有用;

生成多個 HTML 檔案
通過在配置檔案中新增多次這個外掛,來生成多個 HTML 檔案。
webpack.config.js程式碼如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
console.log(SRC_PATH)
module.exports = {
  
  entry: SRC_PATH + "/js/index.js",
  //輸出的檔名 合併以後的js會命名為index.js
  output: {
    path: BUILD_PATH,
    filename: 'index.js'
  },
  //新增我們的外掛 會自動生成一個html檔案
  plugins: [
    new HtmlwebpackPlugin(),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'app1/home.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    })
  ]
};

官網可以看這裡 https://www.npmjs.com/package/html-webpack-plugin
也可以在配置項 加上minify選項 壓縮HTML檔案;程式碼如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
console.log(SRC_PATH)
module.exports = {
  
  entry: SRC_PATH + "/js/index.js",
  //輸出的檔名 合併以後的js會命名為index.js
  output: {
    path: BUILD_PATH,
    filename: 'index.js'
  },
  //新增我們的外掛 會自動生成一個html檔案
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      minify:{ //壓縮HTML檔案
        removeComments:true,    //移除HTML中的註釋
        collapseWhitespace:true    //刪除空白符與換行符
      }
    })
  ]
};

檢視html生成後的檔案可以看到已經被壓縮了;

3.壓縮js與css

webpack已經內嵌了uglifyJS來完成對JS與CSS的壓縮混淆,無需引用額外的外掛。
壓縮程式碼如下:

new webpack.optimize.UglifyJsPlugin({    //壓縮程式碼
   compress: {
       warnings: false
   },
   except: ['$super', '$', 'exports', 'require']    //排除關鍵字
})

這裡需要注意的是壓縮的時候需要排除一些關鍵字,不能混淆,比如$或者require,如果混淆的話就會影響到程式碼的正常執行。
webpack.config.js程式碼改為如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var webpack = require("webpack");
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');
console.log(SRC_PATH)
module.exports = {
  
  entry: {
    'index' : SRC_PATH + "/js/index.js"
  },
  //輸出的檔名 合併以後的js會命名為index.js
  output: {
    path: BUILD_PATH + '/js/',
    filename: '[name].js'
  },
  //新增我們的外掛 會自動生成一個html檔案
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      minify:{ //壓縮HTML檔案
        removeComments:true,    //移除HTML中的註釋
        collapseWhitespace:true    //刪除空白符與換行符
      }
    }),
    new webpack.optimize.UglifyJsPlugin({    //壓縮程式碼
       compress: {
           warnings: false
       },
       except: ['$super', '$', 'exports', 'require']    //排除關鍵字
    })
  ]
};

繼續執行下webpack可以看到js已經被壓縮了;注意:但是貌似對es6的語法不能壓縮~

4.理解less-loader載入器的使用

 我們先來理解下less-loader載入器,其他的sass-loader也是一個意思,less-loader載入器是把css程式碼轉化到style標籤內,

動態插入到head標籤內;我們先來看看我專案的結構如下:

src/html/index.html程式碼如下:

<!DOCTYPE html>
 <html>
    <head>
        <meta http-equiv="content-type" content="text/html;charset=utf-8" />
        <meta content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no" name="viewport"/>
        <meta content="yes" name="apple-mobile-web-app-capable" />
        <meta content="black" name="apple-mobile-web-app-status-bar-style" />
        <meta content="telephone=no" name="format-detection" />
        <meta content="email=no" name="format-detection" />
        <meta name="description" content="基於webpack的前端工程化開發解決方案探索"/>
        <title>動態生成html的實踐</title>
    </head>
    <body>
        <div>hello webpack</div>
    </body>
</html>

現在我想通過html-webpack-plugin外掛動態生成 html頁面及引入index.js 和 生成 index.js檔案;

webpack.config.js程式碼配置如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {
  entry: SRC_PATH + "/js/index.js",
  output: {
    filename: "build.js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: "style!css!less"
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    })
  ]
};

在專案的根目錄執行webpack,即可動態生成html檔案和js檔案,開啟生成後的index.html即可看到css生效了,且css被動態內鏈到head標籤內了;

其中less/main.less 檔案如下程式碼:

@color: red;

body {
    background:@color;
}
如上可以看到less檔案得到編譯,且動態插入到head標籤內;

5.理解babel-loader載入器

babel-loader載入器能將ES6的程式碼轉換成ES5程式碼,我們需要安裝babel-loader
執行命令:npm install babel-loader --save-dev
因此現在需要在webpack.config.js 加入babel-loader的載入器即可;如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {
  entry: SRC_PATH + "/js/index.js",
  output: {
    filename: "build.js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: "style!css!less"
      },
      {
        test: /\.js$/, 
        loader: 'babel'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    })
  ]
};

a.js 假如是ES6的語法;比如如下一句程式碼:
// es6的語法
let LOADER = true;
module.exports = LOADER;

現在在index.js程式碼如下:
var aModule = require('../less/main.less');
console.log(aModule);

// es6的語法
var aMoudle = require('./a');
console.log(aMoudle);

可以看到列印 aMoudle的值為true;說明可以正確的解析了;

6.理解 extract-text-webpack-plugin(獨立打包樣式檔案)

執行安裝命令:
sudo npm install extract-text-webpack-plugin
然後再webpack.config.js 加入載入器配置項如下程式碼:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {
  entry: SRC_PATH + "/js/index.js",
  output: {
    filename: "build.js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    })
  ]
};

在專案的根目錄執行 webpack 即可生效;會在build目錄下 生成 index.css檔案,且在打包後的index.html會自動引入link標籤的css;
如下所示:

如果頁面上有多個less檔案或者css檔案的話,也可以通過 @import 動態匯入;如下在main.less 引入 a.less程式碼如下:
@import './a.less';
a.less 是和main.less 同級目錄下的;

7.webpack打包多個資原始檔

 我們在開發頁面的時候,有時候需要有多個入口檔案,做到檔案是按需載入,這樣就可以使用快取提升效能;

只需要像如下編碼即可:

module.exports = {
  entry: {
     "main": "./src/a.js",
     "index": "./src/index.js"
  },
  output: {
    filename: "[name].js"
  }
};

webpack.config.js程式碼如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require("webpack");
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {
  entry: {
     "a": SRC_PATH + "/js/a.js",
     "index": SRC_PATH + "/js/index.js",
  },
  output: {
    filename: "[name].js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    }),
    new webpack.optimize.UglifyJsPlugin({    //壓縮程式碼
       compress: {
           warnings: false
       },
       except: ['$super', '$', 'exports', 'require']    //排除關鍵字
    })
  ]
};

8.webpack對圖片的打包

 圖片是 url-loader來載入的,我們既可以在css檔案裡url的屬性;

首先先安裝 url-loader外掛;
sudo npm install --save-dev url-loader

首先在less檔案裡面加入如下程式碼:
@color: red;
body {
background:@color;
background:url('../images/1.png') no-repeat;
}
在index.js裡面加入如下程式碼:
var aModule = require('../less/main.less');
console.log(aModule);

在webpack.config.js程式碼配置加入如下:
{
test: /.(png|jpg)$/,
loader: 'url?limit=8192'
}
webpack.config.js所有程式碼如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require("webpack");
//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {
  
  entry: {
     "a": SRC_PATH + "/js/a.js",
     "index": SRC_PATH + "/js/index.js",
  },
  output: {
    filename: "[name].js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      },
      {
        test: /.(png|jpg)$/, 
        loader: 'url?limit=8192&name=img/[hash:8].[name].[ext]'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    }),
    new webpack.optimize.UglifyJsPlugin({    //壓縮程式碼
       compress: {
           warnings: false
       },
       except: ['$super', '$', 'exports', 'require']    //排除關鍵字
    })
  ]
};

因此在專案的根目錄執行webpack後,即可,然後會生成index.css檔案程式碼如下:
body{background:red;background:url(8eaebaa98ed1fe64bbf9f0f954b2b230.png) no-repeat}
因此可以看到會動態轉換成base64編碼;

9.學習web-dev-server 建立伺服器及動態監聽css及js檔案的改變;

 在webpack中,我們經常使用webpack-dev-server作為開發伺服器,用於實時監聽和打包編譯靜態資源,這樣每當我們修改js、css等等檔案時,客戶端(如瀏覽器等)能夠自動重新整理頁面,展示實時的頁面效果。

webpack-dev-server只監聽webpack.config.js中entry入口下檔案(如js、css等等)的變動,

只有這些檔案的變動才會觸發實時編譯打包與頁面重新整理,但是html檔案更改後儲存不能監聽新內容到,但是對於開發影響不大,我們在編寫css檔案
或者js檔案的時候儲存後,會自動重新整理頁面,所以html頁面也會自動更新到;

首先需要進入我們的專案的根目錄下需要安裝webpack-dev-server 安裝命令如下:
sudo npm install --save-dev webpack-dev-server

首先我專案的目錄如下:

安裝完成後,需要在webpack.config.js檔案配置下;如下程式碼:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
var webpack = require("webpack");

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

module.exports = {

  entry: {
     "a": SRC_PATH + "/js/a.js",
     "index": SRC_PATH + "/js/index.js"
  },
 
  output: {
    filename: "/js/[name].js",
    path: BUILD_PATH
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      },
      {
        test: /.(png|jpg)$/, 
        loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("/css/index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'html/index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    }),
    new webpack.optimize.UglifyJsPlugin({    //壓縮程式碼
       compress: {
           warnings: false
       },
       except: ['$super', '$', 'exports', 'require']    //排除關鍵字
    })
  ]
};

接著需要建立一個webpack-config-dev.js檔案,該檔案的作用是建立本地伺服器,及實時監聽css及js檔案的改變;程式碼如下:

var path = require('path')
var webpack = require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  devtool: 'cheap-eval-source-map',
  entry: [
    'webpack-dev-server/client?http://127.0.0.1:8080',
    'webpack/hot/dev-server',
    './src/js/index',
    './src/js/a',
    './src/less/main.less'
  ],
  output: {
    path: path.join(__dirname, 'build'),
    filename: '/js/[name].js'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'html/index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    }),
    new ExtractTextPlugin("index.css")
  ],
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      },
      {
        test: /.(png|jpg)$/, 
        loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]'
      }
    ]
  },
  devServer: {
    contentBase: './dist',
    hot: true
  }
}

上面只是在開發環境配置的;我們還需要一個線上環境,進行打包,我們還需要使用一個線上環境打包的配置;我們可以新建一個叫webpack.config.prod.js檔案; 該檔案的配置用於在生產環境的打包;配置程式碼如下:

var path = require('path')
var webpack = require('webpack');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

module.exports = {
  devtool: 'source-map',
  entry: ['./src/js/index','./src/js/a'],
  output: {
    path: path.join(__dirname, 'build'),
    filename: '/js/[name].js'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'html/index.html',
      template: 'src/html/index.html',
      inject: true,
      hash: true
    }),
    new ExtractTextPlugin("index.css")
  ],
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      },
      {
        test: /.(png|jpg)$/, 
        loader: 'url?limit=8192&name=images/[hash:8].[name].[ext]'
      }
    ]
  }
}

package.json檔案程式碼如下:

{
  "name": "html-webpack-plugin",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "build": "webpack --config webpack.config.prod.js",
    "dev": "webpack-dev-server --config webpack.config.dev.js"
  },
  "author": "",
  "license": "ISC",
  "devDependencies": {
    "babel-core": "^6.9.1",
    "babel-loader": "^6.2.4",
    "css-loader": "^0.23.1",
    "file-loader": "^0.8.5",
    "html-webpack-plugin": "^2.19.0",
    "http-server": "^0.9.0",
    "less": "^2.7.1",
    "less-loader": "^2.2.3",
    "react-hot-loader": "^1.3.0",
    "style-loader": "^0.13.1",
    "url-loader": "^0.5.7",
    "webpack": "^1.13.1",
    "webpack-dev-server": "^1.14.1"
  }
}

接著我們先執行webpack,就可以在專案的根目錄下生成build資料夾了;
如下圖所示:

為了更方便執行我們在package.json新增如下程式碼:
"scripts": {
     "build": "webpack --config webpack.config.prod.js",
     "dev": "webpack-dev-server --config webpack.config.dev.js"
}
因此如果在開發環境的話,可以執行 npm run dec; 如果是線上的環境,可以執行 npm run build 即可;
但是當我們執行 npm run dev的時候 會出現如下錯誤:

這是因為 預設centos的 hosts 會把本地 127.0.0.1 localhost 註釋掉; 我們可以在我們hosts資料夾下 多加一句
127.0.0.1 localhost
即可;
我們可以參考 https://github.com/Unitech/pm2/issues/324

webpack-dev-server git上的地址: https://github.com/tugenhua0707/webpack-dev-server 

10.assets-webpack-plugin外掛解決html檔案的版本號的問題;

 我們上面學習過 html-webpack-plugin 這個外掛,它可以自動新增版本號,但是對於很多前端開發的時候,我們的html頁面是放在伺服器端那邊的部署,架構是前後端分離,因為html是在後臺的,所以根本操作不了html,也不應該耦合。 

我們可以通過webpack的 assets-webpack-plugin 外掛生成一個記錄了版本號的檔案;詳細的可以看官網地址是: https://www.npmjs.com/package/assets-webpack-plugin;
首先我們需要在我們的專案下安裝該外掛:安裝命令如下:
npm install assets-webpack-plugin --save-dev
只需要在webpack.config.json新增如下程式碼:

// 部分程式碼
var AssetsPlugin = require('assets-webpack-plugin');
new AssetsPlugin({
    filename: 'build/webpack.assets.js',
    processOutput: function (assets) {
        return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets);
    }
})

我們現在來在webpack.config.js配置項如下

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

var AssetsPlugin = require('assets-webpack-plugin');

module.exports = {
  entry: {
     "a": SRC_PATH + "/js/a.js",
     "index": SRC_PATH + "/js/index.js"
  },
  output: {
    path: path.join(__dirname, "build"),
    filename: "js/[name]-[chunkhash:8].js",
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("css/index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'html/index.html',
      template: 'src/html/index.html',
      inject: true
    }),
    new AssetsPlugin({
        filename: 'build/webpack.assets.js',
        processOutput: function (assets) {
            return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets);
        }
    })
  ]
};

然後執行webpack命令進行打包後 如下圖所示:

可以看到在我們的build目錄下的js檔案生成a和index帶有版本號的js檔案,在build目錄下還生成了一個 webpack.assets.js檔案;該檔案
程式碼如下:
window.WEBPACK_ASSETS = {"a":{"js":"js/a-a7cbb4daad866656445a.js"},
"index":{"js":"js/index-a7cbb4daad866656445a.js"}}

因此我們可以把該webpack.assets.js檔案讓開發在頁面上引入即可;
<script src="build/webpack.assets.js?v=' + Math.random() + '"></script>
<script src="' + window.WEBPACK_ASSETS['a'].js + '"></script>
<script src="' + window.WEBPACK_ASSETS['index'].js + '"></script>

我們繼續看 chunkhash:8 的含義:其中8是指hash長度為8,預設是16。加上chunkhash就可以快取檔案;因為每次打包的時候都會自動生成
版本號,但是有些檔案並沒有修改的話,我們不需要更改版本號,我們想直接從快取裡面讀取,對於更改的檔案我們需要從伺服器下載,對於
這樣的 chunkhash 可以解決;比如如下兩次的版本號:
window.WEBPACK_ASSETS = {"a":{"js":"js/a-ac4c0d24.js"},"index":{"js":"js/index-7c5ec642.js"}}
當我更改index.js程式碼的時候 a.js程式碼沒有更改的話;變成如下:
window.WEBPACK_ASSETS = {"a":{"js":"js/a-ac4c0d24.js"},"index":{"js":"js/index-9a2fec25.js"}}

11.webpack關於同步載入和非同步載入的問題

 使用webpack打包,直接使用require模組可以解決模組的依賴的問題,

  對於直接require模組,WebPack的做法是把依賴的檔案都打包在一起,造成檔案很臃腫。即是同步載入,同步的程式碼會被合成並且打包在一起;
而 非同步載入的程式碼會被分片成一個個chunk,在需要該模組時再載入,即按需載入,同步載入過多程式碼會造成檔案過大影響載入速度;
非同步過多則檔案太碎,造成過多的Http請求,同樣影響載入速度。這要看開發者自己權衡下;
同步載入的寫法如下:
var aModule =
require('./a');
非同步載入的寫法如下:
require.ensure(['./a'],function(require){
     var aModule = require('./a');
},'tips');
使用 require.ensure 可以解決非同步載入模組的檔案;如上程式碼,如果ensure不指定第三個引數的話(tips),那麼webpack會隨機生成一個數字
作為模組名,我們指定第三個引數為tips;那說明生成後的模組名叫tips.js;
這時候我們需要使用到webpack.config.js中的output選項需要加一個配置項:chunkFilename: "[name].min.js"

下面可以先來理解下 webpack中的output.filename 和output.chunkFilename
比如如下配置程式碼:

{
  entry: {
    "index": "pages/index.jsx"
  },
  output: {
    filename: "[name].min.js",
    chunkFilename: "[name].min.js"
  }
}

filename應該比較好理解,就是對應於entry裡面生成出來的檔名;即會生成 index.min.js檔名;
chunkname的理解是在按需載入(非同步)模組的時候,這樣的檔案是沒有被列在entry中的,如使用CommonJS的方式非同步載入模組時候會使用到;
比如如上這要非同步載入一個a.js模組,程式碼如下:
require.ensure(['./a'],function(require){
    var aModule = require('./a');
},'tips');
指定第三個引數為tips,因此配合output中的 chunkFilename, 即會生成 tips.min.js了;
比如如下所示:

tips.js程式碼如下:

webpackJsonp([1],[
/* 0 */,
/* 1 */
/***/ function(module, exports) {

  /*
  // es6的語法 
  let LOADER = true; 
  module.exports = LOADER;
  */

  function a() {
    console.log("a");
    console.log(11);
    console.log(222444);
  }
  a();

/***/ }
]);

會通過 webpackJsonp 模組包裝一下;接著我們在index.js程式碼看下,它使如何被呼叫的;如下片段程式碼:
__webpack_require__.e/* nsure */(1, function (require) {
     var aModule = __webpack_require__(1);
});
即可引用的到;也就是說如果a.js模組有1000+行程式碼,不會被包含到index.js程式碼內,但是index.js程式碼有a.js程式碼模組的引用;
可以載入到a.js程式碼的內容;
下面是我的webpack.config.js程式碼配置如下:

var path = require('path');
var HtmlwebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');

//定義了一些資料夾的路徑
var ROOT_PATH = path.resolve(__dirname);

var SRC_PATH = path.resolve(ROOT_PATH, 'src');

var BUILD_PATH = path.resolve(ROOT_PATH, 'build');

var AssetsPlugin = require('assets-webpack-plugin');

module.exports = {
  entry: {
     "index": SRC_PATH + "/js/index.js"
  },
  output: {
    path: path.join(__dirname, "build"),
    filename: "js/[name]-[chunkhash:8].js",
    chunkFilename: "js/[name]-[chunkhash:8].js"
  },
  module: {
    loaders: [
      //.css 檔案使用 style-loader 和 css-loader 來處理
      {
        test: /\.less$/,
        loader: ExtractTextPlugin.extract(
            'css?sourceMap!' +
            'less?sourceMap'
        )
      },

      {
        test: /\.js$/, 
        loader: 'babel'
      }
    ]
  },
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  plugins: [
    // 內聯css提取到單獨的styles的css
    new ExtractTextPlugin("css/index.css"),
    new HtmlwebpackPlugin({
      title: 'Hello World app',
      filename: 'html/index.html',
      template: 'src/html/index.html',
      inject: true
    }),
    new AssetsPlugin({
        filename: 'build/webpack.assets.js',
        processOutput: function (assets) {
            return 'window.WEBPACK_ASSETS = ' + JSON.stringify(assets);
        }
    })
  ]
};

index.js程式碼非同步呼叫a.js模組的程式碼如下:
require.ensure(['./a'],function(require){
    var aModule = require('./a');
},'tips');
更多的配置項 看官網 http://webpack.github.io/docs/code-splitting.html

相關文章