webpack 用於編譯 javascript
模塊, 能夠把文件格式編譯成咱們想要的靜態文件格式, 可是處理的過程並非所有由 webpack
自己完成, webpack
只是提供了一個打包機制, 對於各種文件的打包處理須要使用相對應的 預處理模塊 loader
來處理, 做爲一種機制 webpack
會幫助各類 loader
提供識別入口目錄、入口文件、 輸出目錄, 輸出文件。javascript
首先咱們試着打包一個只包含 console.log('hello world')
的 js
文件。java
webpack
環境# 新建 demo 目錄
mkdir webpack-demo cd webpack-demo
# 初始化目錄
npm init -y
# 本地安裝 webpack 工具
npm install webpack webpacl-cli --save-dev
# webpack 默認的入口文件是 .src/index.js 建立 src 目錄和 index.js 文件
mkdir src
echo 「console.log('hello world')」 > src/index.js
# 執行 webpack 命令 須要查看打包後文件, 這裏使用 development 模式
npx webpack --mode development
複製代碼
因爲打包後的文件比較繁瑣, 這裏咱們簡化一下打包後的文件webpack
(function(modules) {
var installedModules = {}
function __webpack_require__(moduleIid) {
}
return __webpack_require__(__webpack_require__.s = "./src/index.js")
})({
"./src/index.js": (function(module, exports) {
eval("console.log('test webpack entry')");
})
})
複製代碼
打包後的文件含有大量的註釋和
webpack
自己的變量, 爲了方便分析能夠把這些註釋和 相似__webpack_require__.s
的複製語句所有刪掉web
從上面的代碼能夠看到,npm
webpack
打包後的代碼經過一個 IIFE
自執行函數, 這個函數接收一個對象參數, 這個對象的 key
爲入口文件的目錄, value
是一個執行入口文件裏面代碼的函數modules
傳遞給 IIFE
函數IIFE
函數裏面聲明瞭一個變量 installedModules
用來存放緩存, 一個函數 __webpack_require__
, 用來轉化入口文件裏面的代碼IIFE
最終把 modules
裏面的的 key
傳遞給 __webpack_require__
函數並返回。咱們進一步看 __webpack_require__
函數都作了什麼。緩存
__webpack_require__
分析function (modules) {
var installedModules = {}
function __webpack_require__ (moduleId) {
if(installedModules[moduleId]) {
return installedModules[moduleId].exports
}
var module = installedModules[moduleId] = {
i: moduleId,
l:false,
exports: {}
}
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
module.l = true
return module.exports
}
}
複製代碼
__webpack_require__
函數接收 ./src/index.js
installedModules
中是否包含 key
爲 ./src/index.js
的對象, 若是存在直接返回這個對象中的 exports
i
l
exports
modules[moduleId].call
調用 IIFE
參數的 value
函數, 並把 value
對應的函數中的 this
指向賦值給了 module.exports
, 後面的 call
方法的後面三個參數爲 value
對應函數的參數module.exports
, 這裏的 module.exports
在第四步的時候已賦值爲 IIFE
參數對象中的 value
對應的函數。因此能夠看出來。 函數 __webpack_require__
實際返回的就是 IIFE
參數對象中的 value
對應的函數, 也就是 eval("\nconsole.log('test webpack entry')\n\n\n//# sourceURL=webpack:///./src/index.js?")
,bash
當咱們運行 webpack
打包後的文件的時候執行的是 "console.log('test webpack entry')"
函數
eval() 函數可計算某個字符串,並執行其中的的 JavaScript 代碼。工具
上面講的打包過程入口文件中並無引用其餘的代碼模塊, 當入口文件中引用其餘的模塊的時候, webpack
的打包過程也和上述過程類似。ui
在 ./src/
下新建 main.js
module.exports = () => {
console.log('main module')
}
複製代碼
在 ./src/index.js
中引入 main.js
const main = reuiqre('./main.js')
console.log('webpack index entry')
main()
複製代碼
運行 npx webpack
打包後的文件
IIFE
參數的變化(function (mudoles) {
})({
'./src/index.js': (function(module,exports, __webpack_require__) {
eval("const main = __webpack_require__(/*! ./main.js */ \"./src/main.js\")\r\nconsole.log('test webpack entry')\r\n\n\n//# sourceURL=webpack:///./src/index.js?");
}),
'./src/main.js': (function(module, exports) {
eval("console.log('main module')\n\n//# sourceURL=webpack:///./src/main.js?");
})
})
複製代碼
若是入口文件中引用了其餘模塊的文件,將會把這些模塊添加到 IIFE
的參數對象中, key
爲模塊的路徑, value
執行該模塊代碼的函數。
IIFE
函數執行邏輯的變化function (modules) {
var installedModules = {}
function __webpack_require__ (moduleId) {
if (installedModules[moduleId]) {
return installedMOdules[mudoleId].exports
}
var module = installedModuled[moduleId] = {
i: moduleId,
l: false,
exports: {}
}
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
return module.exports
}
return __webpack_require__(__webpack_require__.s = "./src/index.js")
}
複製代碼
上面的代碼中依舊返回了以 ./src/index.js
爲參數的函數。 可是函數裏面的邏輯發生了改變。
installedModules
變量存放緩存./src/index.js
傳入 __webpack_require__
函數./src/index.js
不在緩存中, 往下執行module
並在緩存中存放一以 ./src/index.js
爲 key
的對象modules[moduleId]
函數,並指明 做用域和參數 也就是function(module,exports, __webpack_require__) {
eval("const main = __webpack_require__(/*! ./main.js */ \"./src/main.js\")\r\nconsole.log('test webpack entry')\r\n\n\n//# sourceURL=webpack:///./src/index.js?")}
複製代碼
module.exports
, 因爲在第五步調用 modules['./src/index.js']
函數的時候, 已經把 module.exports
做爲了函數的 this
做用域, 因此這時 module.exports
實際就是 modules['./src/index.js']
執行的函數。eval
代碼中使用了一個函數 __webpack_require__
, 這個函數就是在第五步 modules[moduleId].call(module.exports, module, module.exports, __webpack_require__)
的最後一個參數 __webpack_require__
, 這時繼續調用 __webpack__require__
函數並傳入 ./src/main.js
./src/main.js
傳入 __webpack_require__
中, 依舊不在緩存, 再次聲明一個變量 module
並在緩存中新增一個 key
爲 ./src/main.js
的對象。modules[moduleId].call
這時調用 IIFE
參數對象中 key
爲 ./src/main.js
的函數:(function(module, exports) {
eval("console.log('main module')\n\n//# sourceURL=webpack:///./src/main.js?");
})
複製代碼
module.export
, 同第6步類似這時的 module.exports
就是 modules[./src/main.js]
對應的函數。eval
和註釋):const main = console.log('main module')
console.log(test webpack entry)
複製代碼