首页 > 代码库 > requirejs入门知识整理
requirejs入门知识整理
使用模块化开发处理的三大问题:
1.命名冲突;2.繁琐的文件依赖 3.实现异步非阻塞的文件加载,避免网页失去响应
模块化的设计使得JavaScript代码在需要访问“全局变量”的时候,都可以通过依赖关系,把这些“全局变量”作为参数传递到模块的实现体里,在实现中就避免了访问或者声明全局的变量或者函数,有效的避免大量而且复杂的命名空间管理。
requirejs以一个相对于baseUrl的地址来加载所有的代码。如果用了data-main属性,则该路径就是baseUrl,baseUrl亦可通过requirejs config手动设置。如果没有显式指定config及data-main,则默认的baseUrl为包含requirejs的那个HTML页面的所属目录。
可以使用R.js打包,但它需要node.js环境,当然它也可以运行在Java环境中如Rhino。
注意事项:
关于require(["xx"],function(xx){}):
1. require的第一个参数数组内的模块名必须和回调函数的形参一一对应。例如:require([‘selector‘,‘event‘],function($, E) {}。require函数用于加载模块依赖但并不会创建一个模块。
2. require方法里的这个字符串数组参数可以允许不同的值,当字符串是以“.js”结尾,或者以“/”开头,或者就是一个URL时,requirejs会认为用户是在直接加载一个JavaScript文件,否则,当字符串是类似”my/module”的时候,它会认为这是一个模块,并且会以用户配置的baseUrl和paths来加载相应的模块所在的JavaScript文件。config中的paths属性配置的地址也是以baseUrl为起始位置的(除以"/"开头或含有URL协议(如http:))。
3. 这里要指出的是,requirejs默认情况下并没有保证xx中方法的调用一定是在页面加载完成以后执行的,在有需要保证页面加载以后执行脚本时,requirejs提供了一个独立的domReady模块,需要去requirejs官方网站下载这个模块,它并没有包含在requirejs中。有了domReady模块,通过加入依赖就可以实现加载完后执行:require([‘"domReady!","xx"],function(xx){});
4. 对于require到的模块,比如a,b,requirejs会在当前的页面上插入为a.js和b.js分别声明了一个<script>标签,用于异步方式下载JavaScript文件。async属性目前绝大部分浏览器已经支持,它表明了这个<script>标签中的js文件不会阻塞其他页面内容的下载:
5. 避免模块的循环依赖,如果实在避免不了,需要做特殊处理。例如在(a依赖b,同时b依赖a),则在每个模块内部通过factory拿到的依赖模块参数会是undefined,需要将require作为依赖传入,通过require("x")方法再获取。如果a,b模块本身都是输出对象,那么可以分别把exports对象作为依赖传入,在exports的属性方法中可以直接使用a,b
6. Jsonp的使用:使用require(["……?Callback=define"],function(data){});格式,仅对json对象有效,其他字符串、数组、数字都无效。并且requirejs则只能获取该JSONP URL一次——后继使用require()或define()发起的的对同一URL的依赖(请求)只会得到一个缓存过的值。
关于define(id,deps,factory):
1. requirejs的模块定义可以使用 return {} 或return function也可以在依赖的exports对象中指定接口。
2. 尽量不要提供模块的ID,如AMD规范所述,这个ID是可选项,如果提供了,在requirejs的实现中会影响模块的可迁移性,文件位置变化会导致需要手动修改该ID。
3. 每个JavaScript文件只定义一个模块,模块名称和文件路径的查找算法决定了这种方式是最优的,多个的模块和文件会被优化器进行优化。
关于.config:
通过requirejs.config可以对baseUrl,paths,shim等属性进行设置,具体可查看requirejs中文API: http://makingmobile.org/docs/tools/requirejs-api-zh/
关于页面:
在<script>标签中指定data-main属性,则该目录也将作为baseUrl在其他模块定义时起作用,如果不指定该属性,各模块定义时需要指定具体路径,baseUrl也可以在require.config中指定。beseUrl指定之后require到的所有的路径就会以该值作为相对路径,但如果require的文件有.js后缀,将不会使用baseUrl,还是会以当前index.html所在的路径来定位。
AMD与CMD:
异步模块定义(AMD)是Asynchronous Module Definition的缩写,是 requirejs 在推广过程中对模块定义的规范化产出。
通用模块定义(CMD)是Common Module Definition的缩写,是SeaJS 在推广过程中对模块定义的规范化产出。
共性define:
define(id,deps,factory)
Factory可以是字符串、对象、函数,为字符串、对象时表示模块的接口就是该字符串、对象;为函数时表示模块的构造方法,执行构造方法可以得到模块向外提供的接口。
从requirejs,seajs看AMD与CMD区别:
复杂形式:define("manager",["student","class"],function(require, exports, module){});
1. 在书写上,AMD推崇依赖前置(在define的第二个参数中指定),CMD推崇依赖就近(在具体用到其他模块的时候在require进来)。AMD也支持CMD的写法:
但是在requirejs的源码中可以看到
requirejs会通过正则匹配把factory中的require("xx");语句转换为define(["xx"],factory)的形式,等价成了AMD推崇的依赖前置格式。所以说AMD这是在书写格式上提供了CMD的依赖就近,但是在运行逻辑上还是没变,属于伪支持。并且有些老旧浏览器语句转换的结果可能不准确,同时由于源码中正则表达式问题导致factory中特定注释方式也会导致匹配错误,所以最好用个懂得如何解析、转换正确的依赖数组的优化工具来打包优化一下。
2. 在依赖执行上,AMD会提前执行依赖的模块,CMD是延迟执行(具体require到的时候才)。AMD的运行核心思想就是early executing,而CMD的是as lazy as possible。这样做的好处:
1)完成整个过程AMD要比CMD快,通常可以带来更好的用户体验。AMD并行加载完所有依赖模块并执行完后才开始执行其他代码,依赖模块的执行顺序取决于哪个模块先加载完(ps:如果要控制依赖模块执行先后顺序,可以在require.config中配置);而CMD同样并行加载所有依赖模块(无论是在 requirejs 还是 SeaJS 里,在执行 factory 之前,都会确保依赖的模块已经下载好),但不会立即执行,等真正require的时候才开始执行(懒执行),依赖模块的执行严格按照依赖模块在代码中出现的顺序。
2)AMD能尽早发现异常。如果某个依赖模块出异常,factory被调用之前就会收到异常不执行,而CMD某个依赖模块出异常时factory很可能已经执行了一部分。
AMD从2.0开始支持延迟执行。
这样做的弊端:
如果在define内部某个依赖模块是通过if/else判断决定是否被用到,可能最终被没有被用到,但在factory调用前还是被提前执行了。
3. define(id,deps,factory)当factory为函数时有区别。这时CMD的factory只有一种形式:function(require, exports, module) {}。而AMD分两种情况,当deps参数为空时factory参数和CMD的一致,为function(require, exports, module) {};当deps参数非空时factory参数与deps中的依赖模块依次对应。如果在define内部要用到如require("xx");需要将"require"本身作为一个依赖注入到模块中。例如:
4. requirejs和seajs遵循的规范不同:、
requirejs 遵循的是 Modules/AMD 规范。
SeaJS 遵循的是 Mdoules/Wrappings 规范的 define 形式。
5. requirejs有一系列插件,功能强大,书写灵活;seajs追求简洁,职责单一,功能上可以支持css模块的加载。requirejs官方没有对css的模块化处理,但可以使用require插件实现。