Skip to content

webpack 编译结果分析与实现

我们可以知道,webpack 是可以将我们的代码进行打包,那么打包的结果是如何产生和实现的呢?

  • 首先我们知道,webpack 可以实现我们自主使用 ES6/common.js 的模块化导出方式

我们分析一下,webpack 的打包之后的特性:

  1. 将多个模块合并在一个 main.js 中执行
  2. 多个模块不会产生环境变量的污染

#思路:

  • 在 main.js 合并的文件中,我们可以将多个模块合并到一个对象中
js
//键:模块的路径
//值:函数,这样可以避免了模块之间变量的污染,而函数内部放置模块的代码
//参数:因为在模块代码中,我们用到了module.exports,和require函数,所以我们将这些当作参数传递进来
const module = {
  "./a.js": function (module, exports, require) {
    exports.a = "我是a";
    module.exports = "我是 a 模块";
  },

  "./index.js": function (module, exports, require) {
    const A = require("./a");
    console.log("我是主入口", A);
  },
};
  • 我们需要一个函数来执行我们规整好的模块对象,这里选用了自执行函数
js
/* 
我们可以分析出这个函数需要干什么(什么功能)
1.不论如何都会执行index.js文件

2.需要内部实现一个require函数,因为执行模块靠它
  2.1 require函数功能:
        a.执行模块代码
            --require('路径'),将路径传入,通过路径找到modules中的函数执行
        b.返回模块的导出对象
            --因为模块的函数中需要传入(module,exports,require),所以在require函数内部声明一个module对象,然后当执行模块代码的时候,将module传入,因为在模块代码内部用户会对module重新赋值,所以,直接将module返回出去即可。
            
3.模块代码具有缓存,不会重复执行
   --缓存可以
        {
        	"路径":模块结果
        }
*/
(function (modules) {})(传入整合好的模块对象);

#完整代码

js
//将所有的模块合并成一个对象,模块内包含每个模块的代码
// const module = {
//   "./a.js": function (module, exports, require) {
//     exports.a = "我是a";
//     module.exports = "我是 a 模块";
//   },

//   "./index.js": function (module, exports, require) {
//     const A = require("./a");
//     console.log("我是主入口", A);
//   },
// };

//将模块对象交给一个自执行函数处理

(function (modules) {
  //实现模块缓存,缓存模块导出的结果
  let moduleCache = {};
    
    
  /**实现require函数
   * 1.执行该模块
   * 2.返回模块结果
   * @param {string} moduleID  模块的id标识,即模块路径
   * @returns {object} 模块导出的结果
   */
  function _webpack_require(moduleID) {
    //有缓存的话,直接退出函数
    if (moduleCache[moduleID]) return;

    //创建module对象
    let module = {
      exports: {},
    };
    //1.通过模块路径找到模块函数,并执行
    const fun = modules[moduleID];
    fun(module, module.exports, _webpack_require); //将module传入到模块中,在模块中会将该module重新赋值,所以直接返回结果就是这个module对象
    //2.返回模块导出的结果
    const result = module.exports;
    //3.给模块导出的结果加给缓存
    moduleCache[moduleID] = result;

    return result;
  }
    
    

  // 执行入口文件
  _webpack_require("./index.js");
    
    
})({
    
    
  "./a.js": function (module, exports, _webpack_require) {
    exports.a = "我是a";
    module.exports = "我是 a 模块";
    console.log("我是模块A");
    // eval(
    //   "exports.a = '我是a';\nmodule.exports = '我是 a 模块';\nconsole.log('我是模块A');\n//# sourceURL=webpack:///./src/a.js"
    // );
  },

    
  "./index.js": function (module, exports, _webpack_require) {
    const A = _webpack_require("./a.js");
    console.log("我是主入口");

    // eval(
    //   "const A = _webpack_require('./a.js');\nconsole.log('我是主入口');\n//# sourceURL=webpack:///./src/index.js"
    // );
      
      
  },
});

/*
使用eval()的目的:
1.因为我们使用的是编译之后的代码,如果代码出错,在浏览器只会跳到编译后的位置,而不是源代码的位置,这样就不方便调试了
2.而eval()在执行代码的时候,会单独的将内部的代码开辟出一个空间,这样我们就可以在代码出错的时候进行错误定位了
*/

MIT License