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打包bundle.js依赖分析 #64

Open
youngwind opened this issue Apr 18, 2016 · 3 comments
Open

webpack打包bundle.js依赖分析 #64

youngwind opened this issue Apr 18, 2016 · 3 comments

Comments

@youngwind
Copy link
Owner

youngwind commented Apr 18, 2016

问题

用了webpack有一段时间了,其实对webpack打包出来的bundle.js里面到底是什么,怎么实现的不了解,只是知道里面有一坨东西。恰好有同事研究到了这个,觉得非常有意思,所以自己也来研究一下。

一个入口,一个文件

// webpack.config.js
module.exports = {
  entry:'./index.js',
  output:{
    filename:'bundle.js'
  }
};
// index.js
console.log('index');
// 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] = {
/******/            exports: {},
/******/            id: moduleId,
/******/            loaded: false
/******/        };

/******/        // Execute the module function
/******/        modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

/******/        // Flag the module as loaded
/******/        module.loaded = 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;

/******/    // __webpack_public_path__
/******/    __webpack_require__.p = "";

/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports) {

    console.log('index');

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

分析:
1、 整个bundle.js其实是一个自执行表达式,传入参数是一个数组,数组的第一项是一个function,function里面是原先index.js里面真正的内容。
2、 IIFE里面有闭包,__webpack_require_是模块加载函数,接收模块id(对,webpack中每个模块都会有一个独一无二的id,其实也就是在IIFE传参数组中的索引值(0,1,2.....)
3、真正执行module index里面语句的是个调用

// Execute the module function
modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

我们下面来看看webpack这么做是什么意思。

一个入口,两个文件,A依赖B

// a.js
var b = require('./b.js');

console.log('a');

b.b1();
// b.js
exports.b1 = function () {
  console.log('b1')
};

exports.b2 = function () {
  console.log('b2')
};
// bundle.js
/******/ (function(modules) { // webpackBootstrap
// 省略一大段...........
/******/    // Load entry module and return exports
/******/    return __webpack_require__(0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var b = __webpack_require__(1);

    console.log('a');

    b.b1();


/***/ },
/* 1 */
/***/ function(module, exports) {

    exports.b1 = function () {
      console.log('b1')
    };

    exports.b2 = function () {
      console.log('b2')
    };

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

分析:

  1. 由于有两个文件,所以IIFE得参数为长度是2的数组,并按照require的顺序排列。
  2. IIFE函数体部分是一模一样的,所以在这儿就先省略了。
  3. module a发生了变化。因为a依赖b,所以在a中调用webpack加载模块的函数
// 1是模块b的id
var b = __webpack_require__(1);

4.我们再来分析一下

modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);

使用call是因为为了确保每个module中的this指向的是module本身。然后给它传__webpack_require函数是想让module有加载其他module的能力。

两个入口,两个出口

下面我们讨论:A包含B和C,D包含C。
我们看看最后build出来的两个bundle.js有什么不同。
2016-04-18 9 11 37

// bundle.a.js
// 省略相同的部分
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var b = __webpack_require__(1);
    var c = __webpack_require__(2);

    b.b1();

    console.log('a');

/***/ },
/* 1 */
/***/ function(module, exports) {

    exports.b1 = function () {
      console.log('b1')
    };

    exports.b2 = function () {
      console.log('b2')
    };

/***/ },
/* 2 */
/***/ function(module, exports) {

    exports.c1 = function () {
      console.log('c1')
    };

    exports.c2 = function () {
      console.log('c2')
    };

/***/ }
/******/ ]);
// bundle.b.js
/******/ ([
/* 0 */
/***/ function(module, exports, __webpack_require__) {

    var c = __webpack_require__(2);

    c.c2();

    console.log('d');

/***/ },
/* 1 */,
/* 2 */
/***/ function(module, exports) {

    exports.c1 = function () {
      console.log('c1')
    };

    exports.c2 = function () {
      console.log('c2')
    };

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

分析:

  1. 我们从这儿可以看到,两个入口文件a和d的module id都是0,所以可以猜测每个入口文件对应的module id都是0。
  2. bundle.b.js没有id为1的module,为什么呢?因为每个module id在全局都是唯一的,从上面的图片我们可以看到,id为1的是moduleb,而modulec不包含b,所以1那儿就是空的。

问题:这种情况下有个问题,那就是module c在两个bundle.js被重复包含了,怎么提取出来呢?
// 未完待续

@youngwind youngwind changed the title webpack打包bundle.js分析 webpack打包bundle.js依赖分析 Apr 18, 2016
@F3n67u
Copy link

F3n67u commented Mar 28, 2017

Hello,少峰。麻烦您添加一下最后一个章节两个入口,两个出口的源代码,谢谢。
另外,文章中有个小错误:两个入口,两个出口的第二个代码段应该是 bundle.d.js,而不是 bundle.b.js`

@lynnic26
Copy link

lynnic26 commented Aug 5, 2017

两个入口,两个出口得配置代码也贴一下吧

@aaawhz
Copy link

aaawhz commented Oct 11, 2017

闭包没看懂

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

4 participants