webpack 多頁面|入口支持和公共組件單獨打包--轉載

轉載自:http://www.jb51.net/article/117490.htmcss

 

本篇主要介紹:如何自動構建入口文件,並生成對應的output;公共js庫如何單獨打包。html

多入口文件,自動掃描入口。同時支持SPA和多頁面型的項目html5

公共js庫如何單獨打包。node

基礎結構和準備工做jquery

如下示例基於上一篇進行改進,上一篇項目源碼webpack

目錄結構說明git

?
1
2
3
4
5
6
7
8
9
10
11
12
13
.
├── package.json    # 項目配置
├── src      # 源碼目錄
│ ├── pageA.html    # 入口文件a
│ ├── pageB.html    # 入口文件b
│ ├── css/     # css資源
│ ├── img/     # 圖片資源
│ ├── js     # js&jsx資源
│ │ ├── pageA.js    # a頁面入口
│ │ ├── pageB.js    # b頁面入口
│ │ ├── lib/    # 沒有存放在npm的第三方庫或者下載存放到本地的基礎庫,如jQuery、Zepto、avalon
│ ├── pathmap.json   # 手動配置某些模塊的路徑,能夠加快webpack的編譯速度
├── webpack.config.js   # webpack配置入口

一:自動構建入口github

官方多入口示例web

webpack默認支持多入口,官方也有多入口的示例。配件文件webpack.config.js以下npm

?
1
2
3
4
5
6
7
8
9
10
11
12
13
//已簡化
var path = require( "path" );
module.exports = {
  entry: {
   pageA: "./pageA" ,
   pageB: "./pageB"
  },
  output: {
   path: path.join(__dirname, "js" ),
   filename: "[name].bundle.js" ,
   chunkFilename: "[id].chunk.js"
  }
}

每新增一個頁面就須要在webpack.config.js的entry 中增長一個 pageC:"./pageC",頁面少還好,頁面一多,就有點麻煩了,並且配置文件,儘量不改動。那麼如何支持不修改配置呢?

自動構建入口函數

entry其實是一個map對象,結構以下{filename:filepath},那麼咱們能夠根據文件名匹配,很容易構造自動掃描器:
npm 中有一個用於文件名匹配的 glob模塊,經過glob很容易遍歷出src/js目錄下的全部js文件:

安裝glob模塊

?
1
$ npm install glob --save-dev

修改webpack.config.js 配置,新增entries函數,修改entry:entries(),修改output的filename爲"[name].js"

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
//引入glob
var glob = require( 'glob' )
//entries函數
var entries= function () {
  var jsDir = path.resolve(srcDir, 'js' )
  var entryFiles = glob.sync(jsDir + '/*.{js,jsx}' )
  var map = {};
 
  for ( var i = 0; i < entryFiles.length; i++) {
   var filePath = entryFiles[i];
   var filename = filePath.substring(filePath.lastIndexOf( '\/' ) + 1, filePath.lastIndexOf( '.' ));
   map[filename] = filePath;
  }
  return map;
}
//修改入口,已經修改outp的filename
module.exports = {
  //entry: "./src/js/index.js",
  entry: entries(),
  output: {
   path: path.join(__dirname, "dist" ),
   filename: "[name].js"
  },
  ......
  //如下省略,能夠見下文詳細配置

測試

1.在src/js目錄中新增pageA.js

?
1
2
3
//js只有兩行代碼,在body中加一句話
var $ = require( "jquery" )
$( "<div>這是jquery生成的多頁面示例</div>" ).appendTo( "body" )

2.新增pageA.html,也順便修改原來的index.html 對於js文件名的更改

?
1
2
3
4
5
6
7
8
9
10
<!DOCTYPE html>
< html lang = "en" >
< head >
< meta charset = "UTF-8" >
< title ></ title >
</ head >
< body >
< script src = "../dist/index.js" ></ script >
</ body >
</ html >

3.執行webpack,啓動dev-server

?
1
2
$ webpack
$ webpack-dev-server

OK,成功打包生成pageA.js,成功運行

二:公共庫單獨打包

先來分析下,上個步驟打包的日誌:

index.js 依賴了avalon 和 jquery,而後打包後的index.js 有480kb

pageA.js 只用了jquery,而後打包後的js 有294kb

那麼若是引用的lib庫多一點,又被不少頁面引用,那麼lib庫就會被重複打包到page.js中去,模塊越多重複加載的狀況越嚴重。

若是把公共代碼提取出來做爲單獨的js,那麼就處處能夠複用,瀏覽器也就能夠進行緩存,這時候就須要用到webpack內置插件WebPack.optimize.CommonsChunkPlugin

CommonsChunkPlugin 介紹

使用

?
1
new webpack.optimize.CommonsChunkPlugin(options)

Options
翻譯得比較簡單,詳見官方說明:

  1. options.name or options.names(string|string[]): 公共模塊的名稱
  2. options.filename (string): 公開模塊的文件名(生成的文件名)
  3. options.minChunks (number|Infinity|function(module,count) - boolean): 爲number表示須要被多少個entries依賴纔會被打包到公共代碼庫;爲Infinity 僅僅建立公共組件塊,不會把任何modules打包進去。而且提供function,以便於自定義邏輯。
  4. options.chunks(string[]):只對該chunks中的代碼進行提取。
  5. options.children(boolean):若是爲true,那麼公共組件的全部子依賴都將被選擇進來
  6. options.async(boolean|string):若是爲true,將建立一個 option.name的子chunks(options.chunks的同級chunks) 異步common chunk
  7. options.minSize(number):全部公共module的size 要大於number,纔會建立common chunk

2個經常使用的例子,更多例子見官方說明:

1.Commons chunk for entries:針對入口文件提取公共代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
new CommonsChunkPlugin({
  name: "commons" ,
  // (the commons chunk name)
 
  filename: "commons.js" ,
  // (the filename of the commons chunk)
 
  // minChunks: 3,
  // (Modules must be shared between 3 entries)
 
  // chunks: ["pageA", "pageB"],
  // (Only use these entries)
})

2.Explicit vendor chunk:直接指定第三方依賴庫,打包成公共組件

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
entry: {
  vendor: [ "jquery" , "other-lib" ],
  app: "./entry"
}
new CommonsChunkPlugin({
  name: "vendor" ,
 
  // filename: "vendor.js"
  // (Give the chunk a different name)
 
  minChunks: Infinity,
  // (with more entries, this ensures that no other module
  // goes into the vendor chunk)
})

CommonsChunkPlugin使用

基於上篇的項目,參考上面的第二個例子,咱們將jquery 和 avalon 提取出來打包成vendor.js

完整的webpack.config.js 以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
```js
var webpack = require( "webpack" );
  var path = require( "path" );
  var srcDir = path.resolve(process.cwd(), 'src' );
  var nodeModPath = path.resolve(__dirname, './node_modules' );
  var pathMap = require( './src/pathmap.json' );
  var glob = require( 'glob' )
  var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
  var entries= function () {
  var jsDir = path.resolve(srcDir, 'js' )
  var entryFiles = glob.sync(jsDir + '/*.{js,jsx}' )
  var map = {};
  for ( var i = 0; i < entryFiles.length; i++) {
   var filePath = entryFiles[i];
   var filename = filePath.substring(filePath.lastIndexOf( '\/' ) + 1, filePath.lastIndexOf( '.' ));
   map[filename] = filePath;
  }
  return map;
}
 
module.exports = {
  //entry: "./src/js/index.js",
  //entry: entries(),
  entry: Object.assign(entries(), {
   // 用到什麼公共lib(例如jquery.js),就把它加進vendor去,目的是將公用庫單獨提取打包
   'vendor' : [ 'jquery' , 'avalon' ]
  }),
  output: {
   path: path.join(__dirname, "dist" ),
   filename: "[name].js"
  },
  module: {
   loaders: [
    {test: /\.css$/, loader: 'style-loader!css-loader' }
   ]
  },
  resolve: {
   extensions: [ '.js' , "" , ".css" ],
   root: [srcDir,nodeModPath],
   alias: pathMap,
   publicPath: '/'
  },
  plugins: [
   new CommonsChunkPlugin({
    name: 'vendor' ,
    minChunks: Infinity
   })
  ]
}
```

測試、驗證

1.修改入口(Object.assign 是html5.js裏面的....)

?
1
2
3
4
5
//entry: entries(),
entry: Object.assign(entries(), {
  // 用到什麼公共lib(例如jquery.js),就把它加進vendor去,目的是將公用庫單獨提取打包
  'vendor' : [ 'jquery' , 'avalon' ]
}),

2.加入插件CommonsChunkPlugin

?
1
2
3
4
5
6
7
8
var CommonsChunkPlugin = webpack.optimize.CommonsChunkPlugin;
config 中增長 plugins
  plugins: [
   new CommonsChunkPlugin({
    name: 'vendor' ,
    minChunks: Infinity
   })
  ]

3.修改index.html 和 pageA.html,增長對verdor.js的引用

?
1
2
3
< script src = "../dist/vendor.js" ></ script >
< script src = "../dist/index.js" ></ script >
//< script src = "../dist/pageA.js" ></ script >

4.執行webpack

$ webpack

結果分析

能夠看到index.js 就只有457 bytes了,pageA.js 227bytes。vendor.js 是集成了jquery+avalon,因此有488kb。

這樣vendor.js 就能夠重複利用了,也方便瀏覽器進行緩存。

調試過程當中發現

Uncaught ReferenceError: webpackJsonp is not defined

這個是由於當時把vendor.js引入 放到了page.js 後面,致使page.js執行異常,因此,請必定把vendor.js 放在前面。

生成後的index.js就很輕便了,第三方庫都被打包到vendor中了,代碼以下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
webpackJsonp([0],[
/* 0 */
/***/ function (module, exports, __webpack_require__) {
 
  /**
   * Created by sloong on 2016/6/1.
   */
  //avalon 測試
  var avalon = __webpack_require__(1);
  avalon.define({
   $id: "avalonCtrl" ,
   name: "Hello Avalon!"
  });
 
  /*
  //zepto 測試
  require("zepto")
 
  $("<div>這是zepto生成的</div>").appendTo("body")*/
 
  //jquery 測試
  var $ = __webpack_require__(2)
  $( "<div>這是jquery生成的</div>" ).appendTo( "body" )
 
/***/ }
]);

頁面測試均正常

OK,本篇結束了。如何讓webpack 自動在html文件中引入所需js的script標籤,如何給js和css文件加了hash值,這樣瀏覽器每次都能檢測到文件變動,並且也不須要手動修改引入的js文件連接,這些操做webpack都能輕鬆搞定。

相關文章
相關標籤/搜索