常规代码
写JS代码时,我们根据自己的逻辑写出很长的代码后,往往一段时间后,如果重新去看自己写的代码时,会发现根本就不知道我写的是什么,以移动端画板为例,大约200行代码自上而下堆在在JS文件里,通篇的都是for循环和if else条件判断。这是典型’意大利面’式代码,由于过多使用if else跳转指令,显得代码结构异常混乱。
如果代码写成这样,我相信无论是谁都不会有兴趣去阅读你的代码,更严重地是在以后团队项目里,你无法跟别人合作,别人根本就不知道你写的什么,遇到一个bug时,得把所有的逻辑过一遍,然后才能测试代码。
分模块
为了解决这种代码结构混乱的情况,我们可以分模块来写。所谓一个模块就是划分一个独立的单元,让其功能独立,除了一些必需的全局变量,你不在依赖其它变量。每一个模块里的代码,为了更方便开发者阅读,你可以把代码里的一个一个小功能封装成函数。让你的主干内容一目了然。以移动端画板优化后的代码来看,与前面的来比,明显结构更加清晰。这就是按模块写代码的好处。
立即执行函数
以下面代码为例:
1 | let a |
上面这段代码可以表示为一个模块,它有画画的功能,但这个模块暴露了三个私有变量,分别是a,b,draw。如果同一项目里,其它模块的JS文件也用到了这三个变量,那么模块与模块之间就会相互影响。为了解决这个问题,可以用立即执行函数,那么这三个私有变量不再是全局变量,而是形成了独立的作用域。考虑到draw函数里,有用到了jquery和window对象。为了在模块内部调用全局变量,必须显式地将其它变量输入模块。所以我们需要对立即执行函数传入实参。代码如下:
1 | !function($,windwo){ |
模块化
将写好的每一个模块分别放在一个文件里,并建立script关联。这种行为就叫做模块化。
模块化的好处是让带代码逻辑更清晰,让合作更方便。然而各个模块之间是存在依赖关系的,二者顺序不能颠倒,这就表示不能进行同步加载了,只能进行异步加载。
比如说,要执行模块a,必须先执行模块b和模块c,而要执行模块c还得执行模块d。这种层层依赖关系不能同步执行,必须得异步执行。

AMD
AMD是”Asynchronous Module Definition”的缩写,表示‘异步模块定义’。它采用异步方式加载模块,模块的加载不影响后面语句的运行。所有依赖这个模块的语句,都定义在一个回调函数中,待模块加载完成后,便会执行后面的回调函数。
AMD采用require()语句来加载,
1 | require([module],callback) |
第一个参数[module]是一个数组,表示要加载的模块。第二个参数callback,表示加载成功之后的回调函数。模块的加载与回调的执行不是同步的,显然,AMD比较适合浏览器环境。
RequireJS
require.js是实现AMD的一个库,require.js在加载时,会启用脚本加载。
首先,得在本地安装require.js
1 | npm i -D requirejs |
假设我们的代码文件是main.js,放在scripts目录下。
1 | <script data-main="scripts/main" src="node_modules/requirejs/require.js"></script> |
data-main的属性是网页的主模块,上面中,require.js首先第一个加载main.js,其中后面的后缀.js可以省略。
主模块的写法
1 | require(['moduleA','moduleB'],function(moduleA,moduleB){ |
require接受两个参数,第一个参数是数组,表示所依赖的模块。第二个参数是一个回调函数,当加载模块成功后,它被调用,加载的模块会以参数的形式传入该函数,从而在回调函数内部可以使用这些模块。
例如,主模块依赖src目录下的hello.js和world.js这两个模块。那么主模块可以写成这样。
1 | require(['./src/hello','./src/world'],function(x,y){ |
模块的定义
根据主模块的写法,我们知道main.js依赖moduleAhello和moduleBworld这两个模块。
如果moduleA不在依赖其它模块,可以写成这样,
1 | //obj为一个对象 |
如果moduleB还依赖其它moduleC,可以写成这样,
1 | define([moduleC],function(xxx){ |
根据这种层层依赖关系,可以做出以下图:
其中每个模块相应的代码如下
1 | //main.js |
require.js会自动加载依赖,不需要考虑顺序,等全部文件加载完后才会执行主模块的回调。
自定义模块
主模块的依赖模块是[‘hello’,’world’],在默认情况下,require.js会默认依赖模块与主模块在同级目录下。
使用require.config()方法,可以自定义加载模块行为,这是默认情况的加载路径。
1 | require.config({ |
假如模块[‘hello’,’world’]在路径js/下,可以这样写,
1 | require.config({ |
假如所依赖的模块在另一个主机上,可以直接指定它的网址。
1 | require(['jquery'],function($){ |