参考链接
IIFE 匿名函数自调用
现代模块实现的基石
数据是私有的, 外部只能通过暴露的方法操作
// module.js文件
// $ 引入依赖
;(function (window, $) {
// 内部私有的函数
function otherFun() {
console.log('otherFun()')
}
let data = 'www.baidu.com'
function foo() {
console.log(`foo() ${data}`)
$('body').css('background', 'red')
}
function bar() {
console.log(`bar() ${data}`)
otherFun() // 内部调用
}
// 暴露行为
window.myModule = { foo, bar }
})(window, jQuery)
引入的js必须有一定顺序
// index.html文件
<script type="text/javascript" src="jquery-1.10.1.js"></script>
<script type="text/javascript" src="module.js"></script>
<script type="text/javascript">
myModule.foo()
</script>
CJS 与 ESM 原理
基础配置
- webpack.config.js
const HtmlWebpackPlugin = require('html-webpack-plugin')
const { CleanWebpackPlugin } = require('clean-webpack-plugin')
const path = require('path')
module.exports = {
mode: 'development',
devtool: 'source-map',
entry: './src/main.js',
output: {
filename: 'index.js',
path: path.resolve(__dirname, 'dist'),
},
plugins: [
new HtmlWebpackPlugin(),
new CleanWebpackPlugin()
]
}
- src/js/format.js
const dateFormat = date => '2020-12-12'
const prtceFormat = price => '100.00'
module.exports = {
dateFormat,
prtceFormat,
}
- src/js/math.js
export const sum = (a, b) => a + b
export const mul = (a, b) => a * b
CommonJS 原理
// src/main.js 文件
const { dateFormat, priceFormat } = require('./js/format')
console.log(dateFormat('abc'))
console.log(priceFormat('abc'))
webpack打包后的源码
CJS 的特点
- 运行时加载
- require() 是同步加载模块
- 输出的是一个值的拷贝
- CommonJS 模块无论加载多少次,都只会在第一次加载时运行一次,以后再加载,就返回第一次运行的结果,除非手动清除系统缓存
;(function () {
var __webpack_modules__ = {
'./src/js/format.js': function (module) {
const dateFormat = date => {
return '2020-12-12'
}
const priceFormat = price => {
return '100.00'
}
// CommonJS 模块输出的是一个值的拷贝
module.exports = {
dateFormat,
priceFormat,
}
},
}
// The module cache
// 缓存模块,只会在第一次加载时运行一次
var __webpack_module_cache__ = {}
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId]
if (cachedModule !== undefined) {
return cachedModule.exports
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {},
})
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
// Return the exports of the module
// 导出 module.exports 对象
return module.exports
}
!(function () {
// CommonJS 模块是运行时加载
// CommonJS 模块的 require() 是同步加载模块
const { dateFormat, priceFormat } =
__webpack_require__('./src/js/format.js')
console.log(dateFormat('1213'))
console.log(priceFormat('1213'))
})()
})()
ESModule 原理
// src/main.js
import { sum, mul } from './js/math'
console.log(mul(20, 30))
console.log(sum(20, 30))
webpack打包后的源码
ESM 特点
- 输出的是值的引用
- 只读属性
- 是编译时输出接口
- ES6 模块内部的代码都只是被转换成了一段静态的代码,只有进入到运行时才会被执行
- 定义模块为 esModule
;(function () {
'use strict'
var __webpack_modules__ = {
'./src/js/math.js': function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
__webpack_require__.r(__webpack_exports__)
// ES6 模块输出的是值的引用
// ES6 模块是编译时输出接口
__webpack_require__.d(__webpack_exports__, {
sum: function () {
return sum
},
mul: function () {
return mul
},
})
// ES6 模块内部的代码都只是被转换成了一段静态的代码,只有进入到运行时才会被执行
const sum = (num1, num2) => {
return num1 + num2
}
const mul = (num1, num2) => {
return num1 * num2
}
},
}
// The module cache
var __webpack_module_cache__ = {}
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId]
if (cachedModule !== undefined) {
return cachedModule.exports
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {},
})
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
// Return the exports of the module
return module.exports
}
/* webpack/runtime/define property getters */
!(function () {
// define getter functions for harmony exports
__webpack_require__.d = function (exports, definition) {
for (var key in definition) {
if (
__webpack_require__.o(definition, key) &&
!__webpack_require__.o(exports, key)
) {
// import 命令输入的变量都是只读的,因为它的本质是输入接口
Object.defineProperty(exports, key, {
enumerable: true,
get: definition[key],
})
}
}
}
})()
/* webpack/runtime/hasOwnProperty shorthand */
!(function () {
__webpack_require__.o = function (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop)
}
})()
/* webpack/runtime/make namespace object */
!(function () {
// define __esModule on exports
// 定义模块为 esModule
__webpack_require__.r = function (exports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
}
Object.defineProperty(exports, '__esModule', { value: true })
}
})()
var __webpack_exports__ = {}
!(function () {
__webpack_require__.r(__webpack_exports__)
var _js_math_js__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__('./src/js/math.js')
console.log(_js_math_js__WEBPACK_IMPORTED_MODULE_0__)
console.log((0, _js_math_js__WEBPACK_IMPORTED_MODULE_0__.sum)(20, 30))
console.log((0, _js_math_js__WEBPACK_IMPORTED_MODULE_0__.mul)(20, 30))
})()
})()
CommonJS & ESModule 混合原理
// src/main.js
// es module导出内容, CommonJS导入内容
const { sum, mul } = require('./js/math')
// CommonJS导出内容, es module导入内容
import { dateFormat, priceFormat } from './js/format'
console.log(sum(20, 30))
console.log(mul(20, 30))
console.log(dateFormat('aaa'))
console.log(priceFormat('bbb'))
webpack打包源码
;(function () {
var __webpack_modules__ = {
'./src/js/format.js': function (module) {
const dateFormat = date => {
return '2020-12-12'
}
const priceFormat = price => {
return '100.00'
}
module.exports = {
dateFormat,
priceFormat,
}
},
'./src/js/math.js': function (
__unused_webpack_module,
__webpack_exports__,
__webpack_require__
) {
'use strict'
__webpack_require__.r(__webpack_exports__)
__webpack_require__.d(__webpack_exports__, {
sum: function () {
return sum
},
mul: function () {
return mul
},
})
const sum = (num1, num2) => {
return num1 + num2
}
const mul = (num1, num2) => {
return num1 * num2
}
},
}
// The module cache
var __webpack_module_cache__ = {}
// The require function
function __webpack_require__(moduleId) {
// Check if module is in cache
var cachedModule = __webpack_module_cache__[moduleId]
if (cachedModule !== undefined) {
return cachedModule.exports
}
// Create a new module (and put it into the cache)
var module = (__webpack_module_cache__[moduleId] = {
// no module.id needed
// no module.loaded needed
exports: {},
})
// Execute the module function
__webpack_modules__[moduleId](module, module.exports, __webpack_require__)
// Return the exports of the module
return module.exports
}
/* webpack/runtime/compat get default export */
!(function () {
// getDefaultExport function for compatibility with non-harmony modules
__webpack_require__.n = function (module) {
var getter =
module && module.__esModule
? function () {
return module['default']
}
: function () {
return module
}
__webpack_require__.d(getter, { a: getter })
return getter
}
})()
/* webpack/runtime/define property getters */
!(function () {
// define getter functions for harmony exports
__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],
})
}
}
}
})()
/* webpack/runtime/hasOwnProperty shorthand */
!(function () {
__webpack_require__.o = function (obj, prop) {
return Object.prototype.hasOwnProperty.call(obj, prop)
}
})()
/* webpack/runtime/make namespace object */
!(function () {
// define __esModule on exports
__webpack_require__.r = function (exports) {
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' })
}
Object.defineProperty(exports, '__esModule', { value: true })
}
})()
var __webpack_exports__ = {}
!(function () {
'use strict'
__webpack_require__.r(__webpack_exports__)
var _js_format__WEBPACK_IMPORTED_MODULE_0__ =
__webpack_require__('./src/js/format.js')
var _js_format__WEBPACK_IMPORTED_MODULE_0___default = __webpack_require__.n(
_js_format__WEBPACK_IMPORTED_MODULE_0__
)
// es module 导出,CommonJs 导入
const { sum, mul } = __webpack_require__('./src/js/math.js')
// CommonJs 导出,es module 导入
console.log(sum(20, 30))
console.log(mul(20, 30))
console.log((0, _js_format__WEBPACK_IMPORTED_MODULE_0__.dateFormat)('1213'))
console.log(
(0, _js_format__WEBPACK_IMPORTED_MODULE_0__.priceFormat)('1213')
)
})()
})()
多chunk加载的原理解析
1. 多个打包入口
生成多个script标签,按顺序导入
2. 分离公共依赖
-
最先加载 venders.js
-
将第三方模块放入全局变量 webpackChunkwebpacktest 中
-
将对应模块加入到 main 作用域的
__webpack_modules__中
3. import() 异步打包原理
// src/main.js
import('./js/math').then(({ mul, sum }) => {
console.log(mul(20, 30))
console.log(sum(20, 30))
})
webpack 打包后的产物
总结过程
- 执行入口文件 index.js 代码。
import()会被替换成webpack的api
- 执行
__webpack_require__.e()返回一个Promise对象,状态是pending
- 执行 webpack_require.l 生成
script标签挂载到页面document.head.appendChild(script) - script标签下载完成执行触发
self['webpackChunkwebpacktest'].push() push()方法被改写,将触发webpackJsonpCallback执行- 懒加载的模块被加入到main的
__webpack_modules__中 - 把 Promise 的状态从 pending 改成
fulfilled执行后续代码
- 懒加载的模块被加入到main的