浏览器模块化之路(一),了解一下?

解决什么问题

浏览器自己不支持模块化css

怎么解决

  1. seajs / require

是一种在线"编译"模块的方案,至关于在页面上加载一个 CMD/AMD 解释器。这样浏览器就认识了 define、exports、module 这些东西。也就实现了模块化。html

  1. webpack (预编译模块的方案)「把模块转换成函数」

你在本地直接写JS,不论是 AMD / CMD / ES6 风格的模块化,它都能认识,而且编译成浏览器认识的JS。前端

  1. es6

ES6在语言标准的层面上, 实现了模块功能, 并且实现得至关简单, 彻底能够取代CommonJS和AMD规范, 是浏览器和服务器通用的模块解决方案node

浏览器模块化发展过程

从amd cmd 到 (commonjs), 再到浏览器支持模块化es6。webpack

浏览器加载 CommonJS 模块的实现

  • IIFE
  • webpack的实现

IIFE

浏览器不兼容Commonjs的根本缘由,是由于缺乏NodeJs的4个环境变量git

  • require
  • exports
  • module
  • global

解决办法

让浏览器支持,这几个环境变量。咱们来定义这几个变量。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的实现

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);


复制代码

参考

  1. segmentfault.com/a/119000001…
  2. www.ruanyifeng.com/blog/2015/0…

总结

  • 浏览器模块化,从模块解释器到模块打包器,到浏览器支持模块化
  • webpack把每个模块文件打包成一个函数,固然也有多个模块文件合并成一个函数的处理方式
  • 由于浏览器自己不支持模块化,因此webpack实现了模块化的功能,把咱们commonjs写的模块转成了函数(webpack就用函数做用域来hack模块化的效果)
  • webpack 对于commonjs与es6模块混写的处理,后续分析

后续文章

1. webpack是如何支持es模块的

webpack对于es模块的实现,也是基于本身实现的__webpack_require__ 和__webpack_exports__ ,装换成相似于commonjs的形式。对于es模块和commonjs混用的状况,则须要经过__webpack_require__.n的形式作一层包装来实现。

2. webpack的 code splitting经过promise实现模块的动态加载

webpack的模块化不只支持commonjs和es module,还能经过code splitting实现模块的动态加载。根据wepack官方文档,实现动态加载的方式有两种:import和require.ensure。

webpack经过__webpack_require__.e函数实现了动态加载,再经过webpackJsonp函数实现异步加载回调,把模块内容以promise的方式暴露给调用方,从而实现了对code splitting的支持。

相关文章
相关标签/搜索