微信号:FrontDev

介绍:分享 Web 前端相关的技术文章、工具资源、精选课程、热点资讯

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

2016-04-15 20:38 前端大全

(点击上方公众号,可快速关注)


来源:xingoo 

链接:http://www.cnblogs.com/xing901022/p/5388534.html


由于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开始的。


关于循环依赖的源码可以参考云盘(http://pan.baidu.com/s/1c2yk94s)


如何在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($('test1test1'));

            _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重构完页面,再绑定才行。


【今日微信公号推荐↓】

更多推荐请看值得关注的技术和设计公众号


其中推荐了包括技术设计极客 和 IT相亲相关的热门公众号。技术涵盖:Python、Web前端、Java、安卓、iOS、PHP、C/C++、.NET、Linux、数据库、运维、大数据、算法、IT职场等。点击《值得关注的技术和设计公众号》,发现精彩!

 
前端大全 更多文章 详解Javascript中的Object对象 结合个人经历总结的前端入门方法 前端不为人知的一面–前端冷知识集锦 一份优秀的前端开发工程师简历是怎么样的? 浅谈Web缓存
猜您喜欢 Android Gradle 技巧之二: 最爱命令行 JsBridge实现JavaScript和Java的互相调用 PHP培训 - 3人同行,1人免单 Swift中简单实现 静态TableViewCell Java知识图谱收集整理