模块化机制

  • 非模块化代码

例如下面的js代码

alert('hello world');

经过webpack打包后会生成这样的代码:

(function(modules) {
  // 模块缓存
  var installedModules = {};
  // require方法
  function __webpack_require__(moduleId) {
        // 检测缓存
        if(installedModules[moduleId])
            return installedModules[moduleId].exports;
        //创建缓存
       var module = installedModules[moduleId] = {
           exports: {},
           id: moduleId,
            loaded: false
        };
        // 执行模块
       modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
       // 标记已经加载
        module.loaded = true;
        // 返回module.exports
        return module.exports;
    }
    // 暴露模块对象 (__webpack_modules__)
    __webpack_require__.m = modules;
    // 暴露缓存
    __webpack_require__.c = installedModules;
    // 暴露路径
    __webpack_require__.p = "";
    // 加载入口并返回
    return __webpack_require__(0);
 })([function(module, exports) {alert('hello world');}]);
  • AMD模块

例如下面的AMD代码:

define([], function() {
    alert('hello world!');
});

经过webpack打包后变成这样:

function(module, exports, __webpack_require__) {
    var __WEBPACK_AMD_DEFINE_ARRAY__, // AMD依赖列表
        __WEBPACK_AMD_DEFINE_RESULT__; // AMD factory函数的返回值,即模块内容
    __WEBPACK_AMD_DEFINE_ARRAY__ = [];
    // 执行factory函数,获取返回值作为模块内容
    // 函数体使用.apply调用,函数体中this为exports
    // 形参则分别对应依赖列表中的各个依赖模块
    __WEBPACK_AMD_DEFINE_RESULT__ = function() {
        alert('hello world!');
    }.apply(exports, __WEBPACK_AMD_DEFINE_ARRAY__);
    // 如果模块内容不为空,则通过module.exports返回
    // 如果为空,则不处理,在Runtime中声明为{}
    if (__WEBPACK_AMD_DEFINE_RESULT__ !== undefined) {
        module.exports = __WEBPACK_AMD_DEFINE_RESULT__;
    }
}
  • CommonJs

例如下面的CommonJs代码:

var obj= {
    sayHello:function(){
        alert('hello world!');
    }
};
module.exports = obj;

经过webpack打包后变成这样:

function(module, exports) {
    var obj= {
        sayHello: function() {
            alert('hello world!');
        }
    };
    module.exports = obj;
}

引入未模块化的库,如 Zepto

当我们引入未模块化的库的时候,如zepto的时候,会报这样的错误:

Uncaught TypeError: Cannot read property 'createElement' of undefined
  • 原因

首先我们来分析下zepto的源码:

(function(global, factory) {
  if (typeof define === 'function' && define.amd)
    define(function() { return factory(global) })
  else
    factory(global)
}(this, function(window) {
  var Zepto = (function() {
    // ...
    return $
  })()
  window.Zepto = Zepto
  window.$ === undefined && (window.$ = Zepto)
  return Zepto
}))

根据打包机制可以知道, modules[moduleId].call,它会传入新初始化的 module.exports 来作为模块闭包的上下文(context),并运行模块闭包来将模块暴露的对象加入到已加载的模块对象(installedModules)中。
所以对于 Zepto 来说,它初始化时使用的 this其实就是 module.exports,但这个 module.exports 没有赋值过任何变量,即 Zepto 初始化使用的 this为空对象。
所以 factory(global)global 为空对象,Zepto 运行函数中的 window 也就变成了空对象,而 var document = window.document,这个 document 为 undefined,因此会造成 document.createElement 会报 TypeError

  • 解决办法

npm install --save-dev script-loader exports-loader
// webpack.config
{
  // ...
  module: {
    loaders: [{
      test: require.resolve('zepto'),
      loader: 'exports-loader?window.Zepto!script-loader'
    }]
  }
}