*解析
模块是可以实现特定功能的一组代码。
由于JS不能handle超大规模代码,所以要借鉴其它语言处理这种问题的方式:
把逻辑相关的代码组织到同一个区域,区域内部相对独立和外界互不干扰;
外部使用时直接引用对应的区域即可。
模拟类似的功能,用来隔离、组织复杂的JavaScript代码,我们称为模块化。
*作用
1 解决命名冲突
2 解决互相依赖
3 提高代码的可读性
4 提高代码的复用性
*规范
**CommonJS
解析:
服务器端使用的模块规范 例如 Node.js
使用:
1 定义模块 一个文件 = 一个模块 = 一个单独的作用域
2 模块输出: 唯一出口module.exports
对象,存放输出内容
3 加载模块: 使用require
方法读取文件并执行,返回ta内部的module.exports
对象
举例:
Model-1.js 1 定义模块
var name = 'jrg';
function a(){...} name是模块的内部变量,只有文件内的函数可以使用
function b(){...}
module.exports={ 2 模块输出 (导出接口)
a:a,
b:b
}
Model-2.js *新模块 = 新文件
function c(){...}
module.exports = c
main.js 3 加载模块
var xx = require('./Model-1');
var dd = require('./Model-2'); 使用哪个模块再引入
xx.a()
dd.c() 然后就可以调用模块提供的方法
**问题
Q
CommonJS 规范在服务器端这么方便,那么浏览器端可以用吗?
A
NO!
R
服务器端的文件在本地 => 同步加载
;浏览器端的文件在远程服务器 =>异步加载
····所以CommonJS模块在浏览器环境中无法正常加载。
····浏览器端需要另外的规范来封装模块 => AMD & CMD
**AMD
解析:
浏览器端使用的模块规范之一 Asynchronous Module Definition异步模块定义
define函数,定义模块--------------------------------------------------
语法:
define(id?, dependencies?, factory)
参数:
id:模块名;如果没有参数,默认为文件名。
dependencies:一个数组;当前模块 [依赖的模块名称集合]。
factory:工厂函数;模块初始化要执行的函数(只执行一次) or 对象(模块的输出值)。
require函数,加载模块-------------------------------------------------
语法: require([dependencies], function(){});
参数: [dependencies]是一个数组,表示所依赖的模块;
function(){} 是一个回调函数,当前面指定的模块都加载成功后,以参数形式传入该函数,
然后函数内部就可以使用这些模块了。
举例----------------------------------------------------------------------------
myModule1.js 定义模块
define(['dependency'], function(){
function a(){...} // 使用面向对象方式封装好的组件
return a, // 留出接口
});
main.js 加载模块
require(['myModule1','myModule2'], function (myModule1,myModule2){
myModule1.a(); // 调用组件
});
**CMD
解析:
国内发展出来的模块规范Common Module Definition通用模块定义
define函数,定义模块---------------------------------------------------------
语法:
define(function(require, exports, module){ });
参数:
require 是一个方法,获取其他模块的接口
exports 是一个对象,向外提供模块接口
module 是一个对象,存储了一些属性和方法
举例--------------------------------------------------------------------------
ModuleA.js 定义模块
define(function(require, exports, module) { function的三个参数固定不变
var a = require('a.js') 函数内部,用到谁就引入谁
a(); 写法类似commonJS
ModuleA.exprots = xx;
});
main.js 加载模块
seajs.use(['ModuleA.js'], function(ModuleA){ ... });
模块化工具:
▶由于不是JavaScript原生支持,
使用AMD
需要用到对应的库函数RequireJS
;
使用CMD
需要用到对应的库函数SeaJS
;
其实AMD
和CMD
分别 是RequireJS
和SeaJS
在推广过程中对模块定义的规范化产出;
同样倡导模块化开发理念,核心价值是让 JavaScript 的模块化开发变得简单自然。
▶AMD
和CMD
的区别?
** 1** 关于依赖的模块,加载都是异步,执行处理不同
AMD
:推崇依赖前置,提前执行(依赖先执行)
CMD
:推崇依赖就近,延迟执行(运行到需加载,根据顺序执行)
** 2** 关于API
AMD
:API根据使用范围有区别,但使用同一个api接口
CMD
:每个API的职责单一
**requireJS
▶requireJS
主要解决两个问题:
1 实现js文件的异步加载,避免网页失去响应;
2 管理模块之间的依赖性,便于代码的编写和维护。
如果
不使用`requireJS`就要单独加载每个js文件,像这样:
<script src="1.js"></script>
<script src="2.js"></script>
<script src="3.js"></script>
缺点
1 加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;
2 由于js文件之间存在依赖关系,因此必须严格保证加载顺序,依赖性最大的模块必须放到最后,
当依赖关系很复杂的时候,代码的编写和维护都会变得困难。
**总结
至此,已经了解模块规范,可以写出符合规范的 模块文件了!
以AMD为例,梳理一下使用过程:
1 下载require.js文件,放入根目录
2 页面引入requireJS库<script src="http://require.js"></script> => 主文件
3 页面引入主文件 <script src="http://main.js"></script> => 加载模块
4 主文件 => 已放入根目录的模块文件
Tip:
主文件=>
加载模块=>
require函数=>
语法require([dependencies], function(){})
模块文件=>
定义模块=>
define函数=>
语法define([dependencies], factory)
**r.js
这是一个打包工具
作用
把所有的模块和框架js文件
压缩到同一个js文件
里,
Before:
引入requireJS库=>
引入主文件=>
查找模块
After :
引入requireJS库=>
查找模块
使用
1 下载r.js文件,放入根目录
2 配置压缩文件build.js
3 打开node,进入目标DEMO-AMD
4 执行命令行node r.js -o build.js
,会自动生成压缩文件
5 将页面上的主文件地址main.js
替换为压缩后的文件index.merge.min.js
*应用
▼DEMO-AMD
▶index.html
▶CSS
▼JS
▼lib 框架&库目录
▷require.js
▷r.js
▷jquery.js
......
▼app 组件目录
▷gotop.js
......
▶main.js 主文件
▶build.js 压缩配置文件
▼dist 输出目录
▷index.merge.min.js
■ index.html
页面加载 <script src="http://js/lib/require.js" data-main="js/main.js"></script>
□ src 先加载库 => 执行require.js => 解析后指向主文件
□ data-main 自定义属性 => 执行main.js => 主文件里是所有模块的入口
■ main.js 主文件 =>
requirejs.config({ □ 配置路径,方便查找
baseUrl: "./js/app", 默认文件夹(加载时写直接写里面的模块名)
paths: { 特殊文件/夹(相对于baseUrl)
'jquery': '../lib/jquery' 指向模块 (加载时写模块名)
'lib':'../lib' 指向文件夹(加载时写lib下模块名)
}
});
requirejs(["GoTop","lib/xx",...],
function(GoTop,$,...) { □ 加载([依赖模块],工厂函数)
new GoTop(); 模块加载完毕,作为参数传入工厂函数
...... 执行工厂函数
});
● 这里的baseUrl是main.js相对于index.html=> 从index出发找到app
● requirejs(["xxx"]) => 如果没有工厂函数,直接加载并执行文件
■ build.js 压缩配置文件 =>
({ □ 配置路径
baseUrl: ".", 两个baseUrl指向要保持一致(都指向app)
paths: {
'jquery': './lib/jquery',
'GoTop':'./com/GoTop',
......
},
name: "main", 配置入口
out: "./dist/index.merge.min.js" 配置出口(自定义)
});
● 这里的baseUrl是build.js相对于自身的,=> 从build 出发找到 app
但是!程序员永远有更便捷的方法 =>
webpack