基于RequireJS和JQuery的模块化编程

由于js的代码逻辑越来越重,一个js文件可能会有上千行,十分不利于开发与维护。最近正在把逻辑很重的js拆分成模块,在一顿纠结是使用requirejs还是seajs的时候,最终还是偏向于requirejs。毕竟官方文档比较专业嘛...
不过即便是有完整的官方文档,仍然遇到不少的问题,比如jquery-ui的使用。
下面就循序渐进的讲解一下我遇到的问题,以及解决的办法。

关于AMD和CMD的理解

AMD(异步模块定义)的典型就是requirejs,而CMD(通用模块定义)的典型是淘宝的seajs。

他们的相同点是,都会异步的加载js。但是不同点是,require.js加载完会立即执行;而seajs则是等到进入主函数需要执行时才执行。

如果使用seajs初始的加载执行效率会比较高,但是在使用的过程中可能会取执行js,因此可能会出现卡顿,影响用户体验(由于我也没试过,要是说错了,别见怪)。而requirejs则是在一开始就把所有加载的js都执行,这时,如果你的模块中有一些执行方法,它们可能并不会按照你想的顺序执行。

因此,如果已经习惯了异步编程,并且希望有完善的文档推荐使用requirejs;如果是想对执行顺序有特殊要求,又方便开发,那么也可以使用seajs。

如何解决requirejs中循环依赖问题

如果你定义的某个a模块使用到了b模块,而b模块又使用了a模块,那么就会抛出循环依赖的异常。

比如,我这里写了一个循环依赖的例子。
主页面:

<!DOCTYPE html>
<html>
<head>
</head>
<body>
<script data-main="test.js" src="lib/require.js"></script>
</body>
</html>

主方法:

requirejs.config({
baseUrl: './'
}); requirejs(['js/a'],function (a){
console.log("in test");
a.testfromb();
});

a.js模块中,atest()方法提供b调用、testfromb()方法调用b的方法

define(function(require){
var b = require("js/b");
console.log("in a");
return {
atest:function(){
console.log("test in a");
},
testfromb:function(){
console.log("testfromb in a");
b.btest();
}
}
});

b模块中,调用了a的方法。

define(function(require){
var a = require("js/a");
console.log("in b");
return {
btest:function(){
console.log("test in b");
a.atest();
}
}
});

这样相当于a调用了b的方法,但是b的方法依赖于a的方法,这就造成了循环依赖。浏览器会提示错误:

Uncaught Error: Module name "js/a" has not been loaded yet for context: _

按照官方文档的说法,这种属于设计的问题,应该尽量避免。那么如果避免不了该怎么办呢?可以这样修改b模块:

define(function(require){
// var a = require("js/a");
console.log("in b");
return {
btest:function(){
console.log("test in b");
require("js/a").atest();
}
}
});

这里是等到执行atest()方法时,才加载a模块。这时,a模块很显然已经加载完了 。可以看到输出的信息:

in b
a.js:3 in a
test.js:6 in test
a.js:9 testfromb in a
b.js:6 test in b
a.js:6 test in a

同样的方式,修改a可能就不好使了。这时因为模块加载的顺序是从b开始的。

关于循环依赖的源码可以参考云盘

如何在requirejs中使用jquery

如果想要使用jquery比较简单,直接在main.js中添加对应的依赖即可:

requirejs.config({
baseUrl: './',
paths:{
'jquery':'lib/jquery'
}
}); requirejs(['jquery'],
function ($){
$('#test').html('test');
});

如何在requirejs中使用jquery插件

对于jquery的插件,比较常见的做法都是传入一个jquery的对象,在这个jquery对象的基础上添加插件对应的方法。

首先需要添加jquery插件的依赖,这里用两个插件举例子——jquery-ui和jquery-datatables

requirejs.config({
baseUrl: './',
paths:{
'jquery':'lib/jquery',
'jquery-ui':'lib/jquery-ui',
'jquery-dataTables':'lib/jquery.dataTables'
},
shim:{
'jquery-ui':['jquery'],
'jquery-dataTables':['jquery']
}
}); requirejs(['jquery','jquery-ui','jquery-dataTables'],
function ($){
....
});

由于jquery插件都需要依赖于jquery,因此可以在shim中指定依赖关系。
除了上面这种使用方法,也可以使用commonJS风格的调用:

define(function(require){
var $ = require('jquery');
require('jquery-ui');
require('jquery-dataTables'); //下面都是测试,可以忽略
var _test = $('#test');
_test.selectmenu({
width : 180,
change : function(event, ui) {
console.log('change');
}
});
return {
test:function(){
//测试jquery-ui
_test.append($('<option>test1</option><option>test1</option>'));
_test.selectmenu("refresh");
//测试jquery-datatables
var _table = $('table');
_table.dataTable();
}
}
});

不过,执行上面的代码,会报一个异常:

Uncaught TypeError: _table.dataTable is not a function

这是因为,dataTables并不是一个require风格的模块,因此直接这样引入,并不会执行它内部的匿名函数。可以修改它的匿名函数,传入$对象,在最后一行:

     */

    return $.fn.dataTable;
//}));原来是这样
}($)));//这里增加执行这个匿名函数,并且传入$对象。 }(window, document));

这也是在网上搜的方法,原理奈何经验不足....

样例代码可以参考云盘,由于引入的资源不是很全,所以会报错,可以直接忽略,因为能执行UI插件就表示已经成功了。

requirejs使用jquery-ui的问题

由于requirejs加载js文件后会立即执行,如果你的jquery ui 插件需要刷新DOM页面,那么可能会导致页面的事件失效。

比如,你的模块在加载后,对页面的某个元素$('#test')绑定了click事件。但是使用了某个UI插件,这个插件会重新渲染DOM元素,test对应的click事件就失效了。

解决办法:

  • 把事件绑定推迟到DOM元素渲染完后再手动触发绑定;
  • 也可以使用事件捕获代替DOM元素的事件绑定(太麻烦了...不推荐)。

比如在DOM重构的JS模块中,执行渲染的代码下面:

require("xxx").initEvents();

常见场景:

比如我在页面中使用了jquery-steps这个UI插件,它会对页面进行重新渲染。这就导致我最开始绑定的事件都失效了....只有推迟到这个js重构完页面,再绑定才行。

 
分类: JavaScript

RequireJS和JQuery的模块化编程的更多相关文章

  1. 基于RequireJS和JQuery的模块化编程——常见问题解析

    由于js的代码逻辑越来越重,一个js文件可能会有上千行,十分不利于开发与维护.最近正在把逻辑很重的js拆分成模块,在一顿纠结是使用requirejs还是seajs的时候,最终还是偏向于requirej ...

  2. jquery学习笔记---requirejs 和模块化编程

    http://www.cnblogs.com/lisongy/p/4711056.html jquery模块化编程:http://www.cnblogs.com/digdeep/p/4602460.h ...

  3. require.js实现js模块化编程(二):RequireJS Optimizer

    require.js实现js模块化编程(二):RequireJS Optimizer 这一节,我们主要学习一下require.js所提供的一个优化工具r.js的用法. 1.认识RequireJS Op ...

  4. RequireJS模块化编程详解

    1.模块的写法 模块化编程一般都有这么几个过渡过程,如下描述. 原始方法 function m1(){ //... } function m2(){ //... } 上面的函数m1()和m2(),组成 ...

  5. 【javascript激增的思考01】模块化编程

    前言 之前我做过一个web app(原来可以这么叫啦),在一个页面上有很多小窗口,每个小窗口都是独立的应用,比如: ① 我们一个小窗口数据来源是腾讯微博,需要形成腾讯微博app小窗口 ② 我们一个小窗 ...

  6. require.js实现js模块化编程(一)

    1.认识require.js: 官方文档:http://requirejs.org/RequireJS是一个非常小巧的JavaScript模块载入框架,是AMD规范最好的实现者之一.最新版本的Requ ...

  7. javascript模块化编程库require.js的用法

    随着javascript的兴起,越来越多的公司开始将JS模块化,以增加开发的效率和减少重复编写代码的.更是为了能更加容易的维护日后的代码,因为现在的随着人们对交互效果的越来越强烈的需求,我们的JS代码 ...

  8. js模块化编程之彻底弄懂CommonJS和AMD/CMD!

    先回答我:为什么模块很重要? 答:因为有了模块,我们就可以更方便地使用别人的代码,想要什么功能,就加载什么模块.但是,这样做有一个前提,那就是大家必须以同样的方式编写模块,否则你有你的写法,我有我的写 ...

  9. 深入JavaScript模块化编程

    今天看requirejs官网的manual,发现了下面这篇好文章,于是花点时间翻译了一下,翻译不好的地方请指正,谢谢!   推荐阅读原文:) http://www.adequatelygood.com ...

随机推荐

  1. Qt之开机自启动

    Windows开机自启动的程序很多,包括系统软件.杀毒软件.一些其他安装软件等等.当然可以禁止,通过软件管理或者手动删除对应的注册表中的项即可!但是为了系统的服务或者操作上的方便,我们往往需要开机自启 ...

  2. cocos2d-x游戏开发(十五)游戏加载动画loading界面

    个人原创,欢迎转载:http://blog.csdn.net/dawn_moon/article/details/11478885 这个资源加载的loading界面demo是在玩客网做逆转三国的时候随 ...

  3. Android手机怎样录制屏幕及转GIF

    有时候我们须要录制Android 手机的屏幕,比方写了一个Demo应用,须要公布到博客和微博上. 例如以下是我录制转GIF的效果图 对于Android4.4的上的手机,系统自带了一个命令screenr ...

  4. 内省(二)之BeanUtils工具类

    上一篇内省(Introspector)讲到的是采用JavaAPI中的类来操作bean及其属性,而Apache也开源了第三方框架来简化和丰富了对bean属性的操作,这个框架就是BeanUtils. 使用 ...

  5. mysql 分区和集群

    集群和分区:http://han-zw.iteye.com/blog/1662941http://www.php-note.com/article/detail/794 分区:http://lober ...

  6. 【ASP.NET Web API教程】2.1 创建支持CRUD操作的Web API

    原文 [ASP.NET Web API教程]2.1 创建支持CRUD操作的Web API 2.1 Creating a Web API that Supports CRUD Operations2.1 ...

  7. python 网络爬虫(二) BFS不断抓URL并放到文件中

    上一篇的python 网络爬虫(一) 简单demo 还不能叫爬虫,只能说基础吧,因为它没有自动化抓链接的功能. 本篇追加如下功能: [1]广度优先搜索不断抓URL,直到队列为空 [2]把所有的URL写 ...

  8. iphone缩小uIImage图片

    UIImage的缩小 有时候,项目中,要用到上传图片,从图片库里取出的图片有的太大了,而要上传的时候,会很费时间,而且也没必要太大,所以就把图片综缩小一下,再传! #pragma UIImagePic ...

  9. cp命令的实现

    #include <unistd.h> #include <fcntl.h> #include <stdlib.h> #define BUFFERSIZE 4096 ...

  10. 3D空间中射线与三角形的交叉检測算法

    引言 射线Ray,在3D图形学中有非常多重要的应用.比方,pick操作就是使用射线Ray来实现的,还有诸如子弹射线的碰撞检測等等都能够使用射线Ray来完毕. 所以,在本次博客中,将会简单的像大家介绍下 ...