http://www.cnblogs.com/libin-1/p/6938581.htmljavascript
所謂打包工具在web開發中主要解決的問題是:css
(1)文件依賴管理。畢竟如今都是模塊化開發,打包工具首先就是要梳理文件之間的依賴關係。html
(2)資源加載管理。web本質就是html、js和css的文件組合,文件的加載順序(前後時機)和文件的加載數量(合併、嵌入、拆分)也是打包工具重點要解決的問題。vue
(3)效率與優化管理。提升開發效率,即寫最少的代碼,作最好的效果展現;儘量的使用工具,減小機械coding和優化頁面效果,這個是考驗打包工具是否具有魅力的點。java
由上圖能夠推出,打包工具的結構應該是tool+plugins的結構。tool提供基礎能力,即文件依賴管理和資源加載管理;在此基礎上經過一系列的plugins來豐富打包工具的能力。plugins相似互聯網+的概念,文件經plugins處理以後,具有了web渲染中的某種優點。node
決定打包工具能走多遠的是plugins的豐富程度,而webpack目前偏偏是最豐富的,我這裏對比了一下fis與webpack在npm包上數據,看完就知道爲何要使用webpack了。webpack
webpack處理文件的過程能夠分爲兩個維度:文件間的關係和文件的內容。文件間的關係處理,主要是經過文件和模塊標記方法來實現;文件內容的處理主要經過loaders和plugins來處理。web
在webpack的世界裏,js是一等公民,是處理的入口,其餘資源都是在js中經過相似require的方式引入。webpack雖然支持命令行操做,可是通常將配置寫在webpack.conf.js文件中,文件內容是一個配置對象,基本配置項是:entry、ouput、module、plugins屬性。正則表達式
這裏引入了一個chunk的概念,chunk表示一個文件,默認狀況下webpack的輸入是一個入口文件,輸出也是一個文件,這個文件就是一個chunk,chunkId就是產出時給每一個文件一個惟一標識id,chunkhash就是文件內容的md5值,name就是在entry中指定的key值。npm
1
2
3
4
5
6
7
8
9
|
module.exports = {
entry: {
collection:
'./src/main.js'
// collection爲chunk的名字,chunk的入口文件是main.js
},
output: {
path:
'./dist/js'
,
filename:
'[name].[chunkhash].js'
// 輸出到dist/js目錄下,以collection+chunk內容的md5值做爲輸出的文件名
}
};
|
輸出:
moudle對應loader(加載器 )的配置,主要對指定類型的文件進行操做,舉個例子:js類型的文件和css文件須要不一樣的loader來處理。最經常使用的加載器是eslint-loader和babel-loader。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
module.exports = {
entry: {
collection:
'./src/main.js'
// collection爲chunk的名字,chunk的入口文件是main.js
},
output: {
path:
'./dist/js'
,
filename:
'[name].[chunkhash].js'
// 輸出到dist/js目錄下,以collection+chunk內容的md5值做爲輸出的文件名
}
module: {
rules: [
// rules爲數組,保存每一個加載器的配置
{
test: /\.js$/,
// test屬性必須配置,值爲正則表達式,用於匹配文件
loader:
'babel-loader?fakeoption=true!eslint-loader'
,
// loader屬性必須配置,值爲字符串,對於匹配的文件使用babel-loader和eslint-loader處理,處理順序從右向左,先eslint-loader,後babel-loader,loader之間用!隔開,loader與options用?隔開
exclude: /node_module/,
// 對於匹配的文件進行過濾,排除node_module目錄下的文件
include:
'./src'
// 指定匹配文件的範圍
}
]
}
};
|
其中,loader的options也能夠單獨使用options屬性來配置
1
2
3
4
5
6
7
8
9
|
rules: [
{
test: /\.js$/,
loader:
'babel-loader'
,
options: {
fakeoption:
true
}
}
]
|
另外一般babel-loader的配置項能夠寫在根目錄下的.babelrc文件中
1
2
3
4
5
|
{
"presets"
: [
"stage-2"
],
"plugins"
: [
"transform-runtime"
]
}
|
plugins用於擴展webpack的功能,相比着loader更加靈活,不用指定文件類型。經常使用的plugins有三個,html-webpack-plugin、commonChunkPlugin和ExtractTextPlugin。
1
2
3
4
5
6
7
8
9
10
11
|
var
HtmlwebpackPlugin = require(
'html-webpack-plugin'
);
module.exports = {
...
plugins: [
new
HtmlwebpackPlugin({<br> filename:
'collection.html'
,
// 入口html文件名
template:
'./src/index.html'
// 入口html文件模板
})
]
...
};
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
module.exports = {
...
plugins: [
// 把經過npm包引用的第三方類庫從入口文件中提取出來
new
webpack.optimize.CommonsChunkPlugin({
name:
'vendor'
,
minChunks:
function
(module, count) {
// 指定範圍是js文件來自node_modules
return
(module.resource && /\.js$/.test(module.resource) &&module.resource.indexOf(path.join(__dirname,
'../node_modules'
)) === 0);
}
}),
// 把webpack的module管理相關基礎代碼從vendor中提取到manifest
new
webpack.optimize.CommonsChunkPlugin({
name:
'manifest'
,
chunks: [
'vendor'
]
})
]
...
};
|
js是一等公民,webpack默認不產出css文件,產出css文件須要依賴ExtractTextPlugin插件來完成。
1
2
3
4
5
6
7
8
9
10
|
module.exports = {
...
plugins: [
// 把css片斷從入口js文件中提取到對應入口名的css文件中
new
ExtractTextPlugin({
filename:
'./dist/static/css/[name].[contenthash].css'
}),
]
...
};
|
理清這個過程得倒推,先看一下經webpack處理後的js文件,下面的例子中主要涉及3個產出文件,manifest是webpack的module管理代碼,vendor是第三方類庫文件,collection是入口文件,加載的順序是manifest-》vendor-》collection。
查看三個文件的內容可知:
vendor和collection的內容都是一個函數,相似jsonp請求回來的返回值。下面分別是vendor和collection中的代碼。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
webpackJsonp([0],[
// chunkid爲0
/* 0 */
/***/
(
function
(module, exports, __webpack_require__) {
...
/***/
}),
/* 1 */
/***/
(
function
(module, exports) {
...
/* 2 */
...
/* 9 */
/***/
(
function
(module, exports, __webpack_require__) {
...
/***/
}),
/* 10 */
,
// 此處moduleid=10的模塊爲空
/* 11 */
/***/
(
function
(module, exports) {
...
/***/
}),
...
]);
|
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
|
webpackJsonp([1],[
// chunkid爲1
/* 0 */
,
// moduleid爲0-9的模塊均爲空
/* 1 */
,
/* 2 */
,
/* 3 */
,
/* 4 */
,
/* 5 */
,
/* 6 */
,
/* 7 */
,
/* 8 */
,
/* 9 */
,
/* 10 */
/***/
(
function
(module, __webpack_exports__, __webpack_require__) {
...
};
/***/
}),
/* 11 */
,
/* 12 */
,
....
/* 59 */
/***/
(
function
(module, __webpack_exports__, __webpack_require__) {
"use strict"
;
Object.defineProperty(__webpack_exports__,
"__esModule"
, { value:
true
});
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_0_vue__ = __webpack_require__(14);
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_1_vue_validator__ = __webpack_require__(58);
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_1_vue_validator___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_1_vue_validator__);
/* harmony import */
var
__WEBPACK_IMPORTED_MODULE_2__router__ = __webpack_require__(54);
...
/***/
}),
...
], [59]);
// 此處多了一個參數,參數值爲[59],即moduleid=59的模塊名是入口模塊
|
從以上兩個文件能夠發現出一個規律,
(1)每一個文件的chunkid放在第一個參數;
(2)模塊都放在第二個參數,每一個模塊都有對應的id,數組都是從moduleid=0開始,依次增長,若是該模塊不在該文件中,則使用空值來代替;
(3)入口文件中的函數多了一個參數,參數裏面傳入了一個moduleid,視爲入口模塊。
接下來,咱們看一下manifest文件的內容,來看看webpackJsonp函數到底是怎麼運行的。
總的來講是利用閉包傳入了幾個自由變量:
modules:模塊自己是一個函數,modules用於存儲模塊函數數組。
installedModules:用於緩存模塊的返回值,即module.exports。
installedChunks:用於標記chunk文件是否已經被加載。
webpackJsonp:chunk文件加載後的callback函數,主要將文件中的模塊存儲到modules對象中,同時標記chunk文件的下載狀況,對於入口chunk來講,等全部的模塊都放入modules以後,執行入口模塊函數。
__webpack_require__:模塊加載函數,加載的策略是:根據moduleid讀取,優先讀取緩存installedModules,讀取失敗則讀取modules,獲取返回值,而後進行緩存。
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
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
|
/******/
(
function
(modules) {
// webpackBootstrap
/******/
// install a JSONP callback for chunk loading
/******/
var
parentJsonpFunction = window[
"webpackJsonp"
];
// webpackJsonp函數掛在window下,接收三個參數,chunkids:文件的id,moreModules: 文件中的module,executeModules:入口module
/******/
window[
"webpackJsonp"
] =
function
webpackJsonpCallback(chunkIds, moreModules, executeModules) {
/******/
// 把moreModules中的module的放入modules中,緩存起來
/******/
// 利用傳入的chunkid在installedChunks標記對應的文件已下載
/******/
var
moduleId, chunkId, i = 0, resolves = [], result;
/******/
for
(;i < chunkIds.length; i++) {
/******/
chunkId = chunkIds[i];
/******/
if
(installedChunks[chunkId]) {
/******/
resolves.push(installedChunks[chunkId][0]);
/******/
}
/******/
installedChunks[chunkId] = 0;
/******/
}
/******/
for
(moduleId
in
moreModules) {
/******/
if
(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
/******/
modules[moduleId] = moreModules[moduleId];
/******/
}
/******/
}
/******/
if
(parentJsonpFunction) parentJsonpFunction(chunkIds, moreModules, executeModules);
/******/
while
(resolves.length) {
/******/
resolves.shift()();
/******/
}
// 存在入口模塊,則加載對應的模塊並執行
/******/
if
(executeModules) {
/******/
for
(i=0; i < executeModules.length; i++) {
/******/
result = __webpack_require__(__webpack_require__.s = executeModules[i]);
/******/
}
/******/
}
/******/
return
result;
/******/
};
/******/
/******/
// 模塊緩存對象,存放module的exports
/******/
var
installedModules = {};
/******/
/******/
// chunk是否下載標記對象,key爲chunkid,值爲0表示已經下載
/******/
var
installedChunks = {
/******/
2: 0
// 表示chunkid=2的文件已下載,其實就是manifest文件自己
/******/
};
/******/
/******/
// 模塊加載函數:先從緩存讀取,沒有則從modules中讀取module函數,執行後返回exports,最後緩存起來
/******/
function
__webpack_require__(moduleId) {
/******/
/******/
// Check if module is in cache
/******/
if
(installedModules[moduleId]) {
/******/
return
installedModules[moduleId].exports;
/******/
}
/******/
// Create a new module (and put it into the cache)
/******/
var
module = installedModules[moduleId] = {
/******/
i: moduleId,
/******/
l:
false
,
/******/
exports: {}
/******/
};
/******/
/******/
// Execute the module function
/******/
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/
// Flag the module as loaded
/******/
module.l =
true
;
/******/
/******/
// Return the exports of the module
/******/
return
module.exports;
/******/
}
/******/
/******/
// This file contains only the entry chunk.
/******/
// The chunk loading function for additional chunks
/******/
__webpack_require__.e =
function
requireEnsure(chunkId) {
...
/******/
};
/******/
/******/
// expose the modules object (__webpack_modules__)
/******/
__webpack_require__.m = modules;
/******/
/******/
// expose the module cache
/******/
__webpack_require__.c = installedModules;
/******/
/******/
// identity function for calling harmony imports with the correct context
/******/
__webpack_require__.i =
function
(value) {
return
value; };
/******/
/******/
// define getter function for harmony exports
/******/
__webpack_require__.d =
function
(exports, name, getter) {
/******/
if
(!__webpack_require__.o(exports, name)) {
/******/
Object.defineProperty(exports, name, {
/******/
configurable:
false
,
/******/
enumerable:
true
,
/******/
get: getter
/******/
});
/******/
}
/******/
};
/******/
/******/
// getDefaultExport function for compatibility with non-harmony modules
/******/
__webpack_require__.n =
function
(module) {
/******/
var
getter = module && module.__esModule ?
/******/
function
getDefault() {
return
module[
'default'
]; } :
/******/
function
getModuleExports() {
return
module; };
/******/
__webpack_require__.d(getter,
'a'
, getter);
/******/
return
getter;
/******/
};
/******/
/******/
// Object.prototype.hasOwnProperty.call
/******/
__webpack_require__.o =
function
(object, property) {
return
Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/
// __webpack_public_path__
/******/
__webpack_require__.p =
"./"
;
/******/
/******/
// on error function for async loading
/******/
__webpack_require__.oe =
function
(err) { console.error(err);
throw
err; };
/******/
})
/************************************************************************/
/******/
([]);
//# sourceMappingURL=manifest.e7a81691a524d5b99b6b.js.map
|
整體而言,下圖能夠簡易的描述出webpack打包過程,該過程主要分爲三個階段:module構建、trunk構建和產出三個階段。
所謂打包工具在web開發中主要解決的問題是:
(1)文件依賴管理。畢竟如今都是模塊化開發,打包工具首先就是要梳理文件之間的依賴關係。
(2)資源加載管理。web本質就是html、js和css的文件組合,文件的加載順序(前後時機)和文件的加載數量(合併、嵌入、拆分)也是打包工具重點要解決的問題。
(3)效率與優化管理。提升開發效率,即寫最少的代碼,作最好的效果展現;儘量的使用工具,減小機械coding和優化頁面效果,這個是考驗打包工具是否具有魅力的點。
由上圖能夠推出,打包工具的結構應該是tool+plugins的結構。tool提供基礎能力,即文件依賴管理和資源加載管理;在此基礎上經過一系列的plugins來豐富打包工具的能力。plugins相似互聯網+的概念,文件經plugins處理以後,具有了web渲染中的某種優點。
決定打包工具能走多遠的是plugins的豐富程度,而webpack目前偏偏是最豐富的,我這裏對比了一下fis與webpack在npm包上數據,看完就知道爲何要使用webpack了。
webpack處理文件的過程能夠分爲兩個維度:文件間的關係和文件的內容。文件間的關係處理,主要是經過文件和模塊標記方法來實現;文件內容的處理主要經過loaders和plugins來處理。
在webpack的世界裏,js是一等公民,是處理的入口,其餘資源都是在js中經過相似require的方式引入。webpack雖然支持命令行操做,可是通常將配置寫在webpack.conf.js文件中,文件內容是一個配置對象,基本配置項是:entry、ouput、module、plugins屬性。
這裏引入了一個chunk的概念,chunk表示一個文件,默認狀況下webpack的輸入是一個入口文件,輸出也是一個文件,這個文件就是一個chunk,chunkId就是產出時給每一個文件一個惟一標識id,chunkhash就是文件內容的md5值,name就是在entry中指定的key值。
1
2
3
4
5
6
7
8
9
|
module.exports = {
entry: {
collection:
'./src/main.js'
// collection爲chunk的名字,chunk的入口文件是main.js
},
output: {
path:
'./dist/js'
,
filename:
'[name].[chunkhash].js'
// 輸出到dist/js目錄下,以collection+chunk內容的md5值做爲輸出的文件名
}
};
|
輸出:
moudle對應loader(加載器 )的配置,主要對指定類型的文件進行操做,舉個例子:js類型的文件和css文件須要不一樣的loader來處理。最經常使用的加載器是eslint-loader和babel-loader。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
module.exports = {
entry: {
collection:
'./src/main.js'
// collection爲chunk的名字,chunk的入口文件是main.js
},
output: {
path:
'./dist/js'
,
filename:
'[name].[chunkhash].js'
// 輸出到dist/js目錄下,以collection+chunk內容的md5值做爲輸出的文件名
}
module: {
rules: [
// rules爲數組,保存每一個加載器的配置
{
|