前端模块化

前端模块化

模块就是实现一个特定功能的一组代码,通常一个文件就是一个模块,我们前端模块化中,一个模块指的就是一个js文件。

模块化是把一个复杂的应用分解为很多的可管理的模块。

模块化的标准

有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块。这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写法,岂不是乱了套! 【螺丝与螺帽】

  • CommonJS:是一个模块化的标准,Node.js在使用的模块化标准。适用与后端开发的标准。
  • AMD(Async Module Definition):在CommonJS基础上衍生出来的适用于前端开发的模块化标准。
    • 代表:requirejs,国外的,支持程度高,更通用
  • CMD(Common Module Definition):在CommonJS基础上衍生出来的适用于前端开发的模块化标准。
    • 代表:seajs ,国内的,玉伯,支持程度差,不维护了。

【服务器端的模块化规范】

  • Commonjs(nodejs)
    • CommonJS:是一个模块化的标准,Node.js在使用的模块化标准。适用与后端开发的标准。

【浏览器端的模块化规范】

  • AMD(requirejs)
    • AMD(Async Module Definition)
    • 在CommonJS基础上衍生出来的适用于前端开发的模块化标准。
    • 代表:requirejs,国外的,支持程度高,更通用
  • CMD(seajs)规范
    • CMD(Common Module Definition)
    • 在CommonJS基础上衍生出来的适用于前端开发的模块化标准。
    • 代表:seajs ,国内的,玉伯,支持程度差,不维护了。

require.JS

requirejs是AMD规范的一个实现,require.js是一个javascript文件,也是一个模块加载器。

requirejs主要提供两大功能:

  • 加载模块:可以加载AMD规范的模块,也可以是普通的模块,异步加载,并且能够管理模块之间的依赖。
  • 定义模块:有助于不同模块之间的共享与依赖。

使用注意

  • 引入文件
  • 定义模块
  • 加载模块
  • 定义模块和加载模块
    • 不能使用后缀,会自动加上.js后缀;
    • 路径是相对当前html文件
    • 一般使用绝对路径

API接口

requirejs一共对外暴露三个全局变量

requirejs:加载模块,requirejs===require

require:作用:加载模块(异步),普通的js文件(非AMD规范的模块),AMD规范的模块

加载模块时,js文件会执行。

1
2
3
4
5
6
//第一个参数:数组,需要加载的模块,异步加载的
//第二个参数:回调函数,在模块都加载完毕之后,回调函数才会执行
//require(["js/a", "js/b", "js/c"]);
require(["js/a", "js/b", "js/c"], function () {
console.log("我是最后执行的");
});

define:定义模块,使用define函数可以定义一个AMD规范的模块;

好处:require独特的定义方式,避免了全局变量的污染。

模块之间的依赖

在使用define定义一个模块时,如果模块依赖于另一个模块,我们可以通过define的第一个参数去指定。

优点:可以在声明模块的时候就可以定义好依赖,那么使用的时候,就不用在管模块之间的依赖了。使用起来会更加的方便。

注意点:一个js文件就是一个模块,因此在一个js文件中,只能有一个define

模块的输出

模块的输出(对外暴露的东西)

可以return一个定义模块中的全局变量,在加载模块的时候使用一个形参来接收,加载模块的回调函数就可以使用这个变量了,如果是多个变量的话,可以return一个对象;

如果加载模块时依赖多个模块,传递的形参要与依赖模块的位置一一对应,即使有的依赖模块没有模块输出(return),也要用来占位置,所以一般写法是没有模块输出的依赖模块写在后面位置;

如果模块仅仅是执行一些代码实现某个功能,那么直接引入该模块,实现对应的功能即可,不需要返回值。如果该模块的存在是为了提供一些方法、对象或者其他一些内容,那么这个模块需要返回值。

在定义模块的时候,我们通常需要对外返回一些内容,因为使用define定义模块,实质就是一个函数,因此可以使用return返回模块的输出。

1
2
3
4
5
6
7
8
9
10
11
12
define([], function () {
var util = {
sayHello:function (name) {
console.log("大家好,我是"+name);
},
sleep:function () {
console.log("好困啊,我想要睡觉");
}
};
//通过return返回模块的输出
return util;
});

想要获取模块的输出,在function中传递形参即可。

1
2
3
4
5
6
//参数1:加载的模块列表
//参数2:模块加载完成后,会执行的回调函数,形参就是模块的返回值
require(["b"], function (util) {
util.sayHello("张三");
util.sleep();
});

如果依赖于多个模块,应该怎么办?

1
2
3
4
5
6
7
8
//1. 如果依赖于多个模块,并且多个模块都有输出,那么我们需要使用参数一一对应,
//2. 这样就可以获取到多个模块的输出了。
//3. 即便某个模块没有输出,我们也要指定一个参数占着位置。
//4. 通常我们会把有输出的模块写在前面,没有输出的模块写在后面。这样方便写参数。
require(["js/util", "js/common", "js/test"], function (util, demo) {
console.log(util);
console.log(demo);
});

配置路径和别名

配置相关,一般用一个单独的js文件来配置;

  • 使用require来加载某个模块时,路径会以当前html文件作为参考。
  • require可以使用config来配置一些内容,比如基础路径
  • 但是对于大型项目html的位置可能不一样,所以需要使用绝对路径;

require.config({});

require.config({});方法可以配置路径和别名;

参数是一个对象;

baseUrl:"js/",

  • 用来配置文件路径;
  • js/是相对路径,相对于html文件的路径;
  • /lib/是绝对路径,相对于当前端口下的lib文件
  • 如果设置了文件路径,那么在定义、加载、别名的时候都会在前面自动加上这个地址;

paths:{}

  • 用来设置模块文件的别名;
  • 配置好别名之后,就可以直接使用别名加载;
  • "别名":"路径地址/文件名";不能带后缀;

具名模块与匿名模块注意

define声明时,第一个参数可以是该模块的名字,如果声明了模块名,在配置时,模块别名就不能够调整了。

define( "jquery", [], function() {return jQuery;} );

jQuery声明了模块名字;所以别名只能使用”jquery”;很多jQuery插件都依赖这个名字;

shim:{}

用来加载非AMD规范的模块;

requirejs可以通过require函数加载模块。在加载不是AMD规范的模式时,会出现问题,需要做特殊的处理。

市面上有很多模块,并没有实现AMD模块化规范,没有实现模块化的模块也可以使用requirejs去加载,但是require加载时,仅仅会执行这个js文件,并不能帮助这个模块实现依赖的加载,也无法获取到这个模块的输出。

我们可以通过require.config为没实现AMD规范加载依赖项。

可以在对象内配置多个非AMD规范的模块;使用前先配置别名;

"别名":{}

deps:["依赖项1","依赖项2"]

exports: "输出结果"

  • 输出结果是一个非AMD模块中的全局变量同名字符串;
  • 通过这个字符串查找非AMD模块中的同名变量;

注意点:

  • 想使用模块化加载第三方库的时候,首先需要检查第第三方库是否支持模块化。
  • 在文件中搜索AMD;
  • 第三方库支持模块化的,可以直接使用require进行加载。