浅析webpack 打包模块

2019-01-03  本文已影响7人  kuulid

以下分析都是基于webpack4。

首先写一个最基础的webpack配置工程文件:

const path = require("path");

module.exports = {
    entry: path.join(__dirname, 'index.js'),
    output: {
        path: path.join(__dirname, 'outs'),
        filename: 'index.js'
    },
    mode: 'development'
};

入口文件index.js如下:

import Add from './add';
import math from './math';

console.log(Add(1, 2));

console.log(math.add(1, 2));

两个引用的模块也非常简单:
add.js

export default function(a, b) {
  return a + b;
};

math.js

exports.add = function(a, b) {
  return a + b;
}
exports.mult = function(a, b) {
  return a * b;
}

这两个模块的导出形式也是ES6规定的两种导出形式。下面看一下webpack将文件打包的最终样子:

(function(modules) { // webpackBootstrap
    // 已经加载的模块缓存
    var installedModules = {};
    // webpack的require函数实现
    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__);
        // l变量标示模块是否加载过了
        module.l = true;
        // 返回模块暴露给外面的对象
        return module.exports;
    }
    // 暴露给外接的模块对象 这里的模块对象是所有的模块集合 (__webpack_modules__)
    __webpack_require__.m = modules;
    // 暴露的模块缓存
    __webpack_require__.c = installedModules;
    // 给exports出来的每一个属性都定义一个getter函数
    __webpack_require__.d = function(exports, name, getter) {
        if(!__webpack_require__.o(exports, name)) {
            Object.defineProperty(exports, name, { enumerable: true, get: getter });
        }
    };
    // 给exports对象定义__esModule属性
    __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;
    };
    // 兼容 non-harmony 模块,这些模块如果设了__esModule属性,则被标记为non-harmony
    __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 = "";
    // 先require入口文件
    return __webpack_require__(__webpack_require__.s = "./index.js");
})

/***/ "./add.js":
/*!****************!*\
  !*** ./add.js ***!
  \****************/
/*! exports provided: default */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n\n/* harmony default export */ __webpack_exports__[\"default\"] = (function(a, b) {\n  return a + b;\n});;\n\n\n//# sourceURL=webpack:///./add.js?");

/***/ }),

/***/ "./index.js":
/*!******************!*\
  !*** ./index.js ***!
  \******************/
/*! no exports provided */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
      console.log(__webpack_exports__);
"use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony import */ var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add */ \"./add.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ \"./math.js\");\n/* harmony import */ var _math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_math__WEBPACK_IMPORTED_MODULE_1__);\n\n\n\nconsole.log(Object(_add__WEBPACK_IMPORTED_MODULE_0__[\"default\"])(1, 2));\n\nconsole.log(_math__WEBPACK_IMPORTED_MODULE_1___default.a.add(1, 2));\n\n\n//# sourceURL=webpack:///./index.js?");

/***/ }),

/***/ "./math.js":
/*!*****************!*\
  !*** ./math.js ***!
  \*****************/
/*! no static exports found */
/***/ (function(module, exports) {

eval("\nexports.add = function(a, b) {\n  return a + b;\n}\nexports.mult = function(a, b) {\n  return a * b;\n}\n\n//# sourceURL=webpack:///./math.js?");

/***/ })

/******/ });

代码文件打包后如何加载

再来看看打包后代码文件变成什么样子了,大致的结构就是一个对象,key是文件路径,其实就是moduleId,value是一个函数

(function(module, __webpack_exports__, __webpack_require__) {
"use strict";
eval(' // 模块代码的字符串');
})

什么时候执行呢,如上就是在webpack_require函数加载模块的时候,下面把入口文件的代码字符串拆出来看一下:


__webpack_require__.r(__webpack_exports__);
/* harmony import */ 
var _add__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./add */ "./add.js");
/* harmony import */ 
var _math__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./math */ "./math.js");
/* harmony import */ 
var _math__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_math__WEBPACK_IMPORTED_MODULE_1__);

console.log(Object(_add__WEBPACK_IMPORTED_MODULE_0__["default"])(1, 2));
console.log(_math__WEBPACK_IMPORTED_MODULE_1___default.a.add(1, 2));

首先调用的__webpack_require__.r, 只是定义了exports的属性而已 无关大局。
接下来就是调用webpack的require函数,加载两个依赖的模块。
最后再执行。在这里也可以看到了,webpack将文件的路径作为moduleId。
至此,webpack打包文件大致分析完了。

上一篇 下一篇

猜你喜欢

热点阅读