我们知道不管原来什么模块规范,最后全变成了 commonjs。
所以在浏览器中,我们只需要模拟 commonjs 就行。
我们设计一个打包的例子。
|-dist
| |-index.html //html 打包文件
| |-main.js //html 引用的打包文件
|-src
| |-index.js //入口文件
| |-title.js //入口文件引入的例子
假设 dist 就是打包后的文件夹,src 就是打包前的文件夹。我们今天的目的就是要实现一个 dist 的 main.js
/** ./src/index.js 入口文件 **/
let title = require('./title.js');
console.log(title);
/** ./src/title.js 引用文件 **/
module.exports = 'title';
就这样简简单单的两个文件,打包后的代码是怎么实现的呢。下面来讲解一下。
自执行函数
在 commonjs 规范中,基本都是自执行函数,防止作用域被污染。
;(()=>{
//自执行函数体
//...
})();
声明模块定义
在自执行函数中,必定有一模块对象来存放我们导入的函数体。
这个模块对象符合以下几个特性:
- key 是模块的id,不管什么模块,总的模块 id 都是相对于项目根目录的相对路径。
- commonjs 的文件,在 webpack 打包之后仍然是 commonjs。
value 是一个方法,方法体就是导入的 title.js 中的内容,并且有以下几个入参:
- module 代表当前模块。
- exports = module.exports 代表当前模块的导出对象。
- require 是加载别的模块的加载方法。
;(()=>{ //模块定义 var modules = { // key 是模块的id,不管什么模块,总的模块 id 都是相对于项目根目录的相对路径 // commonjs 的文件,在 webpack 打包之后仍然是 commonjs // module 代表当前模块,exports = module.exports 代表当前模块的导出对象,require 是加载别的模块的加载方法 './src/title.js':(module, exports, require) => { module.exports = 'title'; } } //.... })();
require 方法定义
这个方法实现两个最基本的功能
接收模块id为入参。
- 创建一个新的模块。
- 返回模块的导出对象。
;(()=>{ var modules = { './src/title.js':(module, exports, require) => { module.exports = 'title'; } } function require(moduleId){ // 创建一个新的模块 var module = { exports: {} }; //执行模块定义方法,给导出对象赋值 modules[moduleId](module, module.exports, require); //返回导出对象 return module.exports; } })();
设置缓存
如果模块被加载过了,就不需要再加载了。
接下来就是正文内容;(()=>{ var modules = { './src/title.js':(module, exports, require) => { module.exports = 'title'; } } var cache = {}; function require(moduleId){ //如果缓存中有此模块对应的缓存数据 if(cache[moduleId]){ //直接返回模块的结果 return cache[moduleId].exports } var module = cache[moduleId] = { exports: {} }; modules[moduleId](module, module.exports, require); return module.exports; } })();
;(()=>{ var modules = { './src/title.js':(module, exports, require) => { module.exports = 'title'; } } var cache = {}; function require(moduleId){ //如果缓存中有此模块对应的缓存数据 if(cache[moduleId]){ //直接返回模块的结果 return cache[moduleId].exports; } var module = cache[moduleId] = { exports: {} }; modules[moduleId](module, module.exports, require); return module.exports; } // index.js 中的内容 let title = require('./src/title.js'); console.log(title); })();
结果
运行之后会发现和 webpack 打包后的代码是一样的。
webpack 打包源码
下面是 webpack 实际生成的代码,和我们写的基本上相差不大。我们可以参考下。 ```javascript /**/ (() => { // webpackBootstrap /**/ var webpack_modules = ({
// “./src/title.js”: /!**!\ !** ./src/title.js ! ***/ /*/ ((module) => {
module.exports = ‘title’;
/*/ })
/**/ });
/**/
/**/ // 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_modulesmoduleId;
/**/
/**/ // Return the exports of the module
/**/ return module.exports;
/**/ }
/**/
/**/
var webpack_exports = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {
/!**!\
! ./src/index.js !
**/
let title = webpack_require(/! ./title / “./src/title.js”);
console.log(title)
})();
/**/ })() ; ```