webpack系列之输出文件分析

栏目: JavaScript · 发布时间: 6年前

内容简介:上一篇文章我们讲了如何使用新建文件

上一篇文章我们讲了如何使用 webpack ,执行打包会在 dist 生成一堆文件,那么 webpack 输出的文件里面到底长啥样呢?用过的人100%看过,大部分的还是压缩混淆后的代码,一般我们不会去关心它,只管当前持续运行正常就行了。今天我们来看看 webpack 输出的文件

配置

安装

> npm inin -y
> cnpm i webpack webpack-cli -D

新建文件

新建文件 webpack.config.js
新建文件夹 src
webpack.config.js

const path = require('path');
module.exports = {
  devtool: 'inline-source-map',
  mode: 'development',
  entry: './src/main.js',
  output: {
    path: path.resolve(__dirname, './dist'),
    filename: 'bundle.js',
  },
};

src 文件夹下,新增三个文件 main.js (入口文件) a.js b.js
main.js

import { A1, A2 } from './a';
import B from './b';
console.log(A1, A2);
B();

a.js

export const A1 = 'a1';
export const A2 = 'a2';

b.js

export default function() {
  console.log('b');
}

打包

npx webpack --config webpack.config.js

然后就会在 dist 下生成一个 bundle.js 文件,接下来开始分析文件

文件分析

首先先来看看大致的结果

(function(modules) {
  function __webpack_require__(moduleId) {
    ...
  }
  ...
  return __webpack_require__(__webpack_require__.s = "./src/main.js");
})({
  "./src/a.js": (function(module, __webpack_exports__, __webpack_require__) {}
  "./src/b.js": (function(module, __webpack_exports__, __webpack_require__) {}
  "./src/main.js": (function(module, __webpack_exports__, __webpack_require__) {}
})

从上面我们可以看到一个立即执行的函数,传递了一个对象,也就是 modules 的值,最终执行了 __webpack_require__ 函数,执行的这个方法其实是我们在 webpack 里面设置的 entry: ./src/main.js ,对象里还有 key./src/a.js./src/b.js ,也就是我们的 a.jsb.js

我们知道最开始执行了 __webpack_require__(__webpack_require__.s = "./src/main.js") 方法,也就是 __webpack_require__("./src/main.js") ,那么这个 __webpack_require__ 方法又做了什么的

原始的 __webpack_require__ 方法

// The module cache
var installedModules = {};
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;
}

现在我在这上面写上注释

// 模块缓存
var installedModules = {};
function __webpack_require__(moduleId) {
  // 首先全局有一个模块对象,最先判断是否存在这个模块,是否做过相应操作,如果有则直接返回当前模块的一个对象,这里的exports其实就是一个对象
  if(installedModules[moduleId]) {
    return installedModules[moduleId].exports;
  }
  // 这里就直接创建了一个模块,并且缓存在全局的模块中,这里重点关注这个exports,
  // i 指的是模块的名称,比如 './src/main.js'
  // l 意思是当前模块是否加载
  // exports 就是返回出去的对象内容
  var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
  };
  // 到这里就开始通过key去执行 modules(就是刚开始立即执行函数传过来的对象)对象的方法
  // 然后使用call来指向 对象的方法 的this,并且把 module, module.exports, __webpack_require__ 三个值传过去,
  // 这里先做预告,module这个参数传过去其实是没有用到的,主要使用 module.exports 对象, 以及__webpack_require__方法
  modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  // 上面说到有一个参数 l,到这里当前模块已经加载
  module.l = true;
  // 最后把module.exports这个模块返回出去
  return module.exports;
}

接下来我们看看主入口 ./src/main.js 这个 key 的值的内容

./src/main.js ,原内容是这样的,接下来来解释一下

{
  "./src/main.js": (function(module, __webpack_exports__, __webpack_require__) {
    "use strict";
    __webpack_require__.r(__webpack_exports__);
    /* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./src/a.js");
    /* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ "./src/b.js");

    console.log(_a__WEBPACK_IMPORTED_MODULE_0__["A1"], _a__WEBPACK_IMPORTED_MODULE_0__["A2"]);
    Object(_b__WEBPACK_IMPORTED_MODULE_1__["default"])();

  })
}
{
  "./src/main.js": (function(module, __webpack_exports__, __webpack_require__) {
    // 上面我们说到 __webpack_require__ 方法内部会执行 modules[moduleId].call,并传递了三个参数,那么他执行的方法就是这个内部方法

    // 这段代码可以先忽略,在当前项目没有作用
    __webpack_require__.r(__webpack_exports__);

    // 我们看到下面有两段__webpack_require__函数代码的执行,你可以回顾一下main.js的内容,我们是不是做了 import { A1, A2 } from './a';并且 console.log(A1, A2);
    /* harmony import */ var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./src/a.js");
    /* harmony import */ var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ "./src/b.js");
    console.log(_a__WEBPACK_IMPORTED_MODULE_0__["A1"], _a__WEBPACK_IMPORTED_MODULE_0__["A2"]);

    // 这段代码其实是 import B from './b'; B();代码的执行
    Object(_b__WEBPACK_IMPORTED_MODULE_1__["default"])();

  })
}

好了,现在来分析一下 __webpack_require__("./src/a.js") 做了哪些操作,我们先来看看 模块 ./src/a.js 的内容

./src/a.js

{
  "./src/a.js": (function(module, __webpack_exports__, __webpack_require__) {
      __webpack_require__.r(__webpack_exports__);
      /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "A1", function() { return A1; });
      /* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, "A2", function() { return A2; });
      const A1 = 'a1';
      const A2 = 'a2';
 }),
}

代码内容很简单,首先 __webpack_require__("./src/a.js") 执行之后,会创建一个模块,然后去执行模块 ./src/a.js 内部得方法,也是是上面这段,执行完成之后最终会把 module.exports 返回处理,

那么 module.exports 这个是什么内容呢?

看看 webpack_require 内部

var module = installedModules[moduleId] = {
    i: moduleId,
    l: false,
    exports: {}
  };
  // Execute the module function
  modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
  return module.exports

其实就是我们创建模块时的 exports ,然后执行到了模块 ./src/a.js 内部得方法,它内部方法关键的地方在于又调用了 __webpack_require__.d 方法

__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(__webpack_exports__, "A1", function() { return A1; }) ; 可以看出来他给 module.exports 定义了一个 key"key" ,然后取值 get 的时候返回的是 A1(也就是a1)

所以最终 return module.exports 的值为 {A1: 'a1'}

我们回到 ./src/main.js 模块,所以这段代码: var _a__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./a */ "./src/a.js") ;

_a__WEBPACK_IMPORTED_MODULE_0__ 其实就是等于 {A1: 'a1'} console.log(_a__WEBPACK_IMPORTED_MODULE_0__["A1"]) ,取值为 a1A2 同理

webpack系列之输出文件分析

接下来我们看看模块 ./src/b.js ,在主模块它做了什么呢?看看 ./src/main.js

...
var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ "./src/b.js");
Object(_b__WEBPACK_IMPORTED_MODULE_1__["default"])();

看上面模块 ./src/b.js 在主模块的执行第一段代码和之前介绍的一样,第二段代码直接执行了一个方法,这里我们可以猜测出 _b__WEBPACK_IMPORTED_MODULE_1__ 其实就是一个对象,访问了它的 key: default
然后它的key值其实是一个函数,最后执行了这个函数

同理我们可以看看模块 ./src/b.js 内部的方法,以及我们在 src 文件夹下的 b.js 是怎么写的

模块 ./src/b.js

{
  "./src/b.js": (function(module, __webpack_exports__, __webpack_require__) {
    __webpack_require__.r(__webpack_exports__);
    /* harmony default export */ __webpack_exports__["default"] = (function() {
      console.log('b');
  });
}

b.js

export default function() {
  console.log('b');
}

从我们源代码看出,我们是直接导出了一个方法,内部执行了打印字符串 b ,然后再来看看 webapck 的源码部分, __webpack_require__.r(__webpack_exports__) ; 这段可以忽略,解释一下,其实这段代码在对象里定义了一个 __esModule: true ,接着看下面一段,我们从之前讲的知道知道 __webpack_exports__ 其实就是一个单纯的空对象(其实不是,执行了 __webpack_require__.r(__webpack_exports__ )就变成了 {__esModule: true} ),然后它又在对象里增加了一个 default 属性,然后把一个方法赋值给它(其实就是我们打包之前写的一个方法),最终在主入口里执行的模块 var _b__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(/*! ./b */ "./src/b.js") ;其实就等于 {"default": function{}} ,然后下一步执行访问对象 default 的值去执行函数

webpack系列之输出文件分析

思考

可能现在你可能会思考 ,通过上面的比较可以得出一个结论

通过 export 出来,如果 import {a,b,c} from '..' ,打包出来的代码执行简单操作之后(执行 __webpack_require__ 函数)首先会是一个对象,对象会是 {a: ..., b: ..., c: ...}

同过 export default 出来,如果 import a from '..' ,打包处理的代码执行简单操作之后(执行 __webpack_require__ 函数)首先会是一个对象,然后会往对象里添加一个default的key,类似 {default: ...}

总结

整个过程还是挺绕的,你可以自己去 debugger 看看他的执行过程,应该就明白得差不多了,今天就讲了这些吧


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Nature of Code

The Nature of Code

Daniel Shiffman / The Nature of Code / 2012-12-13 / GBP 19.95

How can we capture the unpredictable evolutionary and emergent properties of nature in software? How can understanding the mathematical principles behind our physical world help us to create digital w......一起来看看 《The Nature of Code》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具