javascript ES5 模块化 CommonJs AMD require加载模块

点点圈 提交于 2020-02-22 06:30:45

什么是模块化

模块化其实是一种规范,一种约束,这种约束会大大提升开发效率。将每个js文件看作是一个模块,每个模块通过固定的方式引入,并且通过固定的方式向外暴露指定的内容。

按照js模块化的设想,一个个模块按照其依赖关系组合,最终插入到主程序中。

模块化的方法

普通方法

优点:可以直接调用。
缺点:变量可能会出现重复造成的污染,并且无法进行结构性分类。

function a() {
    console.log("a");
}
function b() {
    console.log("b");
}
a();
b();

对象的写法

优点:变量不会被直接污染,并且易于分类描述内容。
缺点:会暴露所有成员,内部状态可以被外部改写。

var obj={
    _a:false,
    a:function () {
        console.log("a");
    },
    b:function () {
      console.log("b");
    }
};
obj.a();
obj.b();
obj._a=3;

立即执行函数

优点:外部代码无法读取到里面的_num变量,保证了变量不被污染。

var obj=(function () {
     var _num=3;
      return{
          a:function () {
              console.log(a);
          },
          set num(value){
              _num=value;
          },
          get num(){
              return _num;
          }
      }
})();
obj.num=5;
console.log(obj.num)//5;
console.log(obj._num)//undefined
obj.a();//a

基本上这种就是模块的写法了,但是单纯的这样描述仍然不算完美,因此我们在其基础上进行了一下的修改。

放大模式:

var module=(function () {
     return {
          a:1,
          b:2,
          c:function () {}
      }
})();
module=(function (mode) {
    mode.d=10;
   return mode;
})(module);

宽放大模式

如果module没有定义或者没有加载进入,这时候我们可以在带入参数的时候给参数时判断是否存在,不存在给一个空对象。
与"放大模式"相比,"宽放大模式"就是"立即执行函数"的参数可以是空对象。

var module=(function () {
      return {
          a:1,
          b:2,
          c:function () {
              
          }
      }
})();
module=(function (mode) {
    mode.d=10;
    return mode;
})(module || {});
console.log(module);

输入全局变量

独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。
为了在模块内部调用全局变量,必须显式地将其他变量输入模块。

var module1=(function($,YAHOO){
    //...
})(jQuery, YAHOO);

CommonJs规范

2009年,美国程序员Ryan Dahl创造了node.js项目,这标志"Javascript模块化编程"正式诞生。node.js的模块系统,就是参照CommonJS规范实现的。在CommonJS中,有一个全局性方法require(),用于加载模块。CommonJs规范的特点:

  • 原生Module对象,每个文件都是一个Module实例;
  • 文件内通过require对象引入指定模块;
  • 所有文件加载均是同步完成的;
  • 通过module关键字显露内容;
  • 每个模块加载一次之后就会被缓存;
  • 由于使用了Node的api,只能在服务端环境上运行。

举个例子,入口文件main.js:

//加载b.js,并执行返回的函数
var fn=require("./b");
fn();//打印结果 abc

//加载a.js,将返回的对象赋值给obj,执行obj.b()
var obj=require("./a");
obj.b();//打印结果 1

b.js文件:

//导出函数
module.exports=(function(){
    return function(){
        console.log("abc");
    }
})();

a.js文件:

// 导出对象
module.exports=(function(){
    return {
        a:1,
        b:function(){
            console.log(this.a);
        }
    }
})()

AMD规范

有了服务器端模块以后,很自然地,大家就想要客户端模块。而且最好两者能够兼容,一个模块不用修改,在服务器和浏览器都可以运行。但是,由于Commonjs 使用了Node的api,只能在服务端环境上运行。

Commonjs,可以实现模块同步加载,但只能在服务端环境上运行,客户端如果同步加载依赖的话时间消耗非常大,所以需要一个在客户端上基于Commonjs 但是对于加载模块做改进的文案,于是AMD规范诞生了。

AMD是"Asynchronous Module Definition"的缩写,意思就是"异步模块定义"。它采用异步方式加载模块,模块的加载不影响它后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,等到所有依赖加载完成之后(前置依赖),这个回调函数才会运行。

AMD写法

AMD与Commonjs一样都是js模块化规范,与Commonjs写法不同,AMD要求有两个参数,第一个参数是一个数组,里面是要加载的模块;第二个参数是加载成功后的回调函数,函数的参数就是模块返回的内容。

require([moudle],callback);

举个例子,Main.js文件:

//加载模块a,并执行里面的b方法
require(["./a"],function(o){
    o.b();
})

在定义模块的时候需要使用define函数定义,id是定义模块的名字,dependencies是前置依赖,callback是所有依赖加载完毕之后执行的回调函数。

define(id?, dependencies?, callback);

a.js文件:

define((function(){
    return {
        a:1,
        b:function(){
            console.log(this.a);
        }
    }
})());

AMD的两种写法:
无依赖模块规范,这里我们定义了一个加法和一个乘法的方法,return是返回的方法,这两个方法都被卸载一个固定的模块中。AMD规范不能导出函数。

define(function () {
    function add(x,y) {
        return x+y;
    }
    function product(x,y) {
        return x*y;
    }
    return{
        add:add,
        product:product
    }
}());

有依赖模块规范,有依赖表示,该模块中的方法需要用到别的模块,必须依赖于别的模块被加载调用时。定义的时候,第一个参数以数组的形式写入需要加载的前提模块,后面的方法是表示加载完成前置模块后回调的方法,参数对应模块中返回的内容。

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

RequireJS

RequireJs是js模块化的工具框架,是AMD规范的具体实现。RequireJs的特点:

  • 依赖前置
  • 配置文件,有一个main文件,用来配置不同模块的路径。

RequireJs写法:

“主模块”,意思是整个网页的入口代码。它有点像C语言的main()函数,所有代码都从这儿开始运行。就是一开始就执行了主模块,主模块负责了加载所有的需要模块后开始后续内容的执行。我们在首页中写入以下标签,以确定进入主模块。

 <!-- 首先加载 require.js文件  当require.js加载完成后,再继续加载配置文件Main-->
<script src="./require.js" data-main="./js/Main" async defer></script>

配置文件,使用require下的config方法加载配置内容,注意:该内容必须写在主模块的顶部。这个对象的paths属性指定各个模块的加载路径。

//统一当前路径下
require.config({
	paths: {
		"index": "./index.js",
		"a": "./a.js",
		"b": "./b.js"
	}
});

//入口文件
require(["index"],function(obj){
    obj();
})

//各自不同的路径,
//如果这些模块在其他目录,比如js/lib目录,则有两种写法。一是需要逐一指定路径
require.config({
	paths: {
		"index": "./lib/index.js",
		"a": "./lib/a.js",
		"b": "./lib/b.js"
	}
});

//二是当多个内容基于一个路径下,我们不需要逐一写这些路径,增加baseUrl值是该基路径就可以了。
require.config({
	baseUrl: "./lib",
	paths: {
		"index": "./index.js",
		"a": "./a.js",
		"b": "./b.js"
	}
});

//非本地路径
//如果某个模块在另一台主机上,也可以直接指定它的网址,比如:
require.config({
	paths: {
		"jquery": "https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min"
	}
});

a.js文件:

//函数参数为前置依赖中return 的内容
define("a",["b"],function(BoxObj){
    return {
    	num:3,
        run:function(){
            var b=new BoxObj.Box(20);
            b.play();
        }
    }
});

b.js文件:

define("b",function(){
    return {
        num:1,
    }
});

index.js文件:

define("index",["a","b"],function(a,b){
    return function(){
        console.log(a.num+b.num);
    }
});
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!