瀏覽器是無法直接使用模塊之間的commonjs或es6,webpack在打包時做了什么處理,才能讓瀏覽器能夠執行呢,往下看吧。
使用commonjs語法
先看下寫的代碼,
app.js

minus.js

webpack.config.js

代碼非常簡單,沒啥可說的,直接上編譯后的代碼來分析,代碼可以直接復制過來在瀏覽器執行調試
// 一個IIFE, 方法的形參是一個對象,key是頁面的路徑,value是頁面的代碼
;(function (modules) {
// 緩存已經讀取過的module,避免重復執行
var installedModules = {}
// 核心方法。moduleId以頁面的路徑作為屬性
function __webpack_require__(moduleId) {
// 如果緩存存有moduleId,代表已經執行過,直接把緩存里的值返回
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
}
// Load entry module and return exports
return __webpack_require__((__webpack_require__.s = "./app.js"))
})(
// IIFE的參數,把所有需要的頁面轉換為對象的key,先讀取入口文件app.js,文件內部需要minus.js,所以再次調用__webpack_require__執行
{
"./app.js": function (module, exports, __webpack_require__) {
var minus = __webpack_require__(
/*! ./vendor/minus */ "./vendor/minus.js"
)
console.log("minus(1, 2) = ", minus(1, 2))
},
"./vendor/minus.js": function (module, exports) {
module.exports = function (a, b) {
return a - b
}
},
})
再來看下es6語法有什么區別
;(function (modules) {
// webpackBootstrap
// The module cache
var installedModules = {}
// The require function
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
}
// expose the modules object (__webpack_modules__)
// __webpack_require__是一個方法,給這個方法加一個屬性,內容是頁面模塊這個對象
__webpack_require__.m = modules
// expose the module cache
// 同理也是加了一個讀緩存模塊的屬性
__webpack_require__.c = installedModules
// define getter function for harmony exports
// 給這個對象賦值,為什么不直接賦值呢,因為這樣子,這個屬性是不可變的,怎么改使用還是取的get
__webpack_require__.d = function (exports, name, getter) {
if (!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
enumerable: true,
get: getter,
})
}
}
// define __esModule on exports
// 給使用es6導出的模塊打上標記,Symbol.toStringTag
// 請看https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Symbol/toStringTag
// 做這一步的原因是 其他頁面導出的時候會判斷有沒有標記,有的話就會換另一種方式讀取,導出看__webpack_require__.n
__webpack_require__.r = function (exports) {
if (typeof Symbol !== "undefined" && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, {
value: "Module",
})
}
Object.defineProperty(exports, "__esModule", { value: true })
}
// getDefaultExport function for compatibility with non-harmony modules
// 看導出時的讀取方式是采用es6還是commonjs
__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)
}
// Load entry module and return exports
return __webpack_require__((__webpack_require__.s = "./app.js"))
})({
"./app.js": function (module, __webpack_exports__, __webpack_require__) {
"use strict"
__webpack_require__.r(__webpack_exports__)
// 這里需要注意,使用es6導入的時候,webpack改變了原變量名字,所以這個變量可以使用可以修改,
// 但是如果變量是對象的情況下,不允許修改變量指向的內存地址
/* harmony import */ var _vendor_sum__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
/*! ./vendor/sum */ "./vendor/sum.js"
)
console.log(
"sum(1, 3) = ",
Object(_vendor_sum__WEBPACK_IMPORTED_MODULE_0__["sum"])(1, 3)
)
},
"./vendor/sum.js": function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict"
// 打上標記是es6語法
__webpack_require__.r(__webpack_exports__)
// 屬性賦值
/* harmony export (binding) */ __webpack_require__.d(
__webpack_exports__,
"sum",
function () {
return sum
}
)
function sum(a, b) {
return a + b
}
},
})
如何使用分包懶加載
在某些特定環境比如點擊某個按鈕才加載,webpack會創建一個script標簽來加載內容(vue-cli 有利用prefetch做優化)同時生成一個promise
把resolve和reject放進installedChunks數組里,當頁面加載好后會從數組取出resolve執行(利用webpackJsonp重寫push方法)
// 加載模塊
__webpack_require__.e = function requireEnsure(chunkId) {
var promises = []
// JSONP chunk loading for javascript
var installedChunkData = installedChunks[chunkId]
if (installedChunkData !== 0) {
// 0 means "already installed".
// a Promise means "currently loading".
if (installedChunkData) {
promises.push(installedChunkData[2])
} else {
// setup Promise in chunk cache
var promise = new Promise(function (resolve, reject) {
installedChunkData = installedChunks[chunkId] = [
resolve,
reject,
]
})
promises.push((installedChunkData[2] = promise))
// start chunk loading
var script = document.createElement("script")
var onScriptComplete
script.charset = "utf-8"
script.timeout = 120
if (__webpack_require__.nc) {
script.setAttribute("nonce", __webpack_require__.nc)
}
script.src = jsonpScriptSrc(chunkId)
// create error before stack unwound to get useful stacktrace later
var error = new Error()
onScriptComplete = function (event) {
// avoid mem leaks in IE.
script.onerror = script.onload = null
clearTimeout(timeout)
var chunk = installedChunks[chunkId]
if (chunk !== 0) {
if (chunk) {
var errorType =
event &&
(event.type === "load" ? "missing" : event.type)
var realSrc =
event && event.target && event.target.src
error.message =
"Loading chunk " +
chunkId +
" failed.\n(" +
errorType +
": " +
realSrc +
")"
error.name = "ChunkLoadError"
error.type = errorType
error.request = realSrc
chunk[1](error)
}
installedChunks[chunkId] = undefined
}
}
var timeout = setTimeout(function () {
onScriptComplete({ type: "timeout", target: script })
}, 120000)
script.onerror = script.onload = onScriptComplete
document.head.appendChild(script)
}
}
return Promise.all(promises)
}
// 設置webpackJsonp
var jsonpArray = (window["webpackJsonp"] = window["webpackJsonp"] || [])
var oldJsonpFunction = jsonpArray.push.bind(jsonpArray)
jsonpArray.push = webpackJsonpCallback
jsonpArray = jsonpArray.slice()
for (var i = 0; i < jsonpArray.length; i++)
webpackJsonpCallback(jsonpArray[i])
var parentJsonpFunction = oldJsonpFunction
// 異步加載的模塊,加載好后,執行webpackJsonp的push方法
;(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
[0],
{
/***/ "./vendor/sum.js": /***/ function (
module,
__webpack_exports__,
__webpack_require__
) {
"use strict"
__webpack_require__.r(__webpack_exports__)
/* harmony export (binding) */ __webpack_require__.d(
__webpack_exports__,
"sum",
function () {
return sum
}
)
function sum(a, b) {
return a + b
}
/***/
},
},
])
