webpack4打包文件结果原理解析

首先上结论,简单来说就是:

  • 把解析的所有模块变成一个对象
  • 通过一个微入口去按顺序进行加载
  • 依次实现一个递归依赖关系
  • 通过入口对应的value(方法)去执行文件

一、准备工作

1-首先准备需要打包的demo :

a.js

module.exports = "学习webpack"  

Index.js

let str = require('./a.js')  
console.log(str);  

可以看出,代码的结果是打印 ”学习webpack

 

2- 在package.json中进行以下打包指令配置

"scripts": {  
 "build": "webpack --config webpack.config.my.js"  
},  

准备工作做好后就可以执行 npm run build 进行打包了

 

二、分析文件

 

dist目录(打包生成目录)中可以看到生成的bundle.js文件,原文件比较大,精简后可得到以下结果:

现在我们来分析这个文件的打包原理:

(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;
    }

    return __webpack_require__((__webpack_require__.s = "./src/index.js"));
})({
    "./src/a.js":
        function (module, exports) {
            eval(
                'module.exports = "学习webpack"\n\n//# sourceURL=webpack:///./src/a.js?'
            );
        },
    "./src/index.js":
        function (module, exports, __webpack_require__) {
            eval(
                'let str = __webpack_require__(/*! ./a.js */ "./src/a.js")\r\n\r\nconsole.log(str);\r\n\n\n//# sourceURL=webpack:///./src/index.js?'
            );
        }
});

 

1. 看整体首先是一个匿名自执行函数,这是webpack启动函数

 

 

2.执行的时候会传一个对象,对象有几部分,第一部分是我们的key第二部分是我们的value ;

key -> 当前模块的路径 : value->函数, 将此对象传入module中

({
    "./src/a.js":
        function (module, exports) {
            eval(
                'module.exports = "学习webpack"\n\n//# sourceURL=webpack:///./src/a.js?'
            );
        },
    "./src/index.js":
        function (module, exports, __webpack_require__) {
            eval(
                'let str = __webpack_require__(/*! ./a.js */ "./src/a.js")\r\n\r\nconsole.log(str);\r\n\n\n//# sourceURL=webpack:///./src/index.js?'
            );
        }
}); 

 

3.先定义一个缓存,模块加载完了从缓存中拿 :

  var installedModules = {};
  /* 一个对象关系,例如 
  key -> "./src/index.js" 
  value -> {
      i: moduleId,
      l: false,
      exports: {}
    }
  */

 

4. 实现一个require方法(require是不能在浏览器中运行的) :

  function __webpack_require__(moduleId) {  // "./src/index.js"
    // Check if module is in cache - 检查模块是否在缓存中
    if (installedModules[moduleId]) { 
      //如果在直接就返回了
      return installedModules[moduleId].exports;
    }
    //  安装这个模块
    // Create a new module (and put it into the cache)
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    });

    // Execute the module function
    // 从参数对象中找到那个函数并执行
    modules[moduleId].call(
      module.exports, // this 指向
      module, // 模块
      module.exports, // 模块空对象
      __webpack_require__ // 前面的 require 方法
    );

    // Flag the module as loaded
    module.l = true;

    // Return the exports of the module - 返回的结果
    return module.exports;
  }

 

5. 在启动函数的结尾去调用 require 方法,

并传入 在webpack.config.js配置文件中设定的入口模块, 这里是 "./src/index.js"

 

 

6. 调用参数中入口模块对应的函数方法

 

 

7. 可以看到该方法中存在一次递归关系,将 "./src/a.js" 再次传入require方法中找到以下函数,重走逻辑,找到以下方法:

 

 

最后附上全注释版本:

//webpack打包出的文件解析

/*
  把解析的所有模块变成一个对象
  通过一个微入口去按顺序进行加载
  依次实现一个递归依赖关系
  通过入口对应的value(方法)去执行文件
*/

//自执行函数 
//对象当作参数传入整个 modules 里
(function(modules) {
  // webpackBootstrap - webpack启动函数
  // The module cache - 先定义一个缓存,模块加载完了从缓存中拿
  var installedModules = {};
  /* 一个对象关系,例如 
  key -> "./src/index.js" 
  value -> {
      i: moduleId,
      l: false,
      exports: {}
    }
  */

  // The require function - 实现一个require方法(require是不能在浏览器中运行的)
  function __webpack_require__(moduleId) {  // "./src/index.js"
    // Check if module is in cache - 检查模块是否在缓存中
    if (installedModules[moduleId]) { 
      //如果在直接就返回了
      return installedModules[moduleId].exports;
    }
    //  安装这个模块
    // Create a new module (and put it into the cache)
    var module = (installedModules[moduleId] = {
      i: moduleId,
      l: false,
      exports: {}
    });

    // Execute the module function
    // 从参数对象中找到那个函数并执行
    modules[moduleId].call(
      module.exports, // this 指向
      module, // 模块
      module.exports, // 模块空对象
      __webpack_require__ // 前面的 require 方法
    );

    // Flag the module as loaded
    module.l = true;

    // Return the exports of the module - 返回的结果
    return module.exports;
  }

  // Load entry module and return exports
  return __webpack_require__((__webpack_require__.s = "./src/index.js")); //传入入口模块
})({
  //key -> 当前模块的路径 : value->函数  
  "./src/a.js": function(module, exports) {
    eval(
      // 最终的返回结果就是 "学习webpack"
      'module.exports = "学习webpack"\n\n//# sourceURL=webpack:///./src/a.js?'
    );
  },

  "./src/index.js": function(module, exports, __webpack_require__) {
    eval(
      // 再次找到 "./src/a.js" 传入__webpack_require__方法 重新再走流程,
      // 最终的返回结果就是 "学习webpack",将它赋值给 str 通过console.log()打印
      'let str = __webpack_require__("./src/a.js")\r\n\r\nconsole.log(str);\r\n\n\n//# sourceURL=webpack:///./src/index.js?'
    );
  }
});

 

posted @ 2020-02-10 09:35  言叶以上  阅读(556)  评论(0编辑  收藏  举报