深入解析Webpack模块机制与v3核心特性

作者:测试手账

时间:2026-10-22

浏览:388

本文通过实际打包案例深入剖析Webpack的模块加载机制,解析CommonJS、ES Modules及AMD等不同规范的兼容原理。重点介绍Webpack 3引入的Scope Hoisting与Magic Comments特性,阐述其如何优化打包体积与代码执行效率,帮助开发者理解底层实现并提升构建性能。

文章目录

Webpack打包原理与模块机制解析

文章通过基础案例解析打包产物,探讨Webpack打包文件结构、多模块化方案兼容机制及Webpack3新增特性。

基础示例

Webpack配置


 // webpack.config.js
module.exports = {
 entry: './src/index.js',
 output: {
 filename: 'bundle.js',
 path: path.resolve(__dirname, 'dist')
 },
};

简易JS文件


 // src/index.js
 console.log('hello world');

Webpack打包结果

直观感受:仅一行代码却生成大量打包内容(疑问表情)。


// dist/bundle.js
 /******/ (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);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

分析该部分代码,精简后整体为自执行函数,传入模块数组。


 (function(modules) { 
  //...
 })([function(module, exports) {
  //..
 }])

模块数组处理逻辑(注释已明确,此处为大致解读)。


 /******/ (function(modules) { // webpackBootstrap
/******/  // The module cache 缓存已经load过的模块
/******/  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, //模块id
/******/    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 为ES6 exports定义getter
/******/  __webpack_require__.d = function(exports, name, getter) {
/******/   if(!__webpack_require__.o(exports, name)) { // 假如exports本身不含有name这个属性
/******/    Object.defineProperty(exports, name, {
/******/     configurable: false,
/******/     enumerable: true,
/******/     get: getter
/******/    });
/******/   }
/******/  };
/******/
/******/  // getDefaultExport function for compatibility with non-harmony modules 解决ES module和Common js module的冲突,ES则返回module['default']
/******/  __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配置下的公共路径
/******/  __webpack_require__.p = "";
/******/
/******/  // Load entry module and return exports 最后执行entry模块并且返回它的暴露内容
/******/  return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports) {
console.log('hello world');
/***/ })
/******/ ]);

整体执行流程。

  • 传入模块数组。
  • 调用__webpack_require__(__webpack_require__.s = 0)。

构建module对象并加入缓存。

执行module,传入对应参数modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);(exports会被函数内部逻辑修改)。

标记module对象加载完成。

返回模块暴露内容(注意函数传入了module.exports,允许修改引用)。

  • 模块函数中传入module, module.exports, __webpack_require__。
  • 执行期间通过修改上述三者引用,实现变量暴露与引用。

多模块化方案兼容原理

Webpack模块机制解析。

查阅官网关于Webpack模块的说明。

doc.webpack-china.org/concepts/mo…

Webpack模块以多种方式表达依赖,示例如下:

  • ES2015 import 语句。
  • CommonJS require() 语句。
  • AMD define 和 require 语句。
  • css/sass/less 文件中的 @import 语句。
  • 样式(url(...))或 HTML 文件()中的图片链接(image url)。

强大的Webpack模块兼容各类模块化方案,且具备非侵入性(non-opinionated)。

通过编写示例进一步验证。

CommonJS

修改src/index.js。


var cj = require('./cj.js');
console.log('hello world');
cj();

新增src/cj.js,保持其他示例不变。


// src/cj.js
function a() {
 console.log("CommonJS");
}
module.exports = a;

再次运行Webpack。


/******/ (function(modules) { // webpackBootstrap
 //... 省略代码
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
let cj = __webpack_require__(1);
console.log('hello world');
cj();
/***/ }),
/* 1 */
/***/ (function(module, exports) {
function a() {
 console.log("CommonJS");
}
module.exports = a;
/***/ })
/******/ ]);

模块数组增加引入文件,index.js模块函数新增__webpack_require__参数以引用文件(__webpack_require__前文已介绍),整体逻辑为依赖模块修改module.exports,主模块执行依赖模块并获取exports。

ES2015 import

新增src/es.js。


// src/es.js
export default function b() {
 console.log('ES Modules');
}

修改src/index.js。


// src/index.js
import es from './es.js';
console.log('hello world');
es();
webpack.config.js不变,执行webpack
/******/ (function(modules) { // webpackBootstrap
// ... 省略代码
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
console.log('hello world');
Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* default */])();
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = b;
function b() {
 console.log('ES Modules');
}
/***/ })
/******/ ]);

均转为严格模式,由Webpack自动应用。

表现与CommonJS相似,传入export并修改,主模块再require引入。

观察此部分。


Object.defineProperty(__webpack_exports__, "__esModule", { value: true });

其作用?标记当前exports为ES模块,回顾之前的__webpack_require__.n,再次查看。


/******/  // getDefaultExport function for compatibility with non-harmony modules 解决ES module和Common js module的冲突,ES则返回module['default']
/******/  __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;
/******/  };

为避免与非ES Modules冲突?冲突点何在?

若查看Babel转换ES Modules源码可知,为兼容模块,将ES Modules直接挂载至exports.default,并添加__esModule属性,引入时判断是否为转换模块,是则引入module['default'],否则引入module。

进一步引入多个ES Modules观察效果。


// src/es.js
export function es() {
 console.log('ES Modules');
}
export function esTwo() {
 console.log('ES Modules Two');
}
export function esThree() {
 console.log('ES Modules Three');
}
export function esFour() {
 console.log('ES Modules Four');
}

多引入esTwo和esFour,但不使用esFour。


// src/index.js
import { es, esTwo, esFour} from './es.js';
console.log('hello world');
es();
esTwo();

结果。


/******/ (function(modules) { // webpackBootstrap
// ...
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
/* harmony import */ var __WEBPACK_IMPORTED_MODULE_0__es_js__ = __webpack_require__(1);
console.log('hello world');
Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["a" /* es */])();
Object(__WEBPACK_IMPORTED_MODULE_0__es_js__["b" /* esTwo */])();
/***/ }),
/* 1 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
/* harmony export (immutable) */ __webpack_exports__["a"] = es;
/* harmony export (immutable) */ __webpack_exports__["b"] = esTwo;
/* unused harmony export esThree */
/* unused harmony export esFour */
function es() {
 console.log('ES Modules');
}
function esTwo() {
 console.log('ES Modules Two');
}
function esThree() {
 console.log('ES Modules Three');
}
function esFour() {
 console.log('ES Modules Four');
}
/***/ })
/******/ ]);

与前述一致,此例重点在于注释内容。


/* unused harmony export esThree */
/* unused harmony export esFour */

esThree为未引入模块,esFour为引用但未使用模块,Webpack均标记为unused。若使用webpack插件uglify,通过标记可消除esThree和esFour未使用代码(即tree-shaking)。

AMD

查看Webpack如何支持AMD。

新增src/amd.js。


// src/amd.js
define([
],function(){
 return {
  amd:function(){
   console.log('AMD');
  }
 };
});

修改index.js。


// src/index.js
define([
 './amd.js'
],function(amdModule){
 amdModule.amd();
});

结果。


/******/ (function(modules) { // webpackBootstrap
// ... 省略代码
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
 __webpack_require__(1)
], __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule){
 amdModule.amd();
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
    __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {
var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;!(__WEBPACK_AMD_DEFINE_ARRAY__ = [
], __WEBPACK_AMD_DEFINE_RESULT__ = function(){
 return {
  amd:function(){
   console.log('AMD');
  }
 };
}.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
    __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && (module.exports = __WEBPACK_AMD_DEFINE_RESULT__));
/***/ })
/******/ ]);

先整理amd.js代码。


function(module, exports, __webpack_require__) {
 var __WEBPACK_AMD_DEFINE_ARRAY__,
  __WEBPACK_AMD_DEFINE_RESULT__;
 !(
  __WEBPACK_AMD_DEFINE_ARRAY__ = [],
  __WEBPACK_AMD_DEFINE_RESULT__ = function() {
   return {
    amd: function() {
     console.log('AMD');
    }
   };
  }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
  __WEBPACK_AMD_DEFINE_RESULT__ !== undefined &&
  (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
 );
})

简述:收集define Array并置入返回函数,根据参数获取依赖。

apply对数组拆解为单独参数。

再看index.js模块部分。


function(module, exports, __webpack_require__) {
 var __WEBPACK_AMD_DEFINE_ARRAY__,
  __WEBPACK_AMD_DEFINE_RESULT__;
 !(
  __WEBPACK_AMD_DEFINE_ARRAY__ = [__webpack_require__(1)],
  __WEBPACK_AMD_DEFINE_RESULT__ = function(amdModule) {
    amdModule.amd();
  }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__),
  __WEBPACK_AMD_DEFINE_RESULT__ !== undefined && 
  (module.exports = __WEBPACK_AMD_DEFINE_RESULT__)
 );
}

即引入amd.js暴露的{amd:[Function: amd]}。

css/image?

css和image亦可成为Webpack模块,令人惊讶,无法通过普通hack CommonJS或函数调用简单实现,此为anything to JS,需借助Webpack loader实现。

如css转换为JS代码,处理后调用时可用JS将CSS插入style,image类似,此处不赘述,感兴趣读者可深入研究。

Webpack 3核心新特性详解

Webpack3新特性

顺便查看Webpack3新特性表现。

具体参考这里medium.com/webpack/web…

Scope Hoisting

模块数组为独立函数并闭包引用Webpack主函数内容,各模块独立,导致浏览器执行速度变慢。Webpack3借鉴Closure Compiler和RollupJS,将所有闭包连接至单一闭包并放入一个函数,提升执行速度并缩小代码体积。

实际查看效果(注意:此特性仅支持ES Modules,不支持CommonJs和AMD)。

使用上述示例,配置webpack.config.js,增加new webpack.optimize.ModuleConcatenationPlugin()。


const path = require('path');
const webpack = require('webpack');
module.exports = {
 entry: './src/index.js',
 output: {
 filename: 'bundle.js',
 path: path.resolve(__dirname, 'dist')
 },
 module: {
 },
 plugins: [
 new webpack.optimize.ModuleConcatenationPlugin(),
 ]
};

打包。


/******/ (function(modules) { // webpackBootstrap
// ... 省略代码
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict";
Object.defineProperty(__webpack_exports__, "__esModule", { value: true });
// CONCATENATED MODULE: ./src/es.js
function es() {
 console.log('ES Modules');
}
function esTwo() {
 console.log('ES Modules Two');
}
function esThree() {
 console.log('ES Modules Three');
}
function esFour() {
 console.log('ES Modules Four');
}
// CONCATENATED MODULE: ./src/index.js
// src/index.js
console.log('hello world');
es();
/***/ })
/******/ ]);

惊喜发现无require,拼接为一个函数,良好!😃。

Magic Comments

code splitting是Webpack重点特性之一,涉及动态引入时,Webpack可使用require.ensure实现,后Webpack2支持符合ECMAScript提案的import()语法,但其不足是无法指定chunk名称chunkName。为解决此问题,出现Magic Comments,支持通过注释指定,如下。


import(/* webpackChunkName: "my-chunk-name" */ 'module');

总结

Webpack为强大模块打包工具,在依赖处理、模块方面表现优异。本文从bundle.js文件分析出发,探索不同模块方案加载机制,初步理解Webpack,并阐述Webpack3特性。当然,Webpack仍有诸多领域需深入探索,敬请期待后续文章~若本文有任何疑问请留言,小编会及时回复。在此衷心感谢大家对本站网站的支持!

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章