Bootstrap 源码解析(转载苏青)

本文详细解析了Bootstrap插件的工作原理,包括作用域定义、类和插件定义、事件代理、冲突解决、对象数据缓存及单元测试等内容。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、Bootstrap的作用域

2、Bootstrap的类定义

3、Bootstrap的插件定义

4、Bootstrap的事件代理

5、Bootstrap的对象数据缓存

6、Bootstrap的防冲突

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

8、Bootstrap的单元测试

 

 

Bootstrap的作用域

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

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

concat: {
      options: {
        banner: '<%= banner %><%= jqueryCheck %>',
        stripBanners: false
      },
      bootstrap: {
        src: [
          'js/transition.js',
          'js/alert.js',
          'js/button.js',
          'js/carousel.js',
          'js/collapse.js',
          'js/dropdown.js',
          'js/modal.js',
          'js/tooltip.js',
          'js/popover.js',
          'js/scrollspy.js',
          'js/tab.js',
          'js/affix.js'
        ],
        dest: 'dist/js/<%= pkg.name %>.js'
      }
    }

 

Bootstrap的类定义

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

  Button.DEFAULTS = {
    loadingText: 'loading...'
  }

  Button.prototype.setState = function (state) {
    ...
  }

  Button.prototype.toggle = function () {
    ...  
  }

Bootstrap采用这种类定义方式的好处,以及Javascript其他几种类定义的方式,请参照《Javascript面向对象编程(一):封装》 或者阅读《Javascript高级程序设计(第3版)》7.4章节。

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

 

Bootstrap的插件定义

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

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

 

Bootstrap的事件代理

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

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

这段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. // Bootstrap button
  8. +function($){
  9. $.fn.button = function() {
  10. alert('Bootstrap button')
  11. }
  12. }(window.jQuery)
  13. $('a').button() // alert('Bootstrap button')
// Old button
+function($){
  $.fn.button = function() {
    alert('Old button')
  }
}(window.jQuery)  

// Bootstrap button
+function($){
  $.fn.button = function() {
    alert('Bootstrap button')
  }
}(window.jQuery)

$('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. // Bootstrap button
  8. +function($){
  9. // 将原先的button插件对象赋值给一个临时变量old
  10. var old = $.fn.button
  11. $.fn.button = function() {
  12. alert('Bootstrap button')
  13. }
  14. // 执行该函数,恢复原先的button定义,并返回Bootstrap定义的button插件
  15. $.fn.button.noConflict = function () {
  16. $.fn.button = old
  17. returnthis
  18. }
  19. }(window.jQuery)
  20. // <span style="font-family: Helvetica, Tahoma, Arial, sans-serif; white-space: normal; background-color: rgb(255, 255, 255);">作用域</span>外我们可以灵活使用两个button插件
  21. $.fn.button = $.fn.button.noConflict()
  22. $('a').button() // alert('Bootstrap button')
  23. $.fn.button.noConflict()
  24. $('a').button() // alert('Old button')
// Old button
+function($) {
  $.fn.button = function() {
    alert('Old button')
  } 
}(window.jQuery)

// Bootstrap button
+function($){
  // 将原先的button插件对象赋值给一个临时变量old
  var old =  $.fn.button

  $.fn.button = function() {
      alert('Bootstrap button')
  }

  // 执行该函数,恢复原先的button定义,并返回Bootstrap定义的button插件
  $.fn.button.noConflict = function () {
    $.fn.button = old
    return this
  }

}(window.jQuery)

// 作用域外我们可以灵活使用两个button插件
$.fn.button = $.fn.button.noConflict()
$('a').button() // alert('Bootstrap button')

$.fn.button.noConflict()
$('a').button() // alert('Old button')

 

Bootstrap作用域外如何使用Button类

Js代码 复制代码 收藏代码
  1. $.fn.button.Constructor = Button
$.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. cat1.constructor == Cat.prototype.constructor
var Cat = function(name) {
   this.name = name
}
var cat1 = new Cat('Hello Kitty')
var cat2 = new Cat('Doramon')

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. }(window.jQuery)
  11. var Button = $.fn.button.Constructor
+function($){
  // 类定义
  var Button = function() {}
  // 插件定义
  $.fn.button = function() {
      alert('Bootstrap button')
  }
  // 类赋值到jQuery button对象的Constructor属性
  $.fn.button.Constructor = Button

}(window.jQuery) 

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. // 创建Button对象: new Button(this, options),
  5. // 并赋值给变量data: data = new Button(this, options)
  6. // 存储在元素的jQuery对象上的‘bs.button’数据字段 $this.data('bs.button', data)
  7. if (!data) $this.data('bs.button', (data = new Button(this, options)))
  8. // data是一个Button对象,可以调用Button的原生方法
  9. if (option == 'toggle') data.toggle()
  10. elseif (option) data.setState(option)
// 获取存储的Button对象,如果是第一次执行变量data的值为undefined
var data    = $this.data('bs.button')
var options = typeof option == 'object' && option

// 创建Button对象: new Button(this, options),
// 并赋值给变量data: data = new Button(this, options)
// 存储在元素的jQuery对象上的‘bs.button’数据字段 $this.data('bs.button', data)
if (!data) $this.data('bs.button', (data = new Button(this, options)))

// data是一个Button对象,可以调用Button的原生方法
if (option == 'toggle') data.toggle()
else if (option) data.setState(option)

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

 

Bootstrap的单元测试

QUnit + PhantomJS

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值