本文由云 + 社区发表
模块化是指把一个复杂的系统分解到一个一个的模块.
模块化开发的优点:
(1) 代码复用, 让我们更方便地进行代码管理, 同时也便于后面代码的修改和维护.
(2) 一个单独的文件就是一个模块, 是一个单独的作用域, 只向外暴露特定的变量和函数. 这样可以避免污染全局变量, 减少变量命名冲突.
JS 模块化规范有: CommonJS,AMD,CMD,ES6 的模块系统. 本文将依次介绍下每个规范.
0. 早期: 用 script 来引入 JS 模块
- <script type="text/javascript" src="a.js">
- </script>
- <script type="text/javascript" src="b.js">
- </script>
- <script type="text/javascript" src="c.js">
- </script>
- <script type="text/javascript" src="d.js">
- </script>
缺点:
(1) 加载的时候会停止渲染网页, 引入的 JS 文件越多, 网页失去响应的时间越长;
(2) 会污染全局变量;
(3)JS 文件之间存在依赖关系, 加载是有顺序的, 依赖性最大的要放到最后去加载; 当项目规模较大时, 依赖关系变得错综复杂.
(4) 要引入的 JS 文件太多, 不美观, 代码难以管理.
1.CommonJS 规范
是 服务器端模块的规范 , 由 Node.JS 推广使用. 该规范的核心思想是: 允许模块通过 require 方法来同步加载所要依赖的其他模块, 然后通过 exports 或 module.exports 来导出需要暴露的接口.
CommonJS 还可以细分为 CommonJS1 和 CommonJS2, 区别在于 CommonJS1 只能通过 exports.xx = xx 的方式导出, CommonJS2 在 CommonJS1 的基础上加入了 module.exports = xx 的导出方式. CommonJS 通常指 CommonJS2.
采用 CommonJS 规范导入导出:
- // 导出
- module.exports = moduleA.someFunc;
- // 导入
- const moduleA = require('./moduleA');
实例:
- //math.JS
- var num = 0;
- function add(a, b) {
- return a + b;
- }
- module.exports = {
- // 需要向外暴露的变量, 函数
- num: num,
- add: add
- }
可以这样加载:
- // 引入自定义的模块时, 参数包含路径, 可省略. JS
- // 引入核心模块时, 不需要带路径, 如 var http = require("http");
- var math = require('./math');
- math.add(1, 2)//3
实际上, 从上面的例子就可以看出, math.add(1,2) 必须要等待 math.JS 加载完成, 即 require 是同步的.
在服务器端, 模块文件保存在本地磁盘, 等待时间就是磁盘的读取时间. 但对于浏览器而言, 由于模块都放在服务器端, 等待时间取决于网上的快慢. 因此更合理的方案是异步加载模块.
缺点:
(1) 不能并行加载模块, 会阻塞浏览器加载;
(2) 代码无法直接运行在浏览器环境下, 必须通过工具转换成标准的 ES5;
2.AMD 和 require.JS
AMD: 异步模块定义. 上面已经介绍过, CommonJS 是服务器端模块的规范, 主要是为了 JS 在后端的表现制定的, 不太适合前端. 而 AMD 就是要为前端 JS 的表现制定规范. 由于不是 JavaScript 原生支持, 使用 AMD 规范进行页面开发需要用到对应的库函数, 也就是 require.JS(还有个 JS 库: curl.JS). 实际上 AMD 是 require.JS 在推广过程中对模块定义的规范化的产出.
AMD 采用异步方式加载模块, 模块的加载不影响它后面语句的运行. 所有依赖这个模块的语句, 都定义在一个回调函数中, 等到加载完成之后, 这个回调函数才会运行.
require.JS 也采用 require() 语句加载模块, 但是不同于 CommonJS:
- // 定义一个模块
- define('module', ['dep'], function (dep) {
- return exports;
- });
- // 导入和使用
- require(['module'], function (module) {
- });
上面示例中的代码改写成 AMD 形式:
math.JS 定义一个模块:
- define('math', ['jquery'], function (jQuery) {// 引入 jQuery 模块
- return {
- add: function (x, y) {
- return x + y;
- }
- };
- });
导入和使用:
- require(['math'], function (math) {
- math.add(1, 2)
- })
math.add() 与加载 math 模块不是同步的, 不会阻塞浏览器的加载.
3.CMD 和 sea.JS
CMD: 通用模块定义.
国内的玉伯大佬写了 sea.JS, 实际上 CMD 就是 sea.JS 在推广过程中对模块定义的规范化的产出.
- define(function (require, exports, module) {
- // 模块代码
- });
说明:
require: 可以把其他模块导入进来的一个参数;
exports: 可以把模块内的一些属性和方法导出的;
module: 是一个对象, 上面存储了与当前模块相关联的一些属性和方法.
上面示例中的代码改写成 AMD 形式:
- define(function (require, exports, module) {
- var add = function (a, b) {
- return a + b;
- }
- exports.add = add;
- })
- // 导入和使用
- SeaJS.use(['math.js'], function (math) {
- var sum = math.add(1, 2);
- });
CMD 与 AMD 的不同的在于:
(1)AMD 推崇依赖前置; CMD 推崇依赖就近, 只有在用到某个模块的时候再去 require:
- //AMD 推崇的依赖关系前置: 在定义模块时就要声明要依赖的模块
- define(['a', 'b', 'c', 'd'], function (a, b, c, d) { // 依赖必须一开始就写好
- a.doSomething()
- // 此处省略 100 行
- ...
- b.doSomething()
- ...
- })
- //CMD 推崇依赖就近, 按需加载, 只有在用到某个模块时再去 require
- define(function (require, exports, modules) {
- var a = require('a');
- a.doSomething();
- // 此处省略 100 行
- ...
- var b = require("b");// 按需加载
- b.doSomething();
- ...
- })
(2)AMD 的 API 默认是一个当多个用, CMD 的 API 严格区分, 推崇职责单一.
对于依赖的模块, AMD 是提前执行, CMD 是延迟执行.
具体细节可点击 参考 https://github.com/seajs/seajs/issues/277
4.ES6 模块化
ES6 在语言的层面上实现了模块化. 浏览器厂商和 Node.JS 都宣布要原生支持该规范. 它将逐渐取代 CommonJS 和 AMD 规范, 成为浏览器和服务器通用的模块解决方案.
在 ES6 中, 使用 export 关键字来导出模块, 使用 import 关键字引用模块. 但是浏览器还没有完全兼容, 需要使用 babel 转换成 ES5.
- // 导出
- export function hello() { };
- export default {
- // ...
- };
- // 导入
- import { readFile } from 'fs';
- import React from 'react';
使用 import 导入模块时, 需要知道要加载的变量名或函数名.
在 ES6 中还提供了 export default, 为模块指定默认输出. 对应导入模块 import 时, 不需要使用大括号.
上面示例中的代码改写成 ES6 形式:
- //math.JS
- var num = 0;
- var add = function (a, b) {
- return a + b;
- };
- export { num, add };
- // 导入
- import { num, add } from './math';
- function test(ele) {
- ele.textContent = add(1 + num);
- }
缺点
浏览器还没有完全兼容, 必须通过工具转换成标准的 ES5 后才能正常运行.
5. 小结
本文从 script 引入 JS 文件讲起, 到服务器端模块的规范 CommonJs, 再到推崇依赖前置的浏览器端模块的规范 AMD, 推崇依赖就近的浏览器端模块的规范 CMD, 最后介绍了 ES6 的模块化. 每个介绍中都有各规范基本的用法和一个示例. 如有问题, 欢迎指正.
此文已由作者授权腾讯云 + 社区发布
来源: http://www.tuicool.com/articles/EvEZFzU