浏览器自己不支持模块化css
是一种在线"编译"模块的方案,至关于在页面上加载一个 CMD/AMD 解释器。这样浏览器就认识了 define、exports、module 这些东西。也就实现了模块化。html
你在本地直接写JS,不论是 AMD / CMD / ES6 风格的模块化,它都能认识,而且编译成浏览器认识的JS。前端
ES6在语言标准的层面上, 实现了模块功能, 并且实现得至关简单, 彻底能够取代CommonJS和AMD规范, 是浏览器和服务器通用的模块解决方案node
从amd cmd 到 (commonjs), 再到浏览器支持模块化es6。webpack
浏览器不兼容Commonjs的根本缘由,是由于缺乏NodeJs的4个环境变量git
让浏览器支持,这几个环境变量。咱们来定义这几个变量。es6
var module = {
exports:{}
};
(function(module, exports) {
exports.multiply = function (n) { return n * 1000 };
}(module, module.exports))
var f = module.exports.multiply;
f(5) // 5000
复制代码
上面代码向一个当即执行函数提供 module 和 exports 两个外部变量,模块就放在这个当即执行函数里面。模块的输出值放在 module.exports 之中,这样就实现了模块的加载。github
https://github.com/ruanyf/tiny-browser-require/blob/master/require.js
复制代码
webpack是如何支持commonjs的?web
咱们都知道,webpack做为一个构建工具,解决了前端代码缺乏模块化能力的问题。咱们写的代码,通过webpack构建和包装以后,可以在浏览器以模块化的方式运行。这些能力,都是由于webpack对咱们的代码进行了一层包装,本文就以webpack生成的代码入手,分析webpack是如何实现模块化的。segmentfault
PS: webpack的模块不只指js,包括css、图片等资源均可以以模块看待,但本文只关注js。
webpack打包的代码,总体能够简化成下面的结构:
(function (modules) {/* 省略函数内容 */})
([
function (module, exports, __webpack_require__) {
/* 模块a.js的代码 */
},
function (module, exports, __webpack_require__) {
/* 模块b.js的代码 */
}
]);
复制代码
能够看到,整个打包生成的代码是一个IIFE(当即执行函数),函数内容咱们待会看,咱们先来分析函数的参数。
函数参数是咱们写的各个模块组成的数组,只不过咱们的代码,被webpack包装在了一个函数的内部,也就是说咱们的模块,在这里就是一个函数。为何要这样作,是由于浏览器自己不支持模块化,那么webpack就用函数做用域来hack模块化的效果。
若是你debug过node代码,你会发现同样的hack方式,node中的模块也是函数,跟模块相关的参数exports、require,或者其余参数__filename和__dirname等都是经过函数传值做为模块中的变量,模块与外部模块的访问就是经过这些参数进行的,固然这对开发者来讲是透明的。
一样的方式,webpack也控制了模块的module、exports和require,那么咱们就看看webpack是如何实现这些功能的。
下面是摘取的函数内容,并添加了一些注释:
// 一、模块缓存对象
var installedModules = {};
// 二、webpack实现的require
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;
// 七、返回module.exports
return module.exports;
}
// 八、require第一个模块
return __webpack_require__(__webpack_require__.s = 0);
复制代码
webpack对于es模块的实现,也是基于本身实现的__webpack_require__ 和__webpack_exports__ ,装换成相似于commonjs的形式。对于es模块和commonjs混用的状况,则须要经过__webpack_require__.n的形式作一层包装来实现。
webpack的模块化不只支持commonjs和es module,还能经过code splitting实现模块的动态加载。根据wepack官方文档,实现动态加载的方式有两种:import和require.ensure。
webpack经过__webpack_require__.e函数实现了动态加载,再经过webpackJsonp函数实现异步加载回调,把模块内容以promise的方式暴露给调用方,从而实现了对code splitting的支持。