zoukankan      html  css  js  c++  java
  • .2-浅析webpack源码之打包后文件

      先不进源码,分析一下打包后的文件,来一张图:

      

      首先创建两个JS文件,内容如下:

    // config.js
    module.exports = {
        entry: './input.js',
        output: {
            filename: 'output.js'
        }
    }
    // input.js
    console.log('input')

      分别为配置文件和入口JS文件,内容弄个简单的。

      接下来在当前目录执行webpack --config config.js,会输出一个output.js,简化后内容如下:

    (function(modules) { // webpackBootstrap
        // 模块缓存对象
        var installedModules = {};
    
        function __webpack_require__(moduleId) {
    
            // 加载入口JS
    
            // 输出
            return module.exports;
        }
    
        // 挂载模块数组
        __webpack_require__.m = modules;
        // ...
        // 在__webpack_require__挂载多个属性
    
        // 传入入口JS模块ID执行函数并输出模块
        return __webpack_require__(__webpack_require__.s = 0);
    });
    // 包含所有模块的数组
    ([
        /* id为0 */
        (function(module, exports) {
            console.log('1')
        })
    ]);

      可以看到,这是一个IIFE,可以利用闭包来对模块进行缓存以及其余便利性的功能。

      整个JS可以分为三块:

    1、传入包含所有模块的数组,每一个模块有唯一的标识ID

    2、模块输出函数

    3、在函数上挂载多个属性

    __webpack_require__
      
      函数代码如下:
    // 传入入口文件的模块ID
    function __webpack_require__(moduleId) {
        // 检测缓存
        if (installedModules[moduleId]) {
            return installedModules[moduleId].exports;
        }
        // 无缓存时创建新的模块
        // i => 模块ID
        // l => 是否被加载
        // 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;
    }

      入口文件的模块ID会当成参数传入该函数,首先会进行缓存检测,没找到会生成一个新的模块对象,然后执行入口模块,将该模块标记为已加载后返回对应的exports。

      由于被打包的文件内容十分简单,所以只会执行console语句。

    __webpack_require__属性挂载

      由于函数也是对象,所以可以在上面添加属性,具体代码如下:

    // 模块数组
    __webpack_require__.m = modules;
    // 模块缓存
    __webpack_require__.c = installedModules;
    // 属性判断
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    // 如果不存在对应属性 定义输出的getter
    __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_public_path__
    __webpack_require__.p = "";

      挂载了6个属性,模块组、缓存对象、属性检测方法、getter定义、默认模块、公共路径.

      这里的getter稍微解释一下,module的引入模块有两种输出模式,一种是webpack定义的module.exports,另一种是ES定义的export default,这里就是判断是哪一种方式。默认情况下当然是module.exports获取模块输出内容。但是用ES的方式,模块输出会被包裹在一个default对象,此时需要用module['default']来获取。

      接下来弄复杂一些再观察,配置文件不变,改变入口JS并添加一个依赖JS:

    // require,js
    module.exports = function require() {
        console.log('require');
    }
    // input.js
    import rq from "./require.js"
    rq();

      简单的实现一个JS加载另外一个JS,接下来执行打包。

      打包后主处理函数不会变,传入的模块数组发生了变化,代码如下:

    (function(modules) {
        // ...
        return __webpack_require__(__webpack_require__.s = 0);
    })
    ([
        /* 0 */
        (function(module, __webpack_exports__, __webpack_require__) {
            "use strict";
            Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
            /* harmony import */
            var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1);
            /* harmony import */
            var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__);
            __WEBPACK_IMPORTED_MODULE_0__require_js___default()()
        }),
        /* 1 */
        (function(module, exports) {
            module.exports = function require() {
                console.log('require');
            }
        })
    ]);

      这里的模块数组出现了2个,但是入口的JS仍然只有1个,ID为0,被依赖的ID为1。

      可以注意到,module、exports分别对应函数的2个参数,而在主处理函数中exports就是输出的模块内容,所以这就是为什么打包函数都是通过module.exports来输出模块内容。

      这里仔细看一下ID为0的入口模块:

    /* 0 */
    (function(module, __webpack_exports__, __webpack_require__) {
        "use strict";
        // 定义该模块的exports的__esModule属性为true
        // 该属性影响__webpack_require__.n的输出
        Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
        // 获取ID为1的模块
        var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1);
        // 这里传入了模块1并调用了__webpack_require__.n
        // 模块1并没有被标记__esModule
        // 返回函数function getModuleExports() { return module; };
        var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__);
        // 注意这里的module并不是模块的module对象 而是传入函数n的参数
        // 所以这里的调用相当于__webpack_require__(1)() => (function require() {console.log('require');})()
        __WEBPACK_IMPORTED_MODULE_0__require_js___default()()
    }),

      由于案例比较简单,所以虽然变量名很长,但是也非常好懂,引入依赖JS后,判断模块的输出模式,然后执行输出代码。

      暂时先写这么多。

  • 相关阅读:
    简单介绍 CPU 的工作原理
    Converting a MatchCollection to string array
    Ubuntu18.04安装pycharm如何显示锁定图标到收藏夹
    MFC使用控制台
    将CString输出到控制台的方法
    Python 字符串前面加u,r,b的含义
    Converting a MatchCollection to string array
    git命令查看版本记录
    git命令查看版本记录
    SQL Server游标
  • 原文地址:https://www.cnblogs.com/QH-Jimmy/p/8018965.html
Copyright © 2011-2022 走看看