本篇分为两个部分
 第一部分:总结了ES6出现之前,在当时现有的运行环境中,实现"模块"的方式;
 第二部分:总结了ES6出现后,module成为ES6标准,客户端实现模块化的解决方案;

一、require时代

Javascript社区做了很多努力,在当时现有的运行环境中,实现了“模块”的效果

原始写法:

模块就是实现特定功能的一组方法。
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。

 function m1(){
 //...
}
function m2(){
 //...  
}

  上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
  这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。

对象写法:

  为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面

 var module1 = new Object({
 _count : 0,
 m1 : function (){
  //...
 },
 m2 : function (){
  //...
 }
});

  上面的函数m1()和m2(),都封装在 module1 对象里。使用的时候,就是调用这个对象的属性

 module1.m1();

  这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。

 module._count = 1;

立即执行函数写法:

  使用"立即执行函数"(Immediately-Invoked Function Expression,IIFE),可以达到不暴露私有成员的目的

 var module = (function() {
var _count = 0;
var m1 = function() {
alert(_count)
}
var m2 = function() {
alert(_count + 1)
}
return {
m1: m1,
m2: m2
}
})();

  使用上面的写法,外部代码无法读取内部的_count变量。console.info(module._count); //undefined

  module就是Javascript模块的基本写法。

主流模块规范

  在es6以前,还没有提出一套官方的规范,从社区和框架推广程度而言, 通行的javascript模块规范有两种:CommonJS 和 AMD

1.1 CommonJS 规范:

  2009年,美国程序员Ryan Dahl创造了node.js项目,将javascript语言用于服务器端编程。这标志着"Javascript模块化编程"正式诞生。前端(客户端)的复杂程度有限,没有模块也是可以的,但是在服务器端,一定要有模块,与操作系统和其他应用程序互动,否则根本没法编程。

  node编程中最重要的思想之一就是模块,而正是这个思想,让JavaScript的大规模工程成为可能。模块化编程在js界流行,也是基于此,随后在浏览器端,requirejs和seajs之类的工具包也出现了,可以说在对应规范下,require统治了ES6之前的所有模块化编程,即使现在,在ES6 module被完全实现之前,还是这样。

  在CommonJS中,暴露模块使用module.exports和exports,很多人不明白暴露对象为什么会有两个,后面会介绍区别

  在CommonJS中,有一个全局性方法require(),用于加载模块。假定有一个数学模块math.js,就可以像下面这样加载。

 var math = require('math');

  然后,就可以调用模块提供的方法:

 var math = require('math');
math.add(2,3); //

  正是由于CommonJS 使用的require方式的推动,才有了后面的AMD、CMD 也采用的require方式来引用模块的风格

AMD规范:

  有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。
  但是,由于一个重大的局限,使得CommonJS规范不适用于浏览器环境。如果在浏览器中运行,会有一个很大的问题

 var math = require('math');
math.add(2, 3);

  第二行math.add(2, 3),在第一行require(‘math’)之后运行,因此必须等math.js加载完成。也就是说,如果加载时间很长,整个应用就会停在那里等。这对服务器端不是一个问题,因为所有的模块都存放在本地硬盘,可以同步加载完成,等待时间就是硬盘的读取时间。但是,对于客户端浏览器,这却是一个大问题,因为模块都放在服务器端,等待时间取决于网速的快慢,可能要等很长时间,浏览器处于“假死”状态。因此,浏览器端的模块,不能采用“同步加载”(synchronous),只能采用”异步加载”(asynchronous)。这就是AMD规范诞生的背景。

  AMD是“Asynchronous Module Definition”的缩写,意思就是”异步模块定义”。

  它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到加载完成之后,这个回调函数才会运行。
  模块必须采用特定的define()函数来定义。

 define(id?, dependencies?, factory)

  id:字符串,模块名称(可选)  dependencies: 是我们要载入的依赖模块(可选),使用相对路径。注意是数组格式   factory: 工厂方法,返回一个模块函数

  如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

      // math.js
  define(function (){
    var add = function (x,y){
      return x+y;
    };
    return {
      add: add
    };
  });

  如果这个模块还依赖其他模块,那么define()函数的第一个参数,必须是一个数组,指明该模块的依赖性。

 define(['Lib'], function(Lib){
  function foo(){
    Lib.doSomething();
  }
  return {
    foo : foo
  };
});

  当require()函数加载上面这个模块的时候,就会先加载Lib.js文件。AMD也采用require()语句加载模块,但是不同于CommonJS,

  它要求两个参数:require([module], callback);

  第一个参数[module],是一个数组,里面的成员就是要加载的模块;第二个参数callback,则是加载成功之后的回调函数。

  如果将前面的代码改写成AMD形式,就是下面这样:

 require(['math'], function (math) {
 math.add(2, 3);
});

  math.add()与math模块加载不是同步的,浏览器不会发生假死。所以很显然,AMD比较适合浏览器环境。目前,主要有两个Javascript库实现了AMD规范:require.js和curl.js。

CMD规范:
  
CMD (Common Module Definition), 是seajs推崇的规范,CMD则是依赖就近,用的时候再require。它写起来是这样的:

 define(function(require, exports, module) {
var clock = require('clock');
clock.start();
});

  CMD与AMD一样,也是采用特定的define()函数来定义,用require方式来引用模块

  define(id?, dependencies?, factory)
    id:字符串,模块名称(可选)
  dependencies: 是我们要载入的依赖模块(可选),使用相对路径。,注意是数组格式
  factory: 工厂方法,返回一个模块函数

 define('hello', ['jquery'], function(require, exports, module) {
  // 模块代码
});

  如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中。

define(function(require, exports, module) {
// 模块代码
});

  注意:带 id 和 dependencies 参数的 define 用法不属于 CMD 规范,而属于 Modules/Transport 规范。

CMD与AMD区别

  AMD和CMD最大的区别是对依赖模块的执行时机处理不同,而不是加载的时机或者方式不同,二者皆为异步加载模块;AMD依赖前置,js可以方便知道依赖模块是谁,立即加载;而CMD就近依赖,需要使用把模块变为字符串解析一遍才知道依赖了那些模块,这也是很多人诟病CMD的一点,牺牲性能来带来开发的便利性,实际上解析模块用的时间短到可以忽略。

前端模块化 | 解读JS模块化开发中的 require、import 和 export的更多相关文章

  1. 【前端学习笔记】2015-09-09~~~~nodejs中的require()和module.exports

    nodejs中一个js文件就可以看做是一个模块 在node环境中,可以直接var a=require('模块路径以及不带扩展名的模块名') exports---module.exports 其中nod ...

  2. 关于 JS 模块化的最佳实践总结

    模块化开发是 JS 项目开发中的必备技能,它如同面向对象.设计模式一样,可以兼顾提升软件项目的可维护性和开发效率. 模块之间通常以全局对象维系通讯.在小游戏中,GameGlobal 是全局对象.在小程 ...

  3. js模块化入门与commonjs解析与应用

    JS模块化的基本原理 commonjs规范 commonjs在前端模块化中的基本使用 AMD与CMD规范剖析博客链接 一.JS模块化基本原理 在JS没有提出来模块化的时候,开发JS项目比较简单,同时也 ...

  4. 前端之Vue.js库的使用

    vue.js简介 Vue.js读音 /vjuː/, 类似于 view Vue.js是前端三大新框架:Angular.js.React.js.Vue.js之一,Vue.js目前的使用和关注程度在三大框架 ...

  5. JS模块化开发:使用SeaJs高效构建页面

    一.扯淡部分 很久很久以前,也就是刚开始接触前端的那会儿,脑袋里压根没有什么架构.重构.性能这些概念,天真地以为前端===好看的页面,甚至把js都划分到除了用来写一些美美的特效别无它用的阴暗角落里,就 ...

  6. 闲聊——浅谈前端js模块化演变

    function时代 前端这几年发展太快了,我学习的速度都跟不上演变的速度了(门派太多了,后台都是大牛公司支撑类似于facebook的react.google的angular,angular的1.0还 ...

  7. JS模块化开发----require.js

    前言 前端开发中,起初只要在script标签中嵌入几十上百行代码就能实现一些基本的交互效果,后来js得到重视,应用也广泛起来了,jQuery,Ajax,Node.Js,MVC,MVVM等的助力也使得前 ...

  8. Js模块化开发--seajs和gruntJs

    1.Seajs库 解决开发中的冲突依赖等问题,提供代码可维护性. SeaJS 是由玉伯开发的一个遵循 CommonJS 规范的模块加载框架,可用来轻松愉悦地加载任意 JavaScript 模块和css ...

  9. Js模块化开发的理解

    Js模块化开发的理解 模块化是一个语言发展的必经之路,其能够帮助开发者拆分和组织代码,随着前端技术的发展,前端编写的代码量也越来越大,就需要对代码有很好的管理,而模块化能够帮助开发者解决命名冲突.管理 ...

随机推荐

  1. Ubuntu下推荐安装软件

    前言:都是全平台软件,通用性好. 1.搜狗输入法 官网下载: 不能双击.deb安装成功,需要安装依赖,可参考:https://www.cnblogs.com/chendeqiang/p/1017741 ...

  2. SpringBoot(九) ElasticSearch 全文检索

    ElasticSearch ​ ElasticSearch是一个基于Lucene的搜索服务器.它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口.Elasticsearch是用 ...

  3. 关于PHP函数

    从这里我开始聊一些php相关的东西了,因为视频教程里并没有讲到过多的JS,JQ,XML和AJAX,这些在后续自学之后再写一些: 有关php的基本语法数据类型什么的就不做介绍了,在PHP手册或各大学习网 ...

  4. 清北集训Day3T1(转换)

    这题可能是我与正解里的最近的一次了,可以还是sb的把正解叉了. 正解其实比较显然:因为$f(x)$只有81个取值,所以我们可以枚举$f(x)$,然后计算$x$,再判断$x$是否可以转化为$f(x)$ ...

  5. 为什么使用GitHub

    GitHub的特点: 1.开源式分布版本管理系统 2.开源项目集中的代码库 3.所有略有规模的公司都在使用 GitHub的功能介绍: 1.记录多个版本 2.查看历史操作,可以进行版本吧回退和前进的控制 ...

  6. kernel zram feature

    what is zram? Zram wiki zram zram(也称为 zRAM,先前称为 compcache)是 Linux 内核的一项功能,可提供虚拟内存压缩.zram 通过在 RAM 内的压 ...

  7. Vue文件封装日历组件

    封装就是要具有灵活性,样式自适应,调用的时候传入props就可以变成自己想要的样式. 效果展示网址:https://1963331542.github.io/ 源代码: <template> ...

  8. Vue组件之作用域插槽

    写作用域插槽之前,先介绍一下Vue中的slot内容分发: 如果<child-component></child-component>标签之间没有插入那两个p标签的话,页面会显示 ...

  9. BZOJ 3786 星系探索 (splay+dfs序)

    题目大意:给你一棵树,支持一下三种操作 1.获取某节点到根节点的路径上所有节点的权值和 2.更换某棵子树的父亲 3.某子树内所有节点的权值都增加一个值w 当时想到了splay维护dfs序,查完题解发现 ...

  10. CF895C Square Subsets (组合数+状压DP+简单数论)

    题目大意:给你一个序列,你可以在序列中任选一个子序列,求子序列每一项的积是一个平方数的方案数. 1<=a[i]<=70 因为任何一个大于2的数都可以表示成几个质数的幂的乘积 所以我们预处理 ...