分享

Bootstrap 源码解析

 nikybook 2015-09-03

1、Bootstrap的作用域

2、Bootstrap的类定义

3、Bootstrap的插件定义

4、Bootstrap的事件代理

5、Bootstrap的对象数据缓存

6、Bootstrap的防冲突

7、作用域外如何使用Button类

8、Bootstrap的单元测试

 

 

Bootstrap的作用域

Bootstrap每个插件都定义在下面这段作用域代码中: 

Js代码  收藏代码
  1. +function ($) {  
  2.     ...  
  3. }(window.jQuery)  
 请看《IIFE》和《严格模式》编译环境。

 

在插件的作用域之外,全局范围执行代码的第一行,检测了jQuery是否定义。在Grunt的concat任务中,合并所有插件时,检测代码添加在目标文件的banner说明后面。Grunt.js的相关代码: 

Js代码  收藏代码
  1. jqueryCheck: 'if (typeof jQuery === "undefined") { throw new Error("Bootstrap requires jQuery") }\n\n',  
  2.   
  3. concat: {  
  4.       options: {  
  5.         banner: '<%= banner %><%= jqueryCheck %>',  
  6.         stripBanners: false  
  7.       },  
  8.       bootstrap: {  
  9.         src: [  
  10.           'js/transition.js',  
  11.           'js/alert.js',  
  12.           'js/button.js',  
  13.           'js/carousel.js',  
  14.           'js/collapse.js',  
  15.           'js/dropdown.js',  
  16.           'js/modal.js',  
  17.           'js/tooltip.js',  
  18.           'js/popover.js',  
  19.           'js/scrollspy.js',  
  20.           'js/tab.js',  
  21.           'js/affix.js'  
  22.         ],  
  23.         dest: 'dist/js/<%= pkg.name %>.js'  
  24.       }  
  25.     }  

 

Bootstrap的类定义

Js代码  收藏代码
  1. var Button = function (element, options) {  
  2.   this.$element = $(element)  
  3.   this.options  = $.extend({}, Button.DEFAULTS, options)  
  4. }  
  5.   
  6. Button.DEFAULTS = {  
  7.   loadingText: 'loading...'  
  8. }  
  9.   
  10. Button.prototype.setState = function (state) {  
  11.   ...  
  12. }  
  13.   
  14. Button.prototype.toggle = function () {  
  15.   ...    
  16. }  

Bootstrap采用这种类定义方式的好处,以及Javascript其他几种类定义的方式,请参照《Javascript面向对象编程(一):封装》 

Javascript规定,每一个构造函数都有一个prototype属性,指向另一个对象。这个对象的所有属性和方法,都会被构造函数的实例继承。这意味着,我们可以把那些不变的属性和方法,直接定义在prototype对象上。在Button函数体内部定义的属性和方法可以看做是类的私有属性和方法, 为Button.prototype对象定义的属性和方法都可以看做是类的公共属性和方法。这个类封装了插件对象初始化所需的方法和属性。

 

Bootstrap的插件定义

请参看《jQuery插件开发快速入门》,注意两个this指向的是不同对象 

Js代码  收藏代码
  1. $.fn.button = function (option) {  
  2.   return this.each(function () {  
  3.     var $this   = $(this)  
  4.     ...  
  5.   })  
  6. }  

  

Bootstrap的事件代理

Bootstrap Button插件定义最后一部分,事件绑定是这么写的 

Java代码  收藏代码
  1. $(document).on('click.bs.button.data-api', '[data-toggle^=button]', function (e) {  
  2.     ...      
  3. })  

这段JavaScript代码将click委托事件监听器绑定在document元素上,并给click事件赋予命名空间click.bs.button.data-api,选择器匹配的是属性data-toggle的值为"button"开头的标签。

关于jQuery将事件绑定在document文档对象上的好处,就是js事件代理的优点,性能上做了一个测试比较

关于jQuery命名空间的好处,请参看《jQuery .on() and .off() 命名空间

 

Bootstrap的防止冲突

jQuery是全局对象,所以jQuery的插件定义$.fn.button并不受作用域限制。如果在别的插件中同样定义了button插件,后加载的button插件将会覆盖先加载的button插件,jsbin示例: 

Js代码  收藏代码
  1. // Old button  
  2. +function($){  
  3.   $.fn.button = function() {  
  4.     alert('Old button')  
  5.   }  
  6. }(window.jQuery)    
  7.   
  8. // Bootstrap button  
  9. +function($){  
  10.   $.fn.button = function() {  
  11.     alert('Bootstrap button')  
  12.   }  
  13. }(window.jQuery)  
  14.   
  15. $('a').button() // alert('Bootstrap button')  
Bootstrap做了插件冲突处理,jsbin示例: 
Js代码  收藏代码
  1. // Old button  
  2. +function($) {  
  3.   $.fn.button = function() {  
  4.     alert('Old button')  
  5.   }   
  6. }(window.jQuery)  
  7.   
  8. // Bootstrap button  
  9. +function($){  
  10.   // 将原先的button插件对象赋值给一个临时变量old  
  11.   var old =  $.fn.button  
  12.   
  13.   $.fn.button = function() {  
  14.       alert('Bootstrap button')  
  15.   }  
  16.   
  17.   // 执行该函数,恢复原先的button定义,并返回Bootstrap定义的button插件  
  18.   $.fn.button.noConflict = function () {  
  19.     $.fn.button = old  
  20.     return this  
  21.   }  
  22.   
  23. }(window.jQuery)  
  24.   
  25. // <span style="font-family: Helvetica, Tahoma, Arial, sans-serif; white-space: normal; background-color: #ffffff;">作用域</span>外我们可以灵活使用两个button插件  
  26. $.fn.button = $.fn.button.noConflict()  
  27. $('a').button() // alert('Bootstrap button')  
  28.   
  29. $.fn.button.noConflict()  
  30. $('a').button() // alert('Old button')  

 

Bootstrap作用域外如何使用Button类 

Js代码  收藏代码
  1. $.fn.button.Constructor = Button  
在Bootstrap的button插件中还有上面者句代码,去掉它不影响插件的正确执行。 

它很像javascript中类构造器: 

Java代码  收藏代码
  1. var Cat = function(name) {  
  2.    this.name = name  
  3. }  
  4. var cat1 = new Cat('Hello Kitty')  
  5. var cat2 = new Cat('Doramon')  
  6.   
  7. cat1.constructor == Cat.prototype.constructor  
 

但是Javascript是区分大小写的,也就是 这里大写开头的的Constructor 和 Javascript小写开头的constructor 没有任何关系。

查找jQuery源码中也没有对于大写开头的Constructor的定义。所以这里的Constructor只是一个普通属性,我们也可以写成其他名字 $.fn.button.Something = Button,Bootstrap为了指明这个属性的意义而命名为构造器“Constructor”更合理。

这样一来,这段代码就很好理解了:$.fn.button.Constructor = Button 通过将作用域内的Button类赋值给jQuery的button对象的Constructor属性,在IIFE作用域外也可以使用Button类。调用方式:

Js代码  收藏代码
  1. +function($){  
  2.   // 类定义  
  3.   var Button = function() {}  
  4.   // 插件定义  
  5.   $.fn.button = function() {  
  6.       alert('Bootstrap button')  
  7.   }  
  8.   // 类赋值到jQuery button对象的Constructor属性  
  9.   $.fn.button.Constructor = Button  
  10.   
  11. }(window.jQuery)   
  12.   
  13. var Button = $.fn.button.Constructor  

 

Bootstrap的对象数据缓存

 

Js代码  收藏代码
  1. // 获取存储的Button对象,如果是第一次执行变量data的值为undefined  
  2. var data    = $this.data('bs.button')  
  3. var options = typeof option == 'object' && option  
  4.   
  5. // 创建Button对象: new Button(this, options),  
  6. // 并赋值给变量data: data = new Button(this, options)  
  7. // 存储在元素的jQuery对象上的‘bs.button’数据字段 $this.data('bs.button', data)  
  8. if (!data) $this.data('bs.button', (data = new Button(this, options)))  
  9.   
  10. // data是一个Button对象,可以调用Button的原生方法  
  11. if (option == 'toggle') data.toggle()  
  12. else if (option) data.setState(option)  

利用jQuery的 .data(key, value)存储Button对象

 

Bootstrap的单元测试

QUnit + PhantomJS 

 

 

优秀的程序猿有一个共同的秘诀:阅读优秀的代码

 

 

 

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多