const foo = require('./foo');
console.log(foo);
console.log('我是高级前端工程师~');
module.exports = {
name: 'quanquan',
job: 'fe',
};
webpack
之后,打包完成,可以看到bundle.js内的代码// modules 即为存放所有模块的数组,数组中的每一个元素都是一个函数
(function(modules) {
// 安装过的模块都存放在这里面
// 作用是把已经加载过的模块缓存在内存中,提升性能
var installedModules = {};
// 去数组中加载一个模块,moduleId 为要加载模块在数组中的 index
// __webpack_require__作用和 Node.js 中 require 语句相似
function __webpack_require__(moduleId) {
// require 模块时先判断是否已经缓存, 已经缓存的模块直接返回
if(installedModules[moduleId]) {
return installedModules[moduleId].exports;
}
// 如果缓存中不存在需要加载的模块,就新建一个模块,并把它存在缓存中
var module = installedModules[moduleId] = {
// 模块在数组中的index
i: moduleId,
// 该模块是否已加载完毕
l: false,
// 该模块的导出值,也叫模块主体内容, 会被重写
exports: {}
};
// 从 modules 中获取 index 为 moduleId 的模块对应的函数
// 再调用这个函数,同时把函数需要的参数传入,this指向模块的主体内容
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
// 将模块标记为已加载
module.l = true;
// 返回模块的导出值,即模块主体内容
return module.exports;
}
// 向外暴露所有的模块
__webpack_require__.m = modules;
// 向外暴露已缓存的模块
__webpack_require__.c = installedModules;
...
...
// Webpack 配置中的 publicPath,用于加载被分割出去的异步代码,这个暂时还没有用到
__webpack_require__.p = "";
// Load entry module and return exports
// 准备工作做完了, require 一下入口模块, 让项目跑起来
// 使用 __webpack_require__ 去加载 index 为 0 的模块,并且返回该模块导出的内容
// index 为 0 的模块就是 index.js文件,也就是执行入口模块
// __webpack_require__.s 的含义是启动模块对应的 index
return __webpack_require__(__webpack_require__.s = 0);
})
/***** 华丽的分割线 上边时 webpack 初始化代码, 下边是我们写的模块代码 *******/
// 所有的模块都存放在了一个数组里,根据每个模块在数组的 index 来区分和定位模块
([
/* 模块 0 对应 index.js */
(function(module, exports, __webpack_require__) {
// 通过 __webpack_require__ 规范导入 foo 函数,foo.js 对应的模块 index 为 1
const foo = __webpack_require__(1);
console.log(foo);
console.log('我是高级前端工程师~');
}),
/* 模块 1 对应 foo.js */
(function(module, exports) {
// 通过 CommonJS 规范导出对象
module.exports = {
name: 'quanquan',
job: 'fe',
};
})
]);
(function(modules) {
// 模拟 require 语句
function __webpack_require__(index) {
return [/*存放所有模块的数组中,第index个模块暴露的东西*/]
}
// 执行存放所有模块数组中的第0个模块,并且返回该模块导出的内容
return __webpack_require__(0);
})([/*存放所有模块的数组*/])
const foo = require('./foo');❎
import foo from './foo';✅
console.log(foo);
console.log('我是高级前端工程师~');
module.exports = {❎
export default {✅
name: 'quanquan',
job: 'fe',
};
(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__.m = modules;
__webpack_require__.c = installedModules;
__webpack_require__.d = function(exports, name, getter) {
if(!__webpack_require__.o(exports, name)) {
Object.defineProperty(exports, name, {
configurable: false,
enumerable: true,
get: getter
});
}
};
__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;
};
__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
__webpack_require__.p = "";
return __webpack_require__(__webpack_require__.s = 0);
})([相关模块]);
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
// 在__webpack_exports__上定义__esModule为true,表明是一个模块对象
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
var __WEBPACK_IMPORTED_MODULE_0__foo__ = __webpack_require__(1);
console.log(__WEBPACK_IMPORTED_MODULE_0__foo__["a"]);
console.log('我是高级前端工程师~');
},
function(module, __webpack_exports__, __webpack_require__) {
"use strict";
__webpack_exports__["a"] = ({
name: 'quanquan',
job: 'fe',
});
}
window.document.getElementById('btn').addEventListener('click', function () {
// 当按钮被点击后才去加载 show.js 文件,文件加载成功后执行文件导出的函数
import(/* webpackChunkName: "show" */ './show').then((show) => {
show('Webpack');
})
});
module.exports = function (content) {
window.alert('Hello ' + content);
};
webpack有个require.ensure api语法来标记为异步加载模块,最新的webpack4推荐使用新的import() api(需要配合@babel/plugin-syntax-dynamic-import插件)。
因为require.ensure是通过回调函数执行接下来的流程,而import()返回promise,这意味着可以使用最新的ES8 async/await语法,使得可以像书写同步代码一样,执行异步流程。
// main.js
import Add from './add'
console.log(Add, Add(1, 2), 123)
// 按需加载
// 方式1: require.ensure
// require.ensure([], function(require){
// var asyncModule = require('./async')
// console.log(asyncModule.default, 234)
// })
// 方式2: webpack4新的import语法
// 需要加@babel/plugin-syntax-dynamic-import插件
let asyncModuleWarp = async () => await import('./async')
console.log(asyncModuleWarp().default, 234)
// async.js
export default function() {
return 'hello, aysnc module'
}
// 0.bundle.js
// 异步模块
// window["webpackJsonp"]是连接多个chunk文件的桥梁
// window["webpackJsonp"].push = 主chunk文件.webpackJsonpCallback
(window["webpackJsonp"] = window["webpackJsonp"] || []).push([
[0], // 异步模块标识chunkId,可判断异步代码是否加载成功
// 跟同步模块一样,存放了{模块路径:模块内容}
{
"./src/async.js": (function(module, __webpack_exports__, __webpack_require__) {
__webpack_require__.r(__webpack_exports__);
__webpack_exports__["default"] = (function () {
return 'hello, aysnc module';
});
})
}
]);
(function(modules) {
// 获取到异步chunk代码后的回调函数
// 连接两个模块文件的关键函数
function webpackJsonpCallback(data) {
var chunkIds = data[0]; //data[0]存放了异步模块对应的chunkId
var moreModules = data[1]; // data[1]存放了异步模块代码
// 标记异步模块已加载成功
var moduleId, chunkId, i = 0, resolves = [];
for(;i < chunkIds.length; i++) {
chunkId = chunkIds[i];
if(installedChunks[chunkId]) {
resolves.push(installedChunks[chunkId][0]);
}
installedChunks[chunkId] = 0;
}
// 把异步模块代码都存放到modules中
// 此时万事俱备,异步代码都已经同步加载到主模块中
for(moduleId in moreModules) {
modules[moduleId] = moreModules[moduleId];
}
// 重点:执行resolve() = installedChunks[chunkId][0]()返回promise
while(resolves.length) {
resolves.shift()();
}
};
// 记录哪些chunk已加载完成
var installedChunks = {
"main": 0
};
// __webpack_require__依然是同步读取模块代码作用
function __webpack_require__(moduleId) {
...
}
// 加载异步模块
__webpack_require__.e = function requireEnsure(chunkId) {
// 创建promise
// 把resolve保存到installedChunks[chunkId]中,等待代码加载好再执行resolve()以返回promise
var promise = new Promise(function(resolve, reject) {
installedChunks[chunkId] = [resolve, reject];
});
// 通过往head头部插入script标签异步加载到chunk代码
var script = document.createElement('script');
script.charset = 'utf-8';
script.timeout = 120;
script.src = __webpack_require__.p + "" + ({}[chunkId]||chunkId) + ".bundle.js"
var onScriptComplete = function (event) {
var chunk = installedChunks[chunkId];
};
script.onerror = script.onload = onScriptComplete;
document.head.appendChild(script);
return promise;
};
var jsonpArray = window["webpackJsonp"] = window["webpackJsonp"] || [];
// 关键代码: window["webpackJsonp"].push = webpackJsonpCallback
jsonpArray.push = webpackJsonpCallback;
// 入口执行
return __webpack_require__(__webpack_require__.s = "./src/main.js");
})
({
"./src/add.js": (function(module, __webpack_exports__, __webpack_require__) {...}),
"./src/main.js": (function(module, exports, __webpack_require__) {
// 同步方式
var Add = __webpack_require__("./src/add.js").default;
console.log(Add, Add(1, 2), 123);
// 异步方式
var asyncModuleWarp =function () {
var _ref = _asyncToGenerator( regeneratorRuntime.mark(function _callee() {
return regeneratorRuntime.wrap(function _callee$(_context) {
// 执行到异步代码时,会去执行__webpack_require__.e方法
// __webpack_require__.e其返回promise,表示异步代码都已经加载到主模块了
// 接下来像同步一样,直接加载模块
return __webpack_require__.e(0)
.then(__webpack_require__.bind(null, "./src/async.js"))
}, _callee);
}));
return function asyncModuleWarp() {
return _ref.apply(this, arguments);
};
}();
console.log(asyncModuleWarp().default, 234)
})
});
webpack实现模块的异步加载有点像jsonp的流程。
源码具体实现流程:
这里的 bundle.js 和上面所讲的 bundle.js 非常相似,区别在于: