requireJS
参考:http://www.ruanyifeng.com/blog/2012/11/require_js.html
什么是模块化?
以前我们可能会这样做代码分工
//modules.js ----- var moduleA = {......} var moduleB = {......} var moduleC = {......}
然后每个页面都引入JS
不管这个页面用到了几个模块,你都要把整个文件加载进来,于是我们就想,不如分开吧然后我们的代码变成了这样://moduleA.js ----- var moduleA = {......} //moduleB.js ----- var moduleB = {......} //moduleC.js ----- var moduleC = {......}但是,由于各个模块之间存在相互引用的依赖关系,所以我们这样引入JS:假设A引用了C,而C引用了B,ABC里面都用到了JQ,这样的写法有很大的缺点。首先,加载的时候,浏览器会停止网页渲染,加载文件越多,网页失去响应的时间就会越长;其次,由于js文件之间存在依赖关系,因此必须严格保证加载顺序(比如上例的moduleC.js要在moduleA.js的前面),依赖性最大的模块一定要放到最后加载,当依赖关系很复杂的时候,代码的编写和维护都会变得困难。于是require.js出现了,就是为了解决这两个问题:(1)实现js文件的异步加载,避免网页失去响应;(2)管理模块之间的依赖性,便于代码的编写和维护。按照requireJS当中的规范要求,你只需加载一个文件就可以,但同时需要带上一个配置文件config.js第一步:这个require.js你可以从官网下载,这个main.js必须自己完成。不用担心,它写起来非常简单:在这之前我们需要先定义一个config.js用来配置将会用到的js文件:requirejs.config({ baseUrl: "js",//baseUrl所有的路径的"基目录" paths: { //模块名称:文件路径,注意文件没有后缀名,因为在require看来所有的模块都是JS "jquery": "jquery.1.11.3" } });
紧接着,我们可以定义自己的模块文件了,main.js首先main.js也是一个模块,但是我们必须对这个JS改造一下,让它符合require的规范(AMD)要求,才能使用好了,模块main定义完了,我们可以来试试。当然,用之前别忘了修改配置config.jsrequirejs.config({ baseUrl: "js",//baseUrl所有的路径的"基目录" paths: { //模块名称:文件路径,注意文件没有后缀名,因为在require看来所有的模块都是JS "jquery": "jquery.1.11.3", "main":"main.js" //注意:配置路径时,排名不分先后,顺序可以任意。 } });
我们有一个index.html页面,包含一个main.js文件。/* config是config.js相对于main.js的位置,默认为根目录 只有先引用config,才能引用配置好的所有模块*/ require(["config"],function(){ require(["jquery","main"],function($,ma){ //我们引用两个模块jquery、main ma.start(); //ma就是我们刚才在模块ma中返回的对象 }) })当然,因为前边我们定义了main是依赖于jquery的(define(["jquery"],function(){})),因此也可以只加载main:根据我们一开始的设定(A引用了C,而C引用了B,ABC里面都用到了JQ )和上面的讲解,你能否自己完成moduleC.js和moduleA.js?//moduleC.js ----- define(["moduleb","jquery"],function(mb,$){ return { ....... } }); //moduleA.js ----- define(["modulec","jquery"],function(mc,$){ return { ....... } });
别忘了配置://config.js paths: { "jquery": "jquery/jquery.1.11.3", "modulea" : "ms/moduleA", "moduleb" : "ms/moduleB", "modulec" : "ms/moduleC" }一些问题:问:加载配置文件的路径问题?根据你目前的页面的业务JS文件的位置来决定,也就是说,负责加载config的JS在哪里,路径就从哪里开始例如index.js在根目录,config也在根目录:require(["config"],function(){...});例如index.js在js目录中,config在根目录require(["../oncfig"], function(){...});例如index.js在根目录中,config在js目录require(["js/config", function(){...}];问:是不是所有要加载的模块,都必须写define函数,为什么jquery就没写,一样可以加载?谁说JQ没写呢?这是jquery的部分源代码。所以,所有的JS都必须遵循require的规范,才能够正确的加载问:我自己写了一大堆的工具函数,请问怎么加载?首先,工具函数也可以用对象来表示//common.js ----- define(function(){ return { getStyle : function(){ ........ }, randomColor : function(){ ......... } } });当然如果你比较任性,非要写成这个样子://common.js ----- function getStyle(){ ......... } function randomColor(){ ........ }
解决办法也是有的:请百度requireJS, shim配置问:有一些插件,本身没有导出任何对象或核心函数,只是对某个框架的扩展,例如 my.jquery.scroll.js 该怎么用?官方文档给出的答案是使用shim配置//config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": { deps: ["jquery"], exports: "jQuery.fn.scroll" } } });
这是第一种方式, deps表示它依赖了哪个模块,exports表示它输出哪个函数,函数名字要和插件代码里保持一致使用方式://index.js ----- require(["config"],function(){ require(["jquery","jquery.scroll"],function($){ $("#box").scroll(500); }) })第二种配置方式://config.js ----- require.config({ baseUrl: "js", paths: { "jquery" : "jquery.1.11.3", "jquery.scroll" : "my.jquery.scroll" }, shim: { "jquery.scroll": ["jquery"] } });
使用方法同上But! 官方文档又说,这样使用可能导致错误! 请参考原文:所以,终极解决方案是,把插件改成define调用.........问:如果我用到了两个框架,Jquery和Zepto,他们的核心函数都叫$ ,那该怎么办呢?问:jQuery插件使用requireJs
//传入jQuery是因为在require->define([],function(){ // 内是另一个作用域 // }) //无法直接通过jQuery直接访问,当然也可以使用window.jQuery define(["jquery"], function(jQuery) {//适配器模式 //这里不需要返回值,因为本身就是对jQuery的扩展,使用时直接在jquery对象上调用即可 (function($,undefined){ $.fn.extend({ }) })(jQuery); });
说一说好处:讲了这么多,requirejs所倡导的模块化开发,好处在哪里呢?1 你有没有发现,整个项目当中,再也没有出现一个全局变量?2 你有没有发现,你再也没有考虑过加载顺序的问题?3 你有没有发现,即使两个框架名字冲突了也没关系?4 而且你肯定没有发现,所有的JS文件的加载过程,已经变成了异步。5 最后,你还发现,用了requireJS,你的代码想不写成面相对象都难??关于AMD和CMD总在网上看大家讨论AMD和CMD,到底是什么东西呢?AMD就是require所倡导的模块化开发的方式还有一个叫CommonJS的,不过还是和requireJS有些区别,主要针对NODE后端开发另外还有一个大名鼎鼎的SeaJS,作者是淘宝的玉伯,于是诞生了CMD规范根据作者自己的介绍,SeaJS各方面都比requireJS强大这个就不做评价了至于区别呢?AMD推崇的是依赖前置:CMD推崇的是依赖就近:define(function(require, exports, module) { var a = require('./a'); a.doSomething(); ......... var b = require('./b') // 依赖可以就近书写,用到的时候再加载 b.doSomething() })当然requireJS也支持CMD的写法,不过作者本人是不推荐这么写的