为什么要模块化,如何定义一个amdcmd的模块

JavaScript06

为什么要模块化,如何定义一个amdcmd的模块,第1张

模块化更一种开发规范,比如cmd amd 是为了更好的解藕,比如一个网站,按照不同的模块来开发,比如你有个评论区,a 项目有,b 项目有,如果仅是单纯的模块开发,这个js 文件你就可以单独来回引用,

更比如 ,一个页面 分好多个功能, 这时候你要是都写在一个js 中 会越来越大,

而你把他分成不同的模块,

比如评论是一块

分页又是一块,

已经上线,或你不做了,后期别人拉手,或你接手别人的项目, 这时候来个需求让你把分页去掉,或修改 你可以清楚的找到对应模块文件 进行修改 或去掉

模块是自定义的,

组件,更想当于一个通用的东西,有的分功能组件,有的分业务组件

大图切换,这种就是单纯的一个效果展示,只要调用就ok

一个分页,也是只单纯的调用,

组件更是一个多处都可以使用 ,不需要再单独开发的

模块化在项目中十分的重要,一个复杂的项目肯定有很多相似的功能模块,如果每次都需要重新编写模块肯定既费时又耗力。但是引用别人编写模块的前提是要有统一的“打开姿势”,如果每个人有各自的写法,那么肯定会乱套,下面介绍几种JS的模块化的规范。

一:模块化进程一:script标签

这是最原始的 JavaScript 文件加载方式,如果把每一个文件看做是一个模块,那么他们的接口通常是暴露在全局作用域下,也就是定义在 window 对象中,不同模块的接口调用都是一个作用域中,一些复杂的框架,会使用命名空间的概念来组织这些模块的接口。

缺点:

1、污染全局作用域

2、开发人员必须主观解决模块和代码库的依赖关系

3、文件只能按照script标签的书写顺序进行加载

4、在大型项目中各种资源难以管理,长期积累的问题导致代码库混乱不堪

二:模块化进程二:CommonJS规范

该规范的核心思想是允许模块通过require方法来同步加载所要依赖的其他模块,然后通过 exports 或 module.exports 来导出需要暴露的接口。

require("module")

require("../file.js")

exports.doStuff = function(){}

module.exports = someValue

优点:

1、简单并容易使用

2、服务器端模块便于重用

缺点:

1、同步的模块加载方式不适合在浏览器环境中,同步意味着阻塞加载,浏览器资源是异步加载的

2、不能非阻塞的并行加载多个模块

module.exports与exports的区别

1、exports 是指向的 module.exports 的引用

2、module.exports 初始值为一个空对象 {},所以 exports 初始值也是 {}

3、require() 返回的是 module.exports 而不是 exports

exports示例:

// app.js

var circle = require('./circle')

console.log(circle.area(4))

// circle.js

exports.area = function(r){

return r * r * Math.PI

}

module.exports示例:

// app.js

var area = require('./area')

console.log(area(4))

// area.js

module.exports = function(r){

return r * r * Math.PI

}

错误的情况:

// app.js

var area = require('./area')

console.log(area(4))

// area.js

exports = function(r){

return r * r * Math.PI

}

其实是对 exports 进行了覆盖,也就是说 exports 指向了一块新的内存(内容为一个计算圆面积的函数),也就是说 exports 和 module.exports 不再指向同一块内存,也就是说此时 exports 和 module.exports 毫无联系,也就是说 module.exports 指向的那块内存并没有做任何改变,仍然为一个空对象{},也就是说area.js导出了一个空对象,所以我们在 app.js 中调用 area(4) 会报 TypeError: object is not a function 的错误。

总结:当我们想让模块导出的是一个对象时, exports 和 module.exports 均可使用(但 exports 也不能重新覆盖为一个新的对象),而当我们想导出非对象接口时,就必须也只能覆盖 module.exports 。

三:模块化进程三:AMD规范

由于浏览器端的模块不能采用同步的方式加载,会影响后续模块的加载执行,因此AMD(Asynchronous Module Definition异步模块定义)规范诞生了。

AMD标准中定义了以下两个API

1、require([module], callback)

2、define(id, [depends], callback)

require接口用来加载一系列模块,define接口用来定义并暴露一个模块。

示例:

define("module", ["dep1", "dep2"], function(d1, d2){

return someExportedValue

})

require(["module", "../file"], function(module, file){ /* ... */ })

优点:

1、适合在浏览器环境中异步加载模块

2、可以并行加载多个模块

缺点:

1、提高了开发成本,代码的阅读和书写比较困难,模块定义方式的语义不顺畅

2、不符合通用的模块化思维方式,是一种妥协的实现

四:模块化进程四:CMD规范

CMD(Common Module Definition)规范和AMD很相似,尽量保持简单,并与CommonJS和Node.js的 Modules 规范保持了很大的兼容性。在CMD规范中,一个模块就是一个文件。

示例:

define(function(require, exports, module){

var $ = require('jquery')

var Spinning = require('./spinning')

exports.doSomething = ...

module.exports = ...

})

优点:

1、依赖就近,延迟执行

2、可以很容易在 Node.js 中运行

缺点:

1、依赖 SPM 打包,模块的加载逻辑偏重

AMD和CMD的区别

AMD和CMD起来很相似,但是还是有一些细微的差别,让我们来看一下他们的区别在哪里:

1、对于依赖的模块,AMD是提前执行,CMD是延迟执行。

2、AMD推崇依赖前置;CMD推崇依赖就近,只有在用到某个模块的时候再去require。看代码:

// AMD

define(['./a', './b'], function(a, b){ // 依赖必须一开始就写好

a.doSomething()

// 此处略去 100 行

b.doSomething()

...

})

// CMD

define(function(require, exports, module){

var a = require('./a')

a.doSomething()

// 此处略去 100 行

var b = require('./b')

// 依赖可以就近书写

b.doSomething()

// ...

})

3、AMD 的 API 默认是一个当多个用,CMD 的 API 严格区分,推崇职责单一。

五:模块化进程五:ES6模块化

EcmaScript6标准增加了JavaScript语言层面的模块体系定义。ES6 模块的设计思想,是尽量的静态化,使得编译时就能确定模块的依赖关系,以及输入和输出的变量。CommonJS和AMD模块,都只能在运行时确定这些东西。

在 ES6 中,我们使用export关键字来导出模块,使用import关键字引用模块。需要说明的是,ES6的这套标准和目前的标准没有直接关系,目前也很少有JS引擎能直接支持。因此Babel的做法实际上是将不被支持的import翻译成目前已被支持的require。

尽管目前使用import和require的区别不大(本质上是一回事),但依然强烈推荐使用import关键字,因为一旦JS引擎能够解析ES6的import关键字,整个实现方式就会和目前发生比较大的变化。如果目前就开始使用import关键字,将来代码的改动会非常小。

示例:

import "jquery"

export functiondoStuff(){}

module "localModule" {}

优点:

1、容易进行静态分析

2、面向未来的 EcmaScript 标准

缺点:

1、原生浏览器端还没有实现该标准

2、全新的命令字,新版的 Node.js才支持

现在常用的一种javascript的方法是在当前的html文档中插入一个script标签,在标签中引入script脚本Js代码var__includes__=newArrayArray.prototype.indexOf=function(obj){for(vari=0i<this.lengthi++){if(this[i]==obj)returni}return-1}Array.prototype.add=function(obj){this[this.length]=obj}functioninclude_js(js){if(__includes__.indexOf(js)-1)return__includes__.add(js)varhead=document.getElementsByTagName('head')[0]script=document.createElement('script')script.src=jsscript.type='text/javascript'head.appendChild(script)}当你只是在你的htmlw文档中使用这个方法的时候,一切OK,这其实是script的标签的一种快捷的写法而已。但是,如果你在一个javascript使用这个方法,问题就来了,比如我在test.js中使用include_js("test1.js"),在test1.js中有一个变量test1是在test.js中要使用的,在webkit中尽然出现了test1变量未定义的错误,我不知道ie和firefox是否有这种问题,我想可能是include_js本身不是同步执行导致的,所以我只好使用以下方法来完善inlcude_jsJs代码var__includes__=newArrayArray.prototype.indexOf=function(obj){for(vari=0i<this.lengthi++){if(this[i]==obj)returni}return-1}Array.prototype.add=function(obj){this[this.length]=obj}functionxhttp(url,callback){varrequest=nullif(typeofXMLHttpRequest!='undefined'){request=newXMLHttpRequest()}elseif(typeofActiveXObject!='undefined'){request=newActiveXObject('Microsoft.XMLHTTP')}request.open('GET',url,true)request.onreadystatechange=function(){if(request.readyState==4){callback(request.responseText)}}request.send(null)}functionadd_scripts(jss,callback){varfunc=function(jss,idx,callback){if(idx==jss.length){callback()return}add_script(jss[idx],function(){func(jss,++idx,callback)})}func(jss,0,callback)}functionadd_script(js,callback){if(__includes__.indexOf(js)-1){callback()return}__includes__.add(js)xhttp(js,function(js_content){varhead=document.getElementsByTagName('head')[0]script=document.createElement('script')head.appendChild(script)// script.innerHTML=js_content//原帖是这个...本人测试这行..无效 必须用text属性赋值script.defer=truescript.type='text/javascript'script.language='javascript'//本人测试修正..添加script.text=js_content//本人测试修正..添加//zfrong 09.5.20callback()})}functioninclude_js(js){if(__includes__.indexOf(js)-1)return__includes__.add(js)varhead=document.getElementsByTagName('head')[0]script=document.createElement('script')script.src=jsscript.type='text/javascript'head.appendChild(script)}当我在html文档中引入的时候,我用 include_js,当我在js文件中引入js时候,我使用add_scripts,add_scriptJs代码add_scripts(['test1.js','test2.js']),add_scripts方法使用了xmlhttp来读入js内容,并把读入的内容的写到一个新的script标签内,读入是异步执行的,当执行完毕后,会调用callback、