Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

webpack 模块化原理 #10

Open
TopGrd opened this issue Jul 10, 2019 · 0 comments
Open

webpack 模块化原理 #10

TopGrd opened this issue Jul 10, 2019 · 0 comments
Labels

Comments

@TopGrd
Copy link
Owner

TopGrd commented Jul 10, 2019

定义 mark.js

export const val = 'sd';

export default () => {
  console.log('jaja');
};

index.js

import mark from './mark';

const name = 'jack';
mark();
export default name;

下面的代码是 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, { enumerable: true, get: getter });
    }
  };

  // define __esModule on exports
  __webpack_require__.r = function(exports) {
    if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
      Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    }
    Object.defineProperty(exports, '__esModule', { value: true });
  };

  // create a fake namespace object
  // mode & 1: value is a module id, require it
  // mode & 2: merge all properties of value into the ns
  // mode & 4: return value when already ns object
  // mode & 8|1: behave like require
  __webpack_require__.t = function(value, mode) {
    if (mode & 1) value = __webpack_require__(value);
    if (mode & 8) return value;
    if (mode & 4 && typeof value === 'object' && value && value.__esModule)
      return value;
    var ns = Object.create(null);
    __webpack_require__.r(ns);
    Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    if (mode & 2 && typeof value != 'string')
      for (var key in value)
        __webpack_require__.d(
          ns,
          key,
          function(key) {
            return value[key];
          }.bind(null, key),
        );
    return ns;
  };

  // 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 = './src/index.js'));
})(
  /************************************************************************/
  {
    /***/ './src/index.js':
      /*!**********************!*\
      !*** ./src/index.js ***!
      \**********************/
      /*! exports provided: default */
      /***/ function(module, __webpack_exports__, __webpack_require__) {
        'use strict';
        eval(
          '__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _mark__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./mark */ "./src/mark.js");\n\n\nconst name = \'jack\';\nObject(_mark__WEBPACK_IMPORTED_MODULE_0__["default"])();\n/* harmony default export */ __webpack_exports__["default"] = (name);\n\n\n//# sourceURL=webpack:///./src/index.js?',
        );

        /***/
      },

    /***/ './src/mark.js':
      /*!*********************!*\
      !*** ./src/mark.js ***!
      \*********************/
      /*! exports provided: default */
      /***/ function(module, __webpack_exports__, __webpack_require__) {
        'use strict';
        eval(
          '__webpack_require__.r(__webpack_exports__);\n/* harmony default export */ __webpack_exports__["default"] = (() => {\n  console.log(\'jaja\')\n});\n\n\n//# sourceURL=webpack:///./src/mark.js?',
        );

        /***/
      },
  },
);

一步一步来看, 整体是个立即执行函数, 精简下如下

(function(modules) {
  // ...
})({
  '.src/index.js': function(module, __webpack_exports__, __webpack_require__) {
    'use strict';
    eval();
    // ...
    //...
  },
});

传入立即执行函数的 modules 对应的就是一个对象,对象的 key 是文件路径,value 是函数,函数参数为 module, webpack_exports, webpack_require. 函数内部是用使用 eval 执行,单独拿出来看下。

eval index

__webpack_require__.r(__webpack_exports__);
/* harmony import */
var _mark__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(
  /*! ./mark */ './src/mark.js',
);
const name = 'jack';
Object(_mark__WEBPACK_IMPORTED_MODULE_0__['default'])();
/* harmony default export */
__webpack_exports__['default'] = name;
//# sourceURL=webpack:///./src/index.js?

eval mark

__webpack_require__.r(__webpack_exports__);
/* harmony export (binding) */
__webpack_require__.d(__webpack_exports__, 'val', function() {
  return val;
});
const val = 'sd';
/* harmony default export */
__webpack_exports__['default'] = () => {
  console.log('jaja');
};
//# sourceURL=webpack:///./src/mark.js?

可以看到,是通过__webpack_require__ 去加载模块,并且将我们的变量 name 赋值给 webpack_exports 的 default 属性上。至于 webpack_require.r 我们稍后分析。

接下来 看看 立即执行函数内部做了什么

// 内部定义的 module 缓存, 存放加载过的模块
var installedModules = {};
function __webpack_require__(moduleId) {
  // 如果加载过, 直接返回 installedModules里对应模块的 exports
  if (installedModules[moduleId]) {
    return installedModules[moduleId].exports;
  }
  // 没有加载过,则创建一个 moudle 变量
  var module = (installedModules[moduleId] = {
    i: moduleId, // i 代表 moduleId,也就是 './src/index.js'
    l: false, // l (loaded) 代表是否加载完毕
    exports: {}, // exports 后面导出的内容
  });

  // 执行 modules 里的方法,就是上面提到过的 立即执行函数传入的 ./src/index 对应的 value
  // function (module, __webpack_exports__, __webpack_require__) {...}
  modules[moduleId].call(
    module.exports,
    module,
    module.exports,
    __webpack_require__,
  );

  // 执行完毕,设置模块对应的 l 为 true
  module.l = true;

  // 返回 module.exports
  return module.exports;
}

eval 里主要使用了 webpack_require.r(webpack_exports)

// 在__webpack_exports__上定义 __esModule 为 true
__webpack_require__.r = function(exports) {
  if (typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
  }
  Object.defineProperty(exports, '__esModule', { value: true });
};

这里的 webpack_exports 就是 webpack_require 函数里的 module.exports

webpack_require.d

__webpack_require__.d = function(exports, name, getter) {
  if (!__webpack_require__.o(exports, name)) {
    Object.defineProperty(exports, name, { enumerable: true, get: getter });
  }
};

通过__webpack_require__.d 对 module.exports 对象上赋值

也就是 eval 里的执行完以后,我们的 module.exports.default = name

分别打印 index.js 和 mark.js 对应的 module

image

image

至于 bundle.js 中的其他内容,都是些辅助函数,暂不分析了。

梳理下 webpack bundle.js 里的流程

  1. 运行立即执行函数,并传入 modules 对象,key 对应文件路径,value 对应 eval 函数
  2. 立即执行函数返回 wepack_require('./src/index.js') 加载入口文件对应模块并返回相应的 exports
  3. webpack_require 里先判断是否加载过,如果加载过直接返回闭包里 installedModules 里对应的模块
  4. 如果没有,则先创建 module 对象,设置 loaded false, exports 为空对象
  5. 执行传入立即执行函数相应模块对应的 eval 方法
  6. eval 去执行 webpack_require 去加载依赖的模块,通过__webpack_require__.d 给 exports 对象赋值, _webpack_require** .r 定义 **esModule 为 true, 直接给 module.exports.default 赋值默认导出。
  7. 模块加载过后,设置 installedModules 里对应模块的 l flag 为 true,返回 module.exports
@TopGrd TopGrd added the webpack label Jul 10, 2019
@TopGrd TopGrd changed the title webpack 打包后的代码分析 webpack 模块化原理 Jul 11, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

No branches or pull requests

1 participant