- 页面中越来越多的js
- 现在浏览器提供更多接口
- 更少的页面重载刷新,页面中有更多的代码
这些导致浏览器端存在大量的代码,需要重新组织,于是出现了模块系统。
模块系统目前有很多标准:
- <script>标签风格(没有模块系统)
- Commonjs
- AMD规范以及对应实现
- ES6模块
- 其它
<script>标签风格
如果没有模块系统,我们经常会这样处理模块化的代码
<script src="module1.js"></script>
<script src="module2.js"></script>
<script src="libraryA.js"></script>
<script src="module3.js"></script>
各个模块把接口暴露给全局对象,比如window对象。各个模块之间可以通过全局对象进行访问互相依赖的接口。
普遍的问题:
- 全局对象的冲突
- 加载的顺序是重要的
- 开发者需要解决模块的依赖问题
- 在大项目中模块引入的数目将会非常长并且难以维护
commomJs:同步的require
这种风格使用同步的require方法来加载依赖和返回暴露的接口。一个模块可以通过给exports对象添加属性,或者设置module.exports的值来描述暴露对象。
require("module");
require("../file.js");
exports.doStuff = function() {};
module.exports = someValue;
CommonJs规范在服务端nodejs中使用。
优点:服务端代码可以被重用;npm中有大量模块;用起来简单方便
缺点:阻塞调用无法在网络环境应用,网络请求是异步的;不能并行requrie多个模块
CommonJs规范的实现
- node.js -server端
- browserify
- modules-webmake - 编译成一个 bundle
- wreq -client端
AMD:异步的require
require(["module", "../file"], function(module, file) { /* ... */ }); define("mymodule", ["dep1", "dep2"], function(d1, d2) {
return someExportedValue;
});
优点:符合了在网络中异步请求的风格;可以并行加载多个模块
缺点:编码成本增加,可读性不好写起来麻烦;更像是一种临时修补方案
AMD 规范的实现有:
require.js - client 端
curl - client 端
ES6模块
ECMAScript 2015 (6th Edition) 给 JavaScript添加了一些语法结构,用来实现另外一种模块系统
import "jquery";
export function doStuff() {}
module "localModule" {}
优点:静态分析方便;作为ES的标准前途是光明
缺点:浏览器支持还需要很长时间;这种风格的模块不多
传输
实现了模块化,将模块从server端传输到浏览器端又是另一个问题,传输模块会出现两个极端:
- 每个模块都是一个请求
- 一个请求包含了所有模块
这两种情况目前使用的都很普遍,但都不是最佳方案,第一种虽然只传输需要的模块,但是要发多个请求,请求的延迟会导致应用启动的很慢;第二种虽然请求少、延迟低,但是传输不需要的模块又形成了浪费
webpack就提出了一种更灵活的方式:
当编译所有模块时,将模块集分成多个较小的组(块)
这会使得多个请求更小、更快。初始化阶段不需要的模块组可以按需加载。这样就可以加速初始加载,当你实际需要代码时也能加载更多的代码块。
「分割点」取决于开发者
一个大的代码库也是可能的
并且webpack除了能处理js模块,其他资源也可以处理,这样我们使用起来就更方便