浏览器是无法直接使用模块之间的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.
(" +
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
}
/***/
},
},
])