一览js模块化:从CommonJS到ES6
本文由云+社区发表
模块化是指把一个复杂的系统分解到一个一个的模块。
模块化开发的优点:
(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 规范
是服务器端模块的规范,由nodejs推广使用。该规范的核心思想是:允许模块通过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是延迟执行。
具体细节可点击参考
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的模块化。每个介绍中都有各规范基本的用法和一个示例。如有问题,欢迎指正。
此文已由作者授权腾讯云+社区发布

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Layer2 | 区块链发展新思潮
如果我们仔细回顾今年区块链技术的进展,就会发现有新想法的公链团队越来越少出现了,大家谈论以及关注的点也都不再聚焦公链上了。反倒是很多新的 Layer 2 项目在不断涌现,非常活跃。要知道在这之前,尤其是 2017 年,大家无人不谈公链,无人不做公链,各种 1CO 的项目宛如烈火烹油,火的一塌糊涂。 **为什么会有这样的思潮转变呢? ** 这里还是需要提到不可能三角问题。 2017 的很多区块链团队都是野心勃勃,说要颠覆比特币,颠覆以太坊,各种区块链 3.0,甚至是区块链 4.0。比特币和以太坊所为人所诟病的 Tps ,也成了区块链团队攻击并且用来给自己 PR 的材料,很多宣称有百万 Tps 的项目诞生。 而经过一年的发展,大家发现要纯靠一套算法和共识来解决区块链的不可能三角问题是不现实的,因为如果你要保证系统的去中心化和安全性,每一笔交易必须由网络中每个节点进行处理,而这样就注定了它的性能达不到我们要求的。 于是大家的关注点开始转变,越来越多人开始关注第一层的扩容方案。以太坊生态其实一直在研究相关的扩容方案,只是大家在做公链遇到瓶颈的时候,很多人才反过来关注这些东西。 让每个节点做更多...
- 下一篇
KubeCon 2018 参会记录 —— FluentBit Deep Dive
在最近的上海和北美KubeCon大会上,来自于Treasure Data的Eduardo Silva(Fluentd Maintainer)带来了最期待的关于容器日志采集工具FluentBit的最新进展以及深入解析的分享;我们知道Fluentd是在2016年底正式加入CNCF,成为CNCF项目家族的一员,其被广泛用于容器集群中进行应用日志的采集、处理和聚合,但今天主要是跟大家分享一下同样来自于Treasure Data新开源的日志采集工具——FluentBit。 FluentBit vs Fluentd 既然已经有了Fluentd,那么为什么还要开发一个FluentBit呢?我们知道,Fluentd是基于Ruby语言的,在一些应用日志量较大或者单节点日志量较大的场景下,通过Fluentd采集日志的速率会远落后于应用日志的产生速率,进而导致日志采集的延迟时间较大,这对于一些实时性要求较高的业务系统或者监控系统来说是不可接受的;另外一方面,也是由于Fluentd自身的日志处理逻辑越来越复杂,全部放置在一个组件里来完成会导致越来越臃肿,因此Treasure Data在基于Fluentd优秀的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- MySQL8.0.19开启GTID主从同步CentOS8
- SpringBoot2更换Tomcat为Jetty,小型站点的福音
- Red5直播服务器,属于Java语言的直播服务器
- CentOS7,8上快速安装Gitea,搭建Git服务器
- CentOS6,7,8上安装Nginx,支持https2.0的开启
- CentOS8,CentOS7,CentOS6编译安装Redis5.0.7
- Jdk安装(Linux,MacOS,Windows),包含三大操作系统的最全安装
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2全家桶,快速入门学习开发网站教程