一,主要内容
- 什么是模块化
- 为什么使用模块化
- 模块嵌套
- Requirejs使用
1.1 模块化产生
模板就是实现特定功能的一组方法
只要把不同的函数(以及记录状态的变量)简单地放在一起,就算是一个模块。
1.2为什么使用模块化
模块的优点
• 可维护性。 因为模块是独立的,一个设计良好的模块会让外面的代码对自己的依赖越少越好,这样自己就可以独立去更新和改进。
• 命名空间。 在 JavaScript 里面,如果一个变量在最顶级的函数之外声明,它就直接变成全局可用。因此,常常不小心出现命名冲突的情况。使用模块化开发来封装变量,可以避免污染全局环境。
• 重用代码。 我们有时候会喜欢从之前写过的项目中拷贝代码到新的项目,这没有问题,但是更好的方法是,通过模块引用的方式,来避免重复的代码库
1.3 js作用域问题
Js的作用域是函数作用域
代码:
var name='zhangsan';//全局
function demo(){
var name='lisi';//局部
}
for(var i=0;i<100;i++){
var age=10;//全局
}
//下面代码执行的结果?
var arr=[10,20,30,40,50];
for(var i=0;i<arr.length;i++){
arr[i]=function(){
console.log(i);
}
}
arr[2]();//打印结果?
结果分析:
怎么获取当前的下标对应的数据
//闭包解决 变量作用域问题---
var arr=[10,20,30,40,50];
for(var i=0;i<arr.length;i++){
arr[i]=(function(i){
return function(){
console.log(i);
}
})(i);
}
arr[3]();//打印结果?
二、 模块化实现
2.1 模块的写法
一、原始写法
蓝莓派---分页器 addPage() addEvent()
function m1(){
//... }
function m2(){
//...
}
上面的函数m1()和m2(),组成一个模块。使用的时候,直接调用就行了。
这种做法的缺点很明显:"污染"了全局变量,无法保证不与其他模块发生变量名冲突,而且模块成员之间看不出直接关系。
三、对象写法
为了解决上面的缺点,可以把模块写成一个对象,所有的模块成员都放到这个对象里面。
var module1 = new Object({
_count : 0,
m1 : function (){
//...
},
m2 : function (){
//...
}
});
上面的函数m1()和m2(),都封装在module1对象里。使用的时候,就是调用这个对象的属性。
module1.m1();
但是,这样的写法会暴露所有模块成员,内部状态可以被外部改写。比如,外部代码可以直接改变内部计数器的值。
module1._count = 5;
四、立即执行函数写法
使用 立即执行函数,可以达到不暴露私有成员的目的。
var model1=(function(){
var count=0;
var data={
uname:123
}
function setData(obj){
}
var fun1=function (){
console.log("this is function1");
};
var fun2=function (){
console.log("this is function2");
}
return {
fun1:fun1,
fun2:fun2
}
})();
使用上面的方法,外部代码无法读取count变量
console.log(model1.count)//undefined
这是JavaScript模板的基本写法。
4.1 模块的放大模式
如果一个模块很大,必须分成几个部分,或者一个模板需要继承另一个模块,这时就有必要采用‘放大模式’
实现一个获取DOM元素 设置DOM元素
/*模块的嵌套 模块之前是独立的 不能直接相互访问*/
var domElement=(function(){
function getElement(id){
return document.getElementById(id);
}
return {
getElement:getElement
}
})();
var divMoudle=(function(domElement){
function setName(id,value){
return domElement.getElement(id).innerHTML=value;
}
return {
setName:setName
}
})(domElement);
divMoudle.setName('root','hello!!');
【代码演示】
模块的嵌套 实现在当前的模块使用其他模块里面的方法—传递进入
//传递一个jquery 比如下面使用jquery对象
var domElement=(function(){
function getElement(id){
return document.getElementById(id);
}
return {
getElement:getElement
}
})();
var divMoudle=(function(domElement,$){
function setName(id,value){
return domElement.getElement(id).innerHTML=value;
}
console.log($);
return {
setName:setName
}
})(domElement,$);//jQuery
divMoudle.setName('root','hello!!');
4.2 模块的宽放大模式
在浏览器环境中,模块的各个部分通常都是从网上获取的,有时无法知道哪个部分会先加载。如果采用上一节的写法,第一个执行的部分有可能加载一个不存在空对象,这时就要采用"宽放大模式",这样一个比较完整的模块也就出来了
与‘放大模式’相比,‘宽放大模式’就是‘立即执行函数’的参数可以是空对象
//模块的放大模式 模块的宽大模式 容错处理
var domElement=(function(){
function getElement(id){
return document.getElementById(id);
}
return {
getElement:getElement
}
})();
domElement=null;
var divMoudle=(function(domElement,$){
//判断domElement是否存在
if(domElement){
function setName(id,value){
return domElement.getElement(id).innerHTML=value;
}
console.log($);
return {
setName:setName
}
}else{
console.log('domElement对象不存在')
}
})(domElement,$);//jQuery
divMoudle.setName('root','hello!!');
4.3 输入全局变量
独立性是模块的重要特点,模块内部最好不与程序的其他部分直接交互。
为了在模块内部调用全局变量,必须显式地将其他变量输入模块。
var module1 = (function ($, swiper) {
//...
})(jQuery, swiper);
上面的module1模块需要使用jQuery库和swiper库,就把这两个库(其实是两个模块)当作参数输入module1。这样做除了保证模块的独立性,还使得模块之间的依赖关系变得明显。
五、Requirejs
5.1 什么是RequireJs
RequireJS 是一个JavaScript模块加载器。
在ES6出现之前,JS不像其他语言同样拥有“模块”这一概念,于是为了支持JS模块化,出现了各种各样的语言工具,如webpack,如ReuqireJS。
为什么使用RequireJS
• 模块化:模块化就是将不同功能的函数封装起来,并提供使用接口,他们彼此之间互不影响
• 不会阻塞页面:RequireJS,会在相关的js加载后执行回调函数,这个过程是异步的,所以它不会阻塞页面
• 按需加载:平时我们写html文件的时候,在底部可能会引用一堆js文件。在页面加载的时候,这些js也会全部加载。使用require.js就能避免此问题。举个例子,比如说我写了一个点击事件,放到了一个js文件里,并在html引用,在不使用require.js的情况下,页面加载它跟着加载,使用后则是什么时候触发点击事件,什么时候才会加载js
总结:
requireJS 可以很轻易的将一个项目中的JavaScript代码分割成若干个模块(module)。并且requireJS推荐一个模块就是一个文件,所以,你将获得一些零碎的具有互相依赖关系的JS文件。模块化的好处也浅显意见,那就是大大增强代码的可读性、易维护性、可扩展性、减少全局污染等。
5.2使用步骤
1.按照如下目录结构,创建一个文件夹
目录结构
注:马赛克部分是后续写的模块;jquery版本需要用1.7.0及以上的;main.js是主要的配置文件;html文件名称随意
2.在html引入require.js
<script type="text/javascript" data-main="js/script/main" src="js/lib/require.js"></script>
require.js可以npm下载,或者去官网下载。
script里有个data-main属性,require.js会在加载完成以后通过回调方法去加载这data-main里面的js文件,所以这个js文件被加载的时候,RequireJS已经加载执行完毕
3.配置main.js,这里主要介绍如何在main.js里引入jquery
4.引入jquery文件
文件加载效果
注意:创建的文件需要main.js加载 否则不能使用
六、 实例
6.1 Requirejs模块 AMD模块的写法
require.js加载的模块,采用AMD规范。也就是说,模块必须按照AMD的规定来写。
具体来说,就是模块必须采用特定的define()函数来定义。如果一个模块不依赖其他模块,那么可以直接定义在define()函数之中
6.1.1 定义模块 简单名称/价值对
如果模块没有任何依赖关系,并且它只是一个名称/值对的集合,那么只需将对象文字传递define():
define({
color: "black",
size: "unisize"
});
6.1.2定义函数
define(function () {
//....
return {
color: "black",
size: "unisize"
}
});
6.1.3 具有依赖关系的定义函数
如果模块具有依赖关系,则第一个参数应该是一个依赖关系名称数组,第二个参数应该是一个定义函数。一旦所有依赖关系被加载,该函数将被调用来定义模块。该函数应该返回一个定义模块的对象。依赖关系将被传递给定义函数作为函数参数,其顺序与依赖关系数组中的顺序相同
define(['data'],function(data){
console.log(data)
})
注意:依赖注入 (想使用data文件 注入data里面就好了,哪里使用注入就好了) 依赖前置
6.1.4 定义与简体CommonJS的包装一个模块
如果您希望重用一些以传统CommonJS模块格式编写的代码,可能难以重新使用上面所使用的依赖关系数组,并且您可能希望将依赖项名称直接对齐到用于该方法的本地变量依赖(nodejs的模块化)
define(function(require,exports,module){
var data=require("data");
console.log(data)
})
这是什么时候使用什么时候引用 (依赖就近)
其他的定义模块使用我们不再去看 我们常用这四种定义方式
6.1.5 获取dom元素
创建一个DOMElement.js:
define(['jquery'],function(){
//定义函数
function setHtml(className,value){
$("."+className).html(value);
}
return {
setHtml:setHtml
}
})
Index.js
define(["data",'DOMElement'], function(data,DOMElement) {
console.log(data);
DOMElement.setHtml('box','hello world');
});
6.2 Requirejs网易云音乐
实现 网易云音乐数据列表展示,点击音乐播放
网址:http://iwenwiki.com:3000
/personalized/newsong’,//推荐歌单 /personalized/newsong
步骤