一、关于模块化编程
1.1传统代码的缺陷
随着网站功能逐渐丰富,网页中的js也变得越来越复杂和臃肿。
原有通过script标签来导入一个个的js文件这种方式已经不能满足现在互联网开发模式,并且这样写有很大的 缺点,
首先,加载的时候,浏览器会停止网页的渲染,加载的文件越多,那么网页失去响应的时间越长,
其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性大的模块一定要放在最后加载,当依赖关系很复杂的时候,代码的编写和维护都会非常困难。
所以我们需要团队协作、模块复用、单元测试等等一系列复杂的需求。
二、什么是模块化及模块化编程规范
模块化是只在处理某些问题的时候,按照一种分类思想对功能进行模块化的管理和使用。
2.1 require.js的诞生
require.js的诞生就是为了解决上述<script>加载问题。
AMD
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。即需要用define()来定义模块和require(),后续会介绍如何定义模块
对于模块化的发展暂不做详细介绍,百度很6的~
2.2require.js的的加载
先去官网下载最新版本,下载后,可以选择把它放在文件的根目录下或者是js文件下
<script type="text/javascript" src="js/require.js" ></script>
为了避免加载文件的时候造成网页失去响应。解决办法有俩个,一个是把文件的加载放在网页的最底部,一个是写成下面这样
<script src="js/require.js" async="async" ></script>
async属性表明这个文件需要异步加载,避免网页加载的时候失去响应。
加载require.js之后,需要加载我们自己的js文件代码,假设我们自己的代码文件是app.js,也放在根目录下,那么就写成下面这样
<script type="text/javascript" src="js/require.js" data-main="app"></script>
data-main这个属性的作用是指定我们网页程序的主模块。这个文件会第一个被require.js加载。由于require.js默认的文件后缀名是js,所以可以把app.js简写成app。
2.3 主模块的写法
一旦我们定义了主模块的js文件,意思这个就是整个网页的入口代码,所有的代码都从这开始运行。
如果主模块代码不依赖于其他任何模块,那么可以直接写JavaScript代码。
这样就失去了require.js的意义了,因为正常情况下,主模块都会依赖于其他模块,这时就需要遵守AMD规范。
看栗子,格式像这样
// app.js
require(['moduleA', 'moduleB', 'moduleC'], function (moduleA, moduleB, moduleC){
.......
});
require()函数
require()函数接受两个参数。
第一个参数是一个数组,表示程序所依赖的模块,即主模块依赖['moduleA', 'moduleB', 'moduleC']这三个模块;
第二个参数是一个回调函数,当前面指定的模块都加载成功后,它将被调用。加载的模块要以参数形式传入回调函数,从而在回调函数内部就可以使用这些模块。
举个栗子
require(["a","b","c"],function(A,B,C){
.....something.....
})
此时各个模块之间的关系是这样的,index.html和app.js和modules文件架 在根目录下,modules文件下有moduleA,moduleB,moduleC三个文件夹,里边分别有a.js 和 b.js 和 c.js。
require.js会先加载'a', 'b', 'c',然后再运行回调函数。主模块的代码就写在回调函数中。
2.4模块的加载
上一节的栗子中,主模块的依赖模块是'a', 'b', 'c'
文件名分别为a.js ,b.js ,c.js 然后自动加载
使用require.config()方法,我们可以对模块的加载行为进行自定义。require.config()就写在主模块(app.js)的头部。参数就是一个对象,这个对象的paths属性指定各个模块的加载路径。
栗子
//通过require.config方法来配置要导入的模块路径
require.config({
paths:{
//这里写要导入的路径和对应生成的模块名字
a:"modules/moduleA/a",
b:"modules/moduleB/b",
c:"modules/moduleC/c"
}
})
另注
1.如果模块在另一台主机上,也可以直接指定它的网址。
2.如果在这文件在同在其他弄一个文件夹内,比如都在lib文件架内,也可以指定baseUrl
2.5 AMD定义模块
AMD模块必须采用define()函数来定义
1.如果一个模块不依赖于其他模块,那么可以直接定义在define()函数中。
假设上栗中的a.js模块不依赖于其他模块,那么a.js中的代码如下
//define定义模块(不依赖于任何其他模块)
define(function () {
//加法运算
function add (x, y) {
return x + y;
}
return {
add : add
}
})
2.定义模块的时候如果有依赖于其他模块的话,我们就可以把第一个参数加入,第一个参数是一个数组,数组中就是依赖模块的名字或者路径。
如果这里写路径,注意是相对于app.js而言的路径
假设上栗中的b.js是依赖于jquery和a.js模块的,那么我们可以在第一个参数中引入,代码如下
//定义模块的时候如果有依赖于其他模块的话,我们就可以把第一个参数加入
//第一个参数是一个数组,数组中就是依赖模块的名字或者路径
//如果这里写路径需要注意是相对于app.js而言的路径
define(['jquery', './modules/moduleA/a'], function ($, A) {
//改变div颜色
function changeColor () {
$("#box").css({
//将div的北京变成蓝色
backgroundColor : "red"
});
}
//私有方法(说白了就是给自己用,不支持导出这个功能)
//计算两数之差
function mul (x, y) {
return x - y;
}
function addAndMul (x, y) {
//调用自身的两个函数
changeColor();
//用到了A模块里的功能和自身模块的私有方法
return A.add(x, y) + mul(x, y);
}
//因为后续数据需要反馈,所以一定要return
return {
addAndMul : addAndMul
}
});
当require()函数加载上面这个模块的时候,就会先加载他所依赖的模块。
3.为了方便理解下面内容,我们在c.js中也定义一个模块,其代码如下
define(['a', 'b'], function (A, B) {
function changeColor () {
$("#box").css({
//将div的背景变成蓝色
backgroundColor : 'blue'
});
}
function fn (x, y) {
changeColor();
return A.add(x, y) + B.addAndMul(x, y);
}
//因为后续数据需要反馈,所以一定要return
return {
fn : fn
}
});
2.6 模块的调用
此时我们定义了了三个模块。分别为a.js 和b.js 和 c.js,并在主模块app.js中引入了三个模块。
此时我们需要调用这三个模块,并发挥他们的功能,因此,在app.js中,我么除了引入三个模块的路径,还需要调用,代码如下
//通过require方法来导入模块
//如果上面配置了路径,数组里导入的时候直接就可以写上面路径对应的模块名字即可
//jquery比较特殊,叫jquery以外其他名字的话,导出模块会是undefined
//注意:模块支持导出的话,一定要对应上,不支持导出的模块,我们放在数组中的最后
require(['jquery', 'a', 'b', './modules/moduleC/c'], function (j, A, B, C) {
console.log($);
console.log(j);
//使用A模块的功能
console.log(A.add(1, 2));
//使用B模块的功能
console.log(B.addAndMul(1, 2));
//使用C模块的功能
console.log(C.fn(1, 2));
});
调用了app.js需要依赖的a,b,c三个模块,又进行了引用,那么此时app.js中的全部代码如下
//通过require.config方法来配置要导入的模块路径
require.config({
paths : {
//这里写要导入的路径和对应生成的模块名字
jquery : './lib/jquery',
a : './modules/moduleA/a',
b : './modules/moduleB/b'
}
})
//通过require方法来导入模块
//如果上面配置了路径,数组里导入的时候直接就可以写上面路径对应的模块名字即可
//注意:模块支持导出的话,一定要对应上,不支持导出的模块,我们放在数组中的最后
require(['jquery', 'a', 'b', 'modules/moduleC/c'], function (j, A, B, C) {
//使用A模块的功能
console.log(A.add(1, 2));
//使用B模块的功能
console.log(B.addAndMul(1, 2));
//使用C模块的功能
console.log(C.fn(1, 2));
});
注意代码中c模块的变化了么?你再仔细瞅瞅?说好的引用呢?
温馨提示,也要注意b和c里面的代码哦
如果在配置依赖的模块路径没有配置,那么在require调用的时候,在数组参数中写其路径也是可以的。
主页面index.html中没有实际的内容,只是引用,代码如下
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<style type="text/css">
#box{
width: 200px;
height: 200px;
background-color: pink;
}
</style>
</head>
<body>
<div id="box"></div>
<!-- 导入requireJS库 -->
<!-- data-main可以导入主入口文件app是指app.js -->
<script type="text/javascript" src="lib/require.js" data-main="app"></script>
</body>
</html>
此时运行index.html ** 注意 div的颜色。。**
你觉得对么?按照加载的顺序,应该最后加载c.js div的颜色怎么是红色的?应该是蓝色的才对?
因为我们在c.js中return的时候,又调用了b.js,所以最后又加载了b.js,所以,div变成了红色~那么,如何解决这个问题呢?用变量存起来不就好啦 -,c.js中的代码如下
define(['a', 'b'], function (A, B) {
function changeColor () {
$("#box").css({
backgroundColor : 'blue'
});
}
function fn (x, y) {
var num = A.add(x, y) + B.addAndMul(x, y);
changeColor();
return num;
}
return {
fn : fn
}
});
此时我们再输出一下结果
此篇文章助大家理解require.js,真正的require.js用法,可没这么简单呢。
不要吝啬赞美,喜欢就点赞啦