webpack源码解析四之bundle.js 内容分析

目录
文章目录隐藏
  1. 自执行函数
  2. 模块代码
  3. webpack 工具函数
  4. webpack 对模块依赖的处理流程

我们来分析下 webpack 打包完成的文件内容,我们的源码如下:

a.js

import { log } from './b.js'
log('hello')

b.js

export const log = function (m) {
  console.log(m)
}

export const error = function (m) {
  console.error(m)
}

为了减少干扰,这里我们没有用 babel 编译,如果你看到有 module.exports 而不是 __webpack_exports__ 那是因为你启用了 babel 的 modules 导致的。

总共两个文件,内容非常简单。那么我们用 webpack 进行编译后,输出的文件内容如下:

/******/ (function(modules) { // webpackBootstrap
/******/  // 这里省略一大段
/******/     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__b_js__ = __webpack_require__(1);

Object(__WEBPACK_IMPORTED_MODULE_0__b_js__["a" /* log */])('hello')

/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {

"use strict";
const log = function (m) {
  console.log(m)
}
/* harmony export (immutable) */ __webpack_exports__["a"] = log;

const error = function (m) {
  console.error(m)
}
/* unused harmony export error */


/***/ })
/******/ ]);

为了看代码方便,暂时省略掉一段 webpack 生成的代码。 我们可以看到的是,整个代码是一个自执行的函数,其中这个函数接收的是一个数组,而数组中每一项都是我们的一个模块,这里我们有两个模块。不过这个模块的代码都是被一个函数包裹起来的。

自执行函数

这个自执行函数大概是这样的:

(function (modules) {})([module0, module1])

其中 module0 和 module1 是我们的 a 和 b 两个模块,不过也被一个函数包起来了。这段代码会把我们的模块放入一个数组,传给自执行函数,他来负责调用模块。

模块代码

那么我们的模块就变成了这样:

(function(module, __webpack_exports__, __webpack_require__) {

"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__b_js__ = __webpack_require__(1);

Object(__WEBPACK_IMPORTED_MODULE_0__b_js__["a" /* log */])('hello')

/***/ })

这里函数带了几个参数,因为这些是模块系统需要用到的参数,比如我们的 import 被替换成了 __webpack_require__,那么这个函数就是通过 参数传进来的,所以不会报错。同样, export 被替换成了 __webpack_exports__ 。

webpack 工具函数

下面我们深入看一看前面被我们省略的,webpack 自己生成的一段函数:

(function(modules) { // webpackBootstrap
    // The module cache
    var installedModules = {};

    // The require function
    function __webpack_require__(moduleId) {

        // 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, module, module.exports, __webpack_require__);

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

        // Return the exports of the module
        return module.exports;
    }


    // expose the modules object (__webpack_modules__)
    __webpack_require__.m = modules;

    // expose the module cache
    __webpack_require__.c = installedModules;

    // define getter function for harmony exports
    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, {
                configurable: false,
                enumerable: true,
                get: getter
            });
        }
    };

    // getDefaultExport function for compatibility with non-harmony modules
    __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;
    };

    // Object.prototype.hasOwnProperty.call
    __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };

    // __webpack_public_path__
    __webpack_require__.p = "";

    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 0);
})

直接看起来会比较晕,我们把它缩写一下:

(function(modules) {
    var installedModules = {};
    function __webpack_require__(moduleId) {}
    // Load entry module and return exports
    return __webpack_require__(__webpack_require__.s = 0);
})

这样就比较容易读懂了,这个函数 包含三部分内容

  • 首先声明了一个 installedModules 作为缓存,这样如果发现我们 import 了一个之前已经 import 过的模块,则直接返回。
  • 然后定义了 __webpack_require__ 相关的函数
  • 最后,直接调用了 __webpack_require__(__webpack_require__.s = 0) 也就是直接调用了第 0 个模块,其实就是我们的入口文件,因此我们的整个 JS 代码就从这里开始运行起来。

__webpack_require__ 就是给他一个 moduleId,他会返回这个模块的内容。

function __webpack_require__(moduleId) {
  // 看看有没有缓存,有的话就直接用了
  // 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
  // 然后执行这个模块的代码,参数会传入模块系统相关的几个函数,把拿到的结果放到缓存中
  // 通过把 `module.exports`  传给模块,让他自己把自己放到缓存中。
  modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

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

  // Return the exports of the module
  // 最后得到了模块导出的内容,返回就可以了
  return module.exports;
}

webpack 对模块依赖的处理流程

前面我们讲到了,webpack 会通过 actorn 解析 JS 语法,并对 require(‘xxx.js’) 做依赖收集

依赖关系

假设我们有如下的依赖关系:
webpack 对模块依赖的处理流程
在 bundle.js 内容分析的文章中,我们知道 webpack 最终打包出来的 bundle.js 中会把所有的模块变成一个数组,也就是把一颗树变成了一个数组。那么如果按照上图的依赖关系,最终打包的数组中的模块会是什么顺序呢?

其实从代码中很容易分析出来。因为 webpack 从 entry 开始,对每一个 module 都进行处理,碰到 require 之后就跳入到对应的 module 的处理,也就是递归的对这颗依赖树进行处理,这是典型的深度优先遍历的递归解法,而且是先序优先遍历。处理的过程是这样的

1. 处理 main.js,记录入口 [main]
2. 碰到 require(a),记录 [main, a]
3. 进入到 a 模块,碰到语句 require(c), 记录下来 [main, a, c]
4. 同理碰到 require(d),记录下来 [main, a, c, d]
5. 返回到 main.js,下一句是 require(‘b’),记录下来 [main, a, c, d, b]
6. 进入模块 b,碰到语句 require(e),记录下来[main, a, c, d, b, e]
7. 返回,结束

图解如下:
深度优先遍历的递归解法

「点点赞赏,手留余香」

14

给作者打赏,鼓励TA抓紧创作!

微信微信 支付宝支付宝

还没有人赞赏,快来当第一个赞赏的人吧!

声明:本站所有文章,如无特殊说明或标注,均为本站原创发布。任何个人或组织,在未征得本站同意时,禁止复制、盗用、采集、发布本站内容到任何网站、书籍等各类媒体平台。如若本站内容侵犯了原著者的合法权益,可联系我们进行处理。
码云笔记 » webpack源码解析四之bundle.js 内容分析

发表回复