7

webpack核心理念浅析

 3 years ago
source link: https://segmentfault.com/a/1190000040016711
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

webpack核心理念浅析

这段时间在琢磨自己手写一个构建工具,不过在这之前,我们先试着梳理一下webpack的理念,毕竟它山之石可以攻玉,有助于我们想好该如何设计自己的项目。

Webpack is a static module bundler for modern JavaScript applications.
这是关于webpack的介绍,本质上,他就是一个打包器。将js模块打包成一个或者多个。实现如下代码里的能力:

a.js + b.js + c.js. => bundle.js

打包器出现的原因,在此不多讲,网上有很多文章都讲过了。接下来我们讲一讲webpack把多个js文件打包成一个bundle文件的核心思路是什么。

从构建产物反推核心流程

Webpack 是一个庞大的体系,如果从源码的角度,分析从头到尾的流程,那么就会过于复杂,也没必要。毕竟我们学习的主要是核心设计理念。
那么,我们可以试着从结果反推过程,从打包后的代码来分析,webpack的思路。
在 mode: ‘none’, devtool: ‘cheap-source-map’,的配置下,我们可以看到打包出来的代码大概长这个样子:

(function webpackUniversalModuleDefinition(root, factory) {
  if(typeof exports === 'object' && typeof module === 'object')
    module.exports = factory();
  else if(typeof define === 'function' && define.amd)
    define([], factory);
  else if(typeof exports === 'object')
    exports["Magotan"] = factory();
  else
    root["Magotan"] = factory();
})(window, function(){
    return (function(modules){
        // xxxx    
    })([
        //xxxx
    ])['default']
})

显而易见的是这是一个IIFE,简化一下,在加上具体的实现代码,就成了下面这样:

(function webpackUniversalModuleDefinition() {
  exports["Magotan"] = (function (modules) {
    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__);
      module.l = true;
      return module.exports;
    }
    return __webpack_require__(0);
  })([ 
    /* 0 */ //模块数组列表 数组下标即为模块id 
    (function (module, __webpack_exports__, __webpack_require__) {
      __webpack_exports__["default"] = ({
        getInstance: function getInstance() {
          if (magotan === null) {
            throw new Error('未初始化实例,请先通过config初始化');
          }
          return magotan;
        },
    }),
    /* 1 */
    (function (module, exports, __webpack_require__) {
      var toObject = __webpack_require__(2);
      var $keys = __webpack_require__(4);
      __webpack_require__(21)('keys', function () {
        return function keys(it) {
          return $keys(toObject(it));
        };
      });
    }),
  // ...
});
  ])['default'];
})()

通过上面的代码我们可以看出,我们将项目中所有使用到的模块的代码以及依赖关系存在一个modules的数组中,然后通过 webpack自定义的require方法,从入口文件,即id = 0 的模块为起点,递归执行模块。

至此我们可以分析得出,webpack核心流程就是两步

  1. reslove modules阶段 递归解析 产出 Dependency graphs
  2. Bundle 阶段 将上个阶段的产物,组装成数组并用函数包裹,从入口文件开始递归执行

    在理清楚了webpack的设计思路之后,更多的就是具体实现上的问题,比如如何拿到模块之间的关系(利用AST),如何保证不出现循环引用(利用installedModules 对象) 等等,更多的内容会在后续文档中有介绍。


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK