博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
.2-浅析webpack源码之打包后文件
阅读量:4656 次
发布时间:2019-06-09

本文共 4642 字,大约阅读时间需要 15 分钟。

  先不进源码,分析一下打包后的文件,来一张图:

  

  首先创建两个JS文件,内容如下:

// config.jsmodule.exports = {    entry: './input.js',    output: {        filename: 'output.js'    }}
// input.jsconsole.log('input')

  分别为配置文件和入口JS文件,内容弄个简单的。

  接下来在当前目录执行webpack --config config.js,会输出一个output.js,简化后内容如下:

(function(modules) { // webpackBootstrap    // 模块缓存对象    var installedModules = {};    function __webpack_require__(moduleId) {        // 加载入口JS        // 输出        return module.exports;    }    // 挂载模块数组    __webpack_require__.m = modules;    // ...    // 在__webpack_require__挂载多个属性    // 传入入口JS模块ID执行函数并输出模块    return __webpack_require__(__webpack_require__.s = 0);});// 包含所有模块的数组([    /* id为0 */    (function(module, exports) {        console.log('1')    })]);

  可以看到,这是一个IIFE,可以利用闭包来对模块进行缓存以及其余便利性的功能。

  整个JS可以分为三块:

1、传入包含所有模块的数组,每一个模块有唯一的标识ID

2、模块输出函数

3、在函数上挂载多个属性

 

__webpack_require__
  
  函数代码如下:
// 传入入口文件的模块IDfunction __webpack_require__(moduleId) {    // 检测缓存    if (installedModules[moduleId]) {        return installedModules[moduleId].exports;    }    // 无缓存时创建新的模块    // i => 模块ID    // l => 是否被加载    // exports => 输出内容    var module = installedModules[moduleId] = {        i: moduleId,        l: false,        exports: {}    };    // 执行模块函数    modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);    // 标记模块已被加载    module.l = true;    // 返回模块输出    return module.exports;}

  入口文件的模块ID会当成参数传入该函数,首先会进行缓存检测,没找到会生成一个新的模块对象,然后执行入口模块,将该模块标记为已加载后返回对应的exports。

  由于被打包的文件内容十分简单,所以只会执行console语句。

 

__webpack_require__属性挂载

 

  由于函数也是对象,所以可以在上面添加属性,具体代码如下:

// 模块数组__webpack_require__.m = modules;// 模块缓存__webpack_require__.c = installedModules;// 属性判断__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };// 如果不存在对应属性 定义输出的getter__webpack_require__.d = function(exports, name, getter) {    if (!__webpack_require__.o(exports, name)) {        Object.defineProperty(exports, name, {            configurable: false,            enumerable: true,            get: getter        });    }};// 默认模块__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;};// __webpack_public_path____webpack_require__.p = "";

  挂载了6个属性,模块组、缓存对象、属性检测方法、getter定义、默认模块、公共路径.

  这里的getter稍微解释一下,module的引入模块有两种输出模式,一种是webpack定义的module.exports,另一种是ES定义的export default,这里就是判断是哪一种方式。默认情况下当然是module.exports获取模块输出内容。但是用ES的方式,模块输出会被包裹在一个default对象,此时需要用module['default']来获取。

 

  接下来弄复杂一些再观察,配置文件不变,改变入口JS并添加一个依赖JS:

// require,jsmodule.exports = function require() {    console.log('require');}
// input.jsimport rq from "./require.js"rq();

  简单的实现一个JS加载另外一个JS,接下来执行打包。

  打包后主处理函数不会变,传入的模块数组发生了变化,代码如下:

(function(modules) {    // ...    return __webpack_require__(__webpack_require__.s = 0);})([    /* 0 */    (function(module, __webpack_exports__, __webpack_require__) {        "use strict";        Object.defineProperty(__webpack_exports__, "__esModule", { value: true });        /* harmony import */        var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1);        /* harmony import */        var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__);        __WEBPACK_IMPORTED_MODULE_0__require_js___default()()    }),    /* 1 */    (function(module, exports) {        module.exports = function require() {            console.log('require');        }    })]);

  这里的模块数组出现了2个,但是入口的JS仍然只有1个,ID为0,被依赖的ID为1。

  可以注意到,module、exports分别对应函数的2个参数,而在主处理函数中exports就是输出的模块内容,所以这就是为什么打包函数都是通过module.exports来输出模块内容。

  这里仔细看一下ID为0的入口模块:

/* 0 */(function(module, __webpack_exports__, __webpack_require__) {    "use strict";    // 定义该模块的exports的__esModule属性为true    // 该属性影响__webpack_require__.n的输出    Object.defineProperty(__webpack_exports__, "__esModule", { value: true });    // 获取ID为1的模块    var __WEBPACK_IMPORTED_MODULE_0__require_js__ = __webpack_require__(1);    // 这里传入了模块1并调用了__webpack_require__.n    // 模块1并没有被标记__esModule    // 返回函数function getModuleExports() { return module; };    var __WEBPACK_IMPORTED_MODULE_0__require_js___default = __webpack_require__.n(__WEBPACK_IMPORTED_MODULE_0__require_js__);    // 注意这里的module并不是模块的module对象 而是传入函数n的参数    // 所以这里的调用相当于__webpack_require__(1)() => (function require() {console.log('require');})()    __WEBPACK_IMPORTED_MODULE_0__require_js___default()()}),

  由于案例比较简单,所以虽然变量名很长,但是也非常好懂,引入依赖JS后,判断模块的输出模式,然后执行输出代码。

 

  暂时先写这么多。

转载于:https://www.cnblogs.com/QH-Jimmy/p/8018965.html

你可能感兴趣的文章
2.抽取代码(BaseActivity)
查看>>
夏天过去了, 姥爷推荐几套来自smashingmagzine的超棒秋天主题壁纸
查看>>
反射的所有api
查看>>
css 定位及遮罩层小技巧
查看>>
[2017.02.23] Java8 函数式编程
查看>>
sprintf 和strcpy 的差别
查看>>
JS中window.event事件使用详解
查看>>
ES6深入学习记录(一)class方法相关
查看>>
C语言对mysql数据库的操作
查看>>
INNO SETUP 获得命令行参数
查看>>
HTML5与CSS3权威指南之CSS3学习记录
查看>>
docker安装部署
查看>>
AVL树、splay树(伸展树)和红黑树比较
查看>>
多媒体音量条显示异常跳动
查看>>
运算符及题目(2017.1.8)
查看>>
React接入Sentry.js
查看>>
ssh自动分发密匙脚本样板
查看>>
转 小辉_Ray CORS(跨域资源共享)
查看>>
Linux安装postgresql
查看>>
MyBatis启动:MapperStatement创建
查看>>