mode
Mode配置选项,可以告知webpack使用响应模式的内置优化:
默认值是production
可选值有:'none' | 'development' | 'production'
module.exports = {
mode: 'development', // mode是顶层配置选项
entry: './src/index.js',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'js/main.js',
},
plugins: [
new CleanWebpackPlugin(),
new HtmlWebpakcPlugin()
]
}
模块化实现原理
# 以下所有模块化实现原理的版本如下: (版本不同,实现模块化方式可能会存在差异)
$ webpack --version
# webpack 5.46.0
# webpack-cli 4.7.2
webpack.config.js
module.exports = {
mode: 'development',
// 在开始模块化调试的时候,需先将devtool设置为'source-map'
// 因为mode设置为development后,devtool的默认值就会被设置为eval
// 此时编译后的代码其实是不利于我们进行阅读的
devtool: 'source-map',
}
ES Module模块化
// 模块
export const sum = (num1, num2) => num1 + num2
// 主模块
import { sum } from './js/math'
console.log(sum(3, 5))
// 打包后的js文件 ---- 是一个使用了严格模式的 IIFE --- 在加载后会立即执行
// 为了便于阅读,部分变量进行了变量名的替换
(function() {
"use strict";
// webpack_modules是一个对象,key是模块的路径,value是函数,函数体为模块中之前定义的代码
var webpack_modules = {
"./src/js/math.js":
// __unused_webpack_module就是传入的module对象 --- {export: {...}}
// 但是这个对象是没有使用的,只是占位,所以取名叫__unused_ ...
(function(__unused_webpack_module, __webpack_exports__, __webpack_require__) {
// 给导出对象加上标识
__webpack_require__.r(__webpack_exports__)
// 在导出对象上,挂载sum函数的函数体
__webpack_require__.d(__webpack_exports__, {
"sum": function() { return sum }
})
// 之前在math.js中定义的代码
const sum = (num1, num2) => num1 + num2
})
}
// 一个空对象,用于对模块数据进行缓存
var cache = {}
// 一个函数,解析模块
function __webpack_require__(moduleId) {
// 如果有缓存,直接取缓存中的值
var cachedModule = cache[moduleId]
if (cachedModule !== undefined) {
return cachedModule.exports
}
// 没有缓存,使用连续赋值,使module和chche[moduleId]指向同一个对象
// { exports: {} } --- 存放导出数据的对象
var module = cache[moduleId] = {
exports: {}
}
// 取出模块中定义的diam并执行
webpack_modules[moduleId](module, module.exports, __webpack_require__)
// 将导出的数据返回
return module.exports
}
// 在存储导出数据的对象上挂载需要导出的数据
!function() {
/*
参数1: 存放导出数据的对象
参数2: 需要导出的数据
*/
__webpack_require__.d = function(exports, definition) {
// 遍历导出数据对象自身
for(var key in definition) {
// 将导出的数据设置代理
if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
Object.defineProperty(exports, key, { enumerable: true, get: definition[key] })
}
}
}
}()
// 传入一个属性,判断该属性是不是传入对象的自身属性
!function() {
__webpack_require__.o = function(obj, prop) { return Object.prototype.hasOwnProperty.call(obj, prop) }
}()
// IIFE 给导出的对象加上标识,表明这个对象是Webpack解析后,用于存放模块导出内容的对象
!function() {
__webpack_require__.r = function(exports) {
// 如果浏览器支持Symbol,那么调用(导出对象).toString()的时候输出 [object Module]
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
}
// 每一个导出模块都有一个属性 __esModule, 值为true
Object.defineProperty(exports, '__esModule', { value: true })
}
}()
var __webpack_exports__ = {}
// IIFE 主模块
!function() {
// 给模块打上标识
__webpack_require__.r(__webpack_exports__)
// 引入模块,并存储导出的数据
var mathModuleObj_ = __webpack_require__("./src/js/math.js")
console.log(mathModuleObj_.sum(3, 5))
}()
})();
将上述的代码进行简化后如下
!function() {
// webpack_modules是模块映射,key是模块的路径,value是函数,函数体为模块中之前定义的代码
var module_map = {
"./src/js/math.js":
/*
参数1: module对象
参数2: 存放导出数据的对象
参数3: 自定义解析导入模块的函数,(在模块中又引入了别的模块的时候使用)
*/
(function(__exports, __require) {
addTag()
// 之前在math.js中定义的代码
const sum = (num1, num2) => num1 + num2
// 挂载需要暴露的数据
__exports.sum = sum
})
}
// 一个空对象,用于对模块数据进行缓存
var cache = {}
// 一个函数,解析模块
function __require(moduleId) {
// 如果有缓存,直接取缓存中的值
if (cache[moduleId]) {
return cache[moduleId].exports
}
// 没有缓存,使用连续赋值,使module和chche[moduleId]指向同一个对象
// { exports: {} } --- 存放导出数据的对象
var module = cache[moduleId] = {
exports: {}
}
// 执行模块,将导出数据存放到预先定义的对象中
module_map[moduleId](module.exports, __require)
// 将导出的数据返回
return module.exports
}
// 打上ESM的标记,以便于区分ESM模块和CJS模块
function addTag() {
if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
}
// 加上属性__esModule 值为true
Object.defineProperty(exports, '__esModule', { value: true })
}
// 执行主模块
!function() {
addTag()
var { sum } = __require("./src/js/math.js")
console.log(sum(3, 5))
}()
}()
Common JS模块化
// 模块
const sum = (num1, num2) => num1 + num2
module.exports.sum = sum
// 主模块
const { sum } = require('./js/math')
console.log(sum(3, 5))
// 编译后的js文件 --- 依旧是一个IIFE
(function() {
// 模块映射 key是模块路径, value是函数 函数体是模块对应代码体
var __webpack_modules__ = ({
"./src/js/math.js":
(function(module) {
const sum = (num1, num2) => num1 + num2
module.exports.sum = sum
})
});
// 缓存
var __webpack_module_cache__ = {};
// 自定义require方法 --- 和ESM的自定义require方法原理一致
function __webpack_require__(moduleId) {
var cachedModule = __webpack_module_cache__[moduleId];
if (cachedModule !== undefined) {
return cachedModule.exports;
}
var module = __webpack_module_cache__[moduleId] = {
exports: {}
};
__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
return module.exports;
}
var __webpack_exports__ = {};
// IIFE 执行主模块
!function() {
const { sum } = __webpack_require__("./src/js/math.js")
console.log(sum(3, 5))
}();
})();
ESM 和 CJS之间互相调用
从ESM的模块化原理和CJS的模块化原理可以看出,在webpack中,ESM和CJS的模块化转换方式基本是一致的
所以在webpack中支持ESM和CJS互相调用
// esm模块导出
module.exports.format = () =>console.log('format')
// cjs模块导出
export const sum = (num1, num2) => num1 + num2
// 主模块
// CJS导出,ESM引入
import { format } from './js/format'
// ESM导出,CJS引入
const { sum } = require('./js/math')
// 函数调用
format()
console.log(sum(2,5))