webpack 多頁應用架構系列實戰

龍恩0707發表於2017-07-16

閱讀目錄

1.webpack配置瞭解

 webpack的配置檔案是一個nodejs的module,使用CommonJS風格來編寫的,比如如下:

module.exports = {
  entry: './index',
  output: {
    path: __dirname + '/dist',
    filename: 'bundle.js'
  }
}

webpack的配置檔案可以隨便命名,預設為 webpack.config.js,因此在專案的根目錄下,直接執行 webpack就可以進行打包,但是也可以對webpack命名為其他的名字,比如把它放入 build/webpack.dev.js ,程式碼目錄結構如下:

### 目錄結構如下:
demo                                        # 工程名
|   |--- dist                               # 打包後生成的目錄檔案             
|   |--- node_modules                       # 所有的依賴包
|   |--- src                                # 專案的檔案包
|   |    |--- pages                         # 存放所有頁面的檔案
|   |    |    |--- page1
|   |    |    |    |--- index.html          # 第一個頁面的html檔案
|   |    |    |    |--- index.styl          # 第一個頁面的css檔案
|   |    |    |    |--- index.js            # 第一個頁面的js檔案
|   |    |    |--- page2
|   |    |    |    |--- index.html          # 第二個頁面的html檔案
|   |    |    |    |--- index.styl          # 第二個頁面的css檔案
|   |    |    |    |--- index.js            # 第二個頁面的js檔案
|   |--- build
|   |    |--- webpack.base.js               # webpack 基本配置檔案
|   |    |--- webpack.dev.js                # 開發檔案
|   |    |--- webpack.build.js              # 打包線上檔案                               
|   |--- .gitignore  
|   |--- README.md
|   |--- package.json 

因此我們在package.json 配置檔案如下:

"scripts": {
  "dev": "node build/webpack.dev.js",
  "build": "node build/webpack.build.js"
}

進入專案的根目錄後,執行 npm run dev 即可進行打包。

1.1) 入口檔案配置 entry引數

   entry入口檔案可以是字串的單入口檔案,也可以是陣列的多入口檔案,但是我們最常見的是一個物件的方式來組織入口檔案。因此object中的key在webpack裡相當於入口的name,可以用來生成檔案的路徑,也可以使用來為此入口唯一的標識。
比如如下:

entry: {
  'page1': path.resolve(__dirname, '../src/pages/page1'),
  'page2': path.resolve(__dirname, '../src/pages/page2')
}

假如頁面上有多個入口的話,這樣一個個寫比較麻煩,因此可以寫一個函式如下:

/*
 獲取專案中多個入口檔案
*/
function getEntries(paths) {
  // node 中同步獲取檔案列表
  var files = glob.sync(paths),
    entries = {};

  files.forEach(function(filepath) {
    var toArray = filepath.split('/');
    var filename = toArray[toArray.length - 2];
    entries[filename] = filepath;
  });
  return entries;
}

var entries = getEntries('./src/pages/*/index.js');
Object.keys(entries).forEach(function(name) {
  entry[name] = entries[name]
});

1.2) 輸出檔案:out引數

output引數是告訴webpack以什麼方式來生成/輸出檔案,output有幾個常用的引數如:path, publicPath, filename, chunkFilename, 如下程式碼:

output: {
  path: path.resolve(__dirname, '../dist'),
  publicPath: '/assets/',   // 供外掛在生產模式下更新內嵌到css、html檔案裡的相對路徑url值
  filename: 'static/js/[name].js',
  chunkFilename: '[id].bundle.js',
}

1.3) path引數

   path參數列示生成檔案的根目錄,需要傳入一個絕對路徑,如:path.resolve(__dirname, '../dist'),會解析成 /專案的根目錄下/dist檔案, path引數和filename引數會共同組成入口檔案的完整路徑。

1.4) publicPath

該參數列示的是一個URL路徑(指向生成檔案的根目錄),可以用於css/js/images/font檔案等資源的路徑,可以確保網頁正確的載入到這些資原始檔。

1.5) publicPath引數 和 path引數的區別:

   path引數是針對本地檔案系統的,但是publicPath則針對的是瀏覽器,它既可以是一個相對路徑,比如 '../../dist', 也可以是一個絕對路徑,比如:'http://www.xxx.com/', 那麼什麼時候使用相對路徑呢?什麼時候使用絕對路徑呢?如果是引用本專案下的檔案,最好使用相對路徑,如果是引用跨專案的檔案,需要使用絕對路徑。

1.6) filename

filename屬性表示的是如何命名生成的入口檔案,可以有如下規則:
  1. [name], 指代入口檔案的name,也就是上面的entry中的key。
  2. [hash] 指代本次編譯的一個hash版本,但是請注意,只要在同一次編譯過程中生成的檔案,這個[hash]值就是一樣的,每一次編譯,hash值都是一樣,也就是說不存在快取檔案,只要一編譯所有的hash
都會改變。
  3. [chunkhash] 指代當前chunk的一個hash版本,也就是說,在每次編譯過程中,每一個chunk的hash都是不一樣的,如果某個chunk沒有發生變化,那麼該chunk的hash也不會發生變化,也就是可以理解如果頁面的檔案沒有發生改變,那麼chunk的hash也不會發生改變,因此未改變的檔案會在快取中讀取。
1.7) chunkFilename

   chunkFilename 引數 與 filename引數類似,都是用來定義生成的命名方式的,只不過,chunkFilename引數指定的是除了入口檔案外的chunk。

1.8) module引數中的 rules 配置(其實rules就相當於之前的loaders):

module: {
  rules: [
    {
      test: /\.js$/,
      include: [
        path.resolve(__dirname, 'src/pages/**/*.js')
      ],
      exclude: /(node_modules)/,      // 排除node_modules 檔案
      use: {
        loader: 'babel-loader',
        options: {
          presets: ['es2015'],
          plugins: ['transform-runtime']
        }
      }
    }
  ]
}

下面對一些子引數進行說明:
test: test其實是一個使用正規表示式匹配檔案的一種方式,比如上面的是,匹配以 .js結尾的檔案。
include: 用來表示本rules 配置是針對那些目錄/檔案,比如上面的程式碼配置是針對 src/pages/ 下的所有js檔案的。
exclude: 是用來排除掉那些檔案的,比如上面的是 /(node_modules)/ 的意思是 排除掉 node_modules下的js檔案的。
use: 使用到的loader的配置
use 下的 loader: 使用載入器名稱。
options: 該引數是為當前loader設定的引數,對於babel也可以單獨放在 .babelrc檔案中,也就是說該引數可以在專案的根目錄不包含.babelrc檔案,把.babelrc檔案移到該配置項來即可。

2.webpack CommonsChunkPlugin公共程式碼剝離

    與單頁應用相比,多頁應用存在多個入口(每個頁面即一個入口),每個入口意味著一套完整的js程式碼(包括業務邏輯和載入第三方庫、框架)。然後在每個頁面中分別載入該檔案即可,

CommonsChunkPlugin: 該外掛是一個可選用於建立一個獨立檔案(chunk), 這個檔案包括多個入口的chunk的公共模組,通過將公共模組拆出來,最終合成的檔案能夠在最開始的時候載入一次,便存到快取中,供後續使用,優點是:會帶來速度的提升,因為瀏覽器會迅速將公共的程式碼從快取中提取出來
,而不是每次訪問一個新頁面時候,再去載入一個更大的檔案。

CommonsChunkPlugin 初始化有哪些引數?
name: 給這個包含公共程式碼的chunk命名。
filename: 命名打包後生成的js檔案。
minChunks 公共程式碼的判斷標準:某個js模組被多少個chunk(入口檔案)載入了才算是公共程式碼。
chunks,表示需要在哪些chunk(配置中entry的每一項)裡尋找公共程式碼進行打包,預設不設定,那麼它的提取範圍為所有的chunk。

下面是一個簡單的CommonsChunkPlugin的實列含義:

var commonsChunkPlugin = new webpack.optimize.CommonsChunkPlugin = ({
  name: 'vender',     // 這公共程式碼的chunk命名為 'vender'
  filename: '[name].bundle.js',  // 生成的檔名為 vender.bundle.js
  minChunks: 2,     // 設定要有2個chunk(即2個頁面)載入的js模組才會被納入公共程式碼。
  chunks: ['pageA', 'pageB'],  // 只使用這些入口的 chunk
})

var commonsChunkPlugin = new webpack.optimize.CommonsChunkPlugin = ({
  name: 'vender',     // 這公共程式碼的chunk命名為 'vender'
  // filename: '[name].bundle.js',  // 生成的檔名為 vender.bundle.js
  minChunks: Infinity,  // 隨著入口chunk越來越多,這個配置保證沒其他的模組會打包進 公共的chunk
})

下面是使用對 webpack CommonsChunkPlugin 詳解的demo:

### 目錄結構如下:
demo                                        # 工程名
|   |--- dist                               # 打包後生成的目錄檔案             
|   |--- node_modules                       # 所有的依賴包
|   |--- src                                # 專案的檔案包
|   |    |--- common
|   |    |     |---css                      # 公用頁面的css檔案
|   |    |     |---js                       # 公用頁面的js檔案
|   |    |--- libs
|   |    |     |--- jquery.js               # 第三方庫檔案   
|   |    |--- main.js                       # 入口檔案
|   |--- .gitignore  
|   |--- README.md
|   |--- index.html                         # 首頁檔案
|   |--- package.json                      
|   |--- webpack.config.js                  # 配置檔案 
|   |--- webpack.production.config.js       # 上線打包配置檔案

1.1) 未使用 CommonsChunkPlugin 打包情況下
webpack.config.js 程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js'
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      publicPath: '/assets/', //指定資原始檔引用的目錄
      filename: 'bundle.js' // 檔名為 bundle.js
      //filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
    ]
}

main.js程式碼如下:

require('./common/css/style.css');
import './common/css/stylus.styl';
require('./libs/jquery.js');

執行命令 npm run start 可以看到打包後的檔案 bundle.js 程式碼內 包含第三方jquery框架的原始碼。

1.2)使用CommonsChunkPlugin
單一入口檔案,分檔案輸出
webpack.config.js 程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js'
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'chunk',
        filename: 'chunk.js'   // 把公用的 webpackJsonp 打包到chunk.js 裡面去
      })
    ]
}

main.js 程式碼如下:

require('./common/css/style.css');
import './common/css/stylus.styl';

require('./common/js/test.js');
require('./common/js/test2.js');

require('./libs/jquery.js');

輸出檔案 chunk.js 和 main.js , chunk.js 是把公用的webpackJsonp打包到 chunk.js 程式碼裡面去了。
但是 test.js 和 test2.js 及 jquery.js 被打包到 main.js 裡面去了。
現在我們可以將 test.js 和 test2.js 打包到chunk.js , 如下webpack配置程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js',
      chunk: ['./src/common/js/test.js', './src/common/js/test2.js']
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'chunk',
        filename: 'chunk.js'   // 把公用的 webpackJsonp 打包到chunk.js 裡面去
      })
    ]
}

1.3) 單一入口,模組重複引用,最終把相同的模組打包到 入口檔案內。
webpack.config.js 配置如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js'
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'chunk',
        minChunks: 2
      })
    ]
}

main.js 程式碼如下:

require('./common/css/style.css');
import './common/css/stylus.styl';

require('./common/js/test');
require('./common/js/test2');

test.js程式碼如下:

require('./test2');
var test1 = 11;
exports.test1 = test1;

test2模組被引用了兩次打包,但是最終模組被打包到main.js裡面去了,minChunks: 2 的含義是 至少入口檔案引用2個相同的模組會被打包到main.js 裡面去。

1.4) 多入口,模組重複引用,將多個引用的模組被打包到公共模組。如下webpack.config.js程式碼:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js',
      main2: './src/main2.js',
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'chunk',
        minChunks: 2
      })
    ]
}

main.js程式碼如下:

require('./common/css/style.css');
import './common/css/stylus.styl';

require('./common/js/test');
require('./common/js/test2');

main2.js程式碼如下:

var test1 = require('./common/js/test');
var test2 = require('./common/js/test2');

console.log(test1);
console.log(test2);

如上程式碼: main.js 和 main2.js 都引用了 test.js 和 test2.js,打包後,test.js和 test2.js 被打包到 chunk.js內。 minChunks的含義是:至少引用了
2個相同的模組才會被打包到 chunk.js裡面去。預設為2.

1.5)將第三方業務框架分開打包。

webpack.config.js 程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js',
      main2: './src/main2.js',
      jquery: ['./src/libs/jquery.js']
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: ['chunk', 'jquery'],
        minChunks: 2
      })
    ]
}

上面打包後 在頁面上 會先載入 jquery.js,該模組包含了所有的模組檔案,包括webpackJSon依賴程式碼,然後就是載入chunk.js,最後就是 main.js 和 main2.js了。
注意:webpack使用外掛 CommonsChunkPlugin進行打包的時候,將符合引用次數的(minChunks)的模組打包到name引數的陣列的第一個塊內(chunk), 然後陣列裡面的依次打包,(查詢entry裡面的key), CommonsChunkPlugin中的最後一個塊包含webpack生成的在瀏覽器上使用各個塊的載入程式碼,
所以頁面上使用的時候最後一個塊必須先載入。
所以我們在頁面上會先看到先 載入 jquery.js ,檢視該程式碼就會看到包含所有的程式碼,然後載入chunk.js程式碼,最後就是main.js和main2.js程式碼了。

1.6)minChunks: infinity
webpack.config.js程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js',
      main2: './src/main2.js',
      jquery: ['./src/libs/jquery.js'],
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'jquery',
        minChunks: 2
      })
    ]
}

main.js 程式碼如下:

require('./common/css/style.css');
import './common/css/stylus.styl';

require('./common/js/test');
require('./common/js/test2');

main2.js程式碼如下:

var test1 = require('./common/js/test');
var test2 = require('./common/js/test2');

console.log(test1);
console.log(test2);

執行程式碼後 發現main.js和 main2.js共同也能用的程式碼 test.js和 test2.js 程式碼被打包到jquery.js裡面去了。

當把 上面的 webpack.config.js的程式碼 minChunks 修改為 minChunks: Infinity 後, test.js 和 test2.js程式碼都被打包到 main.js和main2.js程式碼內了。

1.7)引數chunks
webpack.config.js 程式碼如下:

// 匯入路徑包
const path = require('path'); 
const webpack = require('webpack');
const HtmlWebpackPlugin = require('html-webpack-plugin');
var CommonsChunkPlugin = require("webpack/lib/optimize/CommonsChunkPlugin");

module.exports = {

    //開啟sourceMap便於除錯
    devtool: 'eval-source-map', 

    //入口檔案,
    entry: {
      main: './src/main.js',
      main2: './src/main2.js',
      jquery: ['./src/libs/jquery.js'],
    }, 

    output: {
      // 輸出檔案到當前目錄下的 build資料夾內
      path: path.resolve(__dirname, 'build'), 
      filename: '[name].js' // 可以打包為多個檔案
    },
    resolve: {
      extensions: ['*', '.js', '.json'],
    },
    // 使用loader模組
    module: {
        /* 
         * 在webpack2.0版本已經將 module.loaders 改為 module.rules, 當然module.loaders也是支援的。
         * 同時鏈式loader(用!連線)只適用於module.loader,同時-loader不可省略 
         */
        rules: [
          {
            test: /\.css$/,
            use: [
              'style-loader', {
                loader: 'css-loader',
                options: {
                  // modules: true // 設定css模組化,詳情參考 https://github.com/css-modules/css-modules
                }
              }, 
              {
                loader: 'postcss-loader',
                // 參考 https://github.com/postcss/postcss-loader
                options: {
                  plugins: function() {
                    return [
                      require('autoprefixer')
                    ];
                  }
                }
              }]
          }, 
          {
            test: /\.styl(us)?$/,
            use: [
                'style-loader', 'css-loader', {
                   loader: "postcss-loader",
                   options: {
                      plugins: function() {
                        return [
                          require('autoprefixer')
                        ];
                      }
                    }
                }, 'stylus-loader']
          }, 
          {
            test: /\.js$/,
            loader: 'babel-loader', 
            exclude: /node_modules/ //需要排除的目錄
          }
        ]
    },
    // 配置devServer各種引數
    devServer: {
        // contentBase: "./",   // 本地伺服器所載入的頁面所在的目錄
        hot: true,              // 配置HMR之後可以選擇開啟
        historyApiFallback: true, // 不跳轉
        inline: true // 實時重新整理
    },
    plugins: [
      new HtmlWebpackPlugin({
          template: './index.html' // 模版檔案
      }),
      new webpack.HotModuleReplacementPlugin(), // 熱載入外掛
      new CommonsChunkPlugin({
        name: 'jquery',
        minChunks: 2,
        chunks: ['main', 'main2']
      })
    ]
}

chunks 程式碼 包含['main', 'main2'],的含義是 都引用的模組才會打包到公用的模組 (jquery)內。

檢視git上的原始碼

3.瞭解ProvidePlugin的用途

 該外掛的作用是 自動載入模組,典型的列子:比如自動載入jquery,首先需要使用 npm install --save jquery 後,在webpack.config.js 加如下配置:

config.plugins.push(
  new Webpack.ProvidePlugin({
    $: 'jquery',
    jQuery: 'jquery'
}));

這樣配置完成後,我們在index.js 可以直接使用 $ 和 jQuery了,如:console.log(jQuery('body'));

3.2) 新增hash
   每次打完包後,我們看到 都會生成三個檔案,分別是manifest.js, vendor.js. 和 頁面上的業務js,manifest 是業務模組所有需要依賴的js,比如:webpackJsonp,而vendor.js 我們這邊打包成第三方框架或庫,我們這邊是
jquery,每次打包後都會生成這三個檔案,但是檔名都是一樣的,瀏覽器可能快取上一次的結果而無法載入最新的資料。因此我們需要新增 hash。

為了解決上述問題,我們需要為打包後的檔名新增hash值,這樣每次修改後打包的hash檔案值將會發生改變,瀏覽器會從伺服器端下載最新的檔案。
基本配置如下:

module.exports = {
  output: {
    path: path.resolve(__dirname, '../dist'),
    publicPath: '',
    filename: isLine ? 'static/js/[name].[chunkhash:5].js' : 'static/js/[name].js'
  }
}

上面的程式碼 首先判斷是否是線上上環境還是日常環境,如果是線上上打包的話,新增 [chunkHash:5]變數,表示打包後的檔案中加入5位的hash值。

3.3) 修改 vendor配置
上面解決瀏覽器快取問題後,每次打包生成一個新的5位的hash編碼值,但是問題又來了,當我修改某一個js檔案的時候,vendor.js的hash值也跟著改變了,我們明白 vendor是我們打包第三方庫jquery的,jquery我並沒有改動,
為了解決上面的問題,我們需要使用 CommonsChunkPlugin 外掛來配置;如下:

module.exports = {
  new Webpack.optimize.CommonsChunkPlugin({
    name: ['vendor','manifest'],
    minChunks: 2
  })
};

vendor.js 是打包庫檔案,而manifest.js 是打包模組中依賴的檔案,當某個js檔案發生改變的話,manifest是頁面載入時啟動檔案,所以hash值會發生改變。而vendor程式碼被抽取出來後作為一個單獨檔案,原始碼沒有改動的話,因此hash值就不會發生改變了。
webpack 多頁應用架構系列實戰git程式碼

相關文章