More Related Content
Similar to jQuery底层架构 (20)
jQuery底层架构
- 2. ⼤大纲
1. 对象结构和构造成本
2. Promise & Deferred
3. jQuery.cache
4. 事件
5. Sizzle
6. ⼀一些⽅方法和实现
12年3月31⽇日星期六
- 6. 构造流程
1. $(element)
this.context = this[0] = element
this.length = 1
return;
2. $(‘body’)
3. $(‘#id’)
document.getElementById(
4. $(selector)
$(document).find(selector)
$(selector, jq)
jq.find(‘selector’)
$(‘selector’, context)
$(context).find(‘selector’)
5. $(jquery)
6. $(func)
$(document).ready(func)
12年3月31⽇日星期六
- 7. 7. $(html)
1. simple tag $(‘<div>’), $(‘<div />’), $(‘<div></div>’)
1.1 document.createElement(
1.2? $(simpleTag, attrs)
jQuery.fn.attr.call(elm, attrs)
2. else
jQuery.buildFragment
12年3月31⽇日星期六
- 8. 应⽤用
1. 构造jQuery对象的空间成本很低,正常情况下10000个也不会超过1M, 旺铺DIY后台打开
渲染完毕⼤大约构造800个jQuery对象。⼀一般⻚页⾯面初始化时构造数量应该< 500
2. 构造jQuery对象的时间成本通常也很低
如从 dom节点,jquery节点,‘body’, id选择器 构造jQuery都不需要Sizzle参于
3. 如果明确知道是jQuery对象,就不需要再次包装(⼀一般新⼿手会出现,喜欢任何变量都套个$)
4. 使⽤用其他选择器,如果没有context, 相当于 $(document).find(...
所以建议任何使⽤用css3选择器进⾏行构造jQuery对象,请带上相应模块的context节点进⾏行限
制
5. 从html构造jQuery对象时,优先采⽤用 simpleTag
12年3月31⽇日星期六
- 9. Promise接⼝口
promise接⼝口为监异步操作定义了统⼀一的api
它是commonjs规范中的内容⻅见 http://wiki.commonjs.org/wiki/Promises/A
var Promise = {
done: function(callback),
fail: function(callback),
always: function(callback),
....
};
12年3月31⽇日星期六
- 10. $.ajax
// 通常
$.ajax(url, {
dataType: ‘jsonp’
success: function(o) {
console.debug(o);
}
});
12年3月31⽇日星期六
- 11. 通过promise接⼝口使⽤用ajax
var o = $.ajax('mock.php', { dataType: 'jsonp' });
o.done(function(o) { // 相当于success
console.debug('success1', o);
});
o.fail(function(e) { // 相当于 error
console.debug('error', e);
});
o.always(function() { // 相当于complete
console.debug('complete');
});
var o = $.ajax('mock.php', { dataType: 'jsonp' });
o.then(function() {
console.debug('success');
}, function() {
console.debug('error');
});
12年3月31⽇日星期六
- 12. $.when
var url = 'mock.php'
a1 = $.ajax(url, { dataType: 'jsonp' }),
a2 = $.ajax(url, { dataType: 'jsonp' }),
a3 = $.ajax(url, { dataType: 'jsonp' });
$.when(a1, a2, a3).done(function() {
console.debug('all complete');
});
额外阅读: promise.pipe()
12年3月31⽇日星期六
- 13. Promise接⼝口另外⼀一半
var Promise = {
// 下⾯面3个⽅方法相当于bind
done: function() {},
fail: function() {},
always: function() {}
// 下⾯面两个⽅方法相当于trigger
resolve: function() {},
reject: function() {}
};
12年3月31⽇日星期六
- 17. 应⽤用
1. 使⽤用 $.when, promise.pipe等完成⼀一些稍复杂的异步请求需求
2. 组件开发者如有必要可以考虑提供promise接⼝口来统⼀一异步操作的使⽤用⽅方式
3. 使⽤用Deferred对象简化promise接⼝口的实现
4. 注意不能让Deferred侵⼊入业务代码(⻅见例⼦子)
5. 使⽤用Callbacks来简化observer模式的实现(1.7提供)
12年3月31⽇日星期六
- 20. $.data
1. 我们使⽤用$(elm).data(‘key’, value) 设置节点相关数据
2. 有三种元素不能设置data: embed, ⾮非flash的object, applet. 查看jQuery.noData获得列表
3. 设置时key字符串会被转成 camelCase
如 $(elm).data(‘a-b’, ‘hello’) 相当于 $(elm).data(‘aB’, ‘hello’)
我们应该全部使⽤用camelCase⽅方式进⾏行设置
4. 获取时 使⽤用key尝试获取,如果获取不到,会把key转化成 camelCase再次获取。
why? 因为允许 $(elm).data({ ‘a-b’: ‘hello’, ‘c-d’: ‘hello2’ }); 这种⽅方式进⾏行⼀一次设置多个data
但是内部没做转换(估计后续会优化)
我们应该全部使⽤用camelCase⽅方式进⾏行获取
5. 获取data时,如果从cache中获取不到,则会尝试从data属性中获取
<div data-config=’{ “requestUrl”: “url” }’>...
可以使⽤用 $(elm).data(‘config’) 进⾏行读取配置信息
⼀一次读取后,这个值就会被cache,下次从cache中直接获取
注:设置时,是不会更改节点属性的, 如果需要可以使⽤用 $#attr(...
12年3月31⽇日星期六
- 22. data事件
var elm = $('#mydiv1');
elm.bind('getData', function(value, name) { // 取得数据前触发,可以拦截哦
console.debug('getData', name, value);
return 'new data';
});
elm.bind('setData', function(value, name) { // 设置数据前触发, 可惜这个⽅方法没有提供拦截
console.debug('setData', name, value);
});
elm.bind('changeData', function(value, name) { // 设置数据后触发
console.debug('changeData', name, value)
});
注: 这⼏几个⽅方法并未出现在⽂文档中(可能是我没发现),但这⼏几个事件出现在很早的
jQuery版本中 1.4就有了
12年3月31⽇日星期六
- 28. 事件源码导读
1. 在理解上述结构后,看源码将会⾮非常⽅方便
2. 源码中如果有结构和逻辑两部分, 要先弄懂数据结构。
3. 数据结构是静⽌止的算法
事件内部⼯工作流程:
1. 当调⽤用elm.on/bind/delete时, 内部调⽤用 jQuery.event.add(elem, types, handler, data, selector)
完成上述结构的构建
2. 事件触发时,将会调⽤用相应节点 data events.handle ⽅方法
3. events.handle 仅仅调⽤用 jQuery.event.dispatch(event) [备注,⽼老版本这个⽅方法叫
jQuery.event.handle]
4. 当调⽤用trigger⼈人为触发事件时, 内部调⽤用 jQuery.event.trigger(event, data, elem, onlyHandlers)
后者调⽤用 节点data events.handle⽅方法
12年3月31⽇日星期六
- 29. 事件使⽤用技巧
0. 从原理上可以看出,⾃自定义事件和原⽣生事件使⽤用的是同⼀一套逻辑,和浏览器⽆无关
1. jQuery 1.7开始使⽤用on/off 来代替原来的 bind, delegate, one 等事件,以规范化事件的使⽤用
2. 如需要对很多相同性质的节点,或动态产⽣生的节点进⾏行事件绑定,请使⽤用 delegate
3. delegate请尽量减少作⽤用域,如有可能,使⽤用 tag#id.cass来作为selecotr, 这样会直接使⽤用正
则式进⾏行⽐比较
4. 可以使⽤用事件数据来传递额外信息
5. e.preventDefault/e.stopPropagation, return false相当于两者
6. e.stopImmediatePropagation(), 节点事件数组中后⾯面的事件都不再执⾏行
12年3月31⽇日星期六
- 30. 7. trigger/ triggerHandler区别
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
triggerHandler: function( type, data ) {
if ( this[0] ) {
return jQuery.event.trigger( type, data, this[0], true );
}
}
8. 在某些情况下可以使⽤用namespace事件来完成⼀一些要求
如在组件中,对该组件所有的事件给个namespace, 以⽅方便清理
我们绑定事件时,可以给⼀一个namespace, 以便在不保存事件引⽤用的情况下移除事件
9. 注意api的⽅方便性, 如⼀一次可以添加多个事件哦, 还有事件函数可以直接为false
link.on(‘click’, false);
10. one / toggle 类型事件 在某些情况下可以帮助我们不要重新发明轮⼦子
12年3月31⽇日星期六
- 31. Sizzle
jQuery中的CSS3选择器引擎
12年3月31⽇日星期六
- 33. ⾼高效的选择器
1. 即使有querySelectorAll, 也不⼀一定⽀支持任何复杂(⻅见jQuery⽂文档)的选择器
2. 经过我的测试,以下选择器⼀一般都是⽀支持的,所以优先采⽤用(正常应⽤用中⾜足够了)
- ⼦子代选择器,邻代选择器等都是⽀支持的 div ul, div>ul, div+ul, div~ul
- 属性选择器 input[name], input[name=username], div[data-tracelog]
- class选择器 div.mod
- 不⽀支持伪类选择器, 只⽀支持hover, active等⼏几个伪类选择器, 如 :first, :last是不⽀支持
的
3. 以下⼏几种情况直接使⽤用dom api直接获取, ⽽而不需要 querySelectorAll
‘body‘ document.body
#id document.getElementById
tag getElementsByTagName
.class getElementsByClass (如果⽀支持的话)
12年3月31⽇日星期六
- 35. 选择器使⽤用技巧
1. 优先使⽤用简单选择器
2. 选取元素时,请指定合适的context(推荐为模块⼦子模块)
如 $(‘a.close’, panel)
3. class选择器请限定tag, 如tag.class
4. 适度使⽤用伪类
5. 选择器层级不要太深,⼀一般指定context后, selector不会超过2层
12年3月31⽇日星期六
- 36. Sizzle流程
0. jQuery.find = Sizzle
1. 处理选择器 -》 数组
jQuery.find('#doc div.mylist ul>li') -》 ["#doc", "div.mylist", "ul", ">", "li"]
2. 根据类别采⽤用不同的处理顺序
- 选择器中包含位置相关的伪类,如 :first, :last, :even, :odd
⻅见 Sizzle.selectors.match.POS 这个正则表达式
如果存在, 则采⽤用⾃自左向右的顺序进⾏行处理
$(‘div ul li:first’)-> $(‘div’).find(‘ul’).find(‘li:first’)
如果不存在,则采⽤用⾃自右向左的顺序进⾏行处理[1]
$(‘div ul li’) -> $(‘li’), 再过滤出所有符合条件的li, 处理是从右⾃自左的
[1] 如果第⼀一个为id选择器,则先处理它,再使⽤用这个节点为context查找
12年3月31⽇日星期六
- 37. Sizzle(‘ul li’)
- 分割选择器 [‘ul’, ‘li’]
- Sizzle.find(‘li’)
- Sizzle.selector.find.TAG ---> getElementsByTagName(li)
- Sizzle.relative(lis, ‘ul’); -> 看看是否在ul下⾯面
Sizzle(‘ul li:first’)
- 分割选择器[‘ul’, ‘li:first’]
- Sizzle.find(‘ul’)
- 分析选择器 [‘ul’]
- Sizzle.find(‘ul’)
Sizzle.selector.find.TAG ---> getElementsByTagName
- Sizzle(‘li’, ul)
- Sizzle.filter(‘:first’, lis)
12年3月31⽇日星期六
- 38. Sizzle源码导读
1. 正则式 chunker ⽤用于分解选择器
2. 正则式Sizzle.selectors.match.POS ⽤用于判断是否有位置类型的选择器
3. Sizzle.selectors.find ⽤用于查找节点(对应于原⽣生⽅方法, getElementById, getElementsByTagName
等)
4. Sizzle.selectors.relative 过滤出符合要求的⽗父节点
5. Sizzle.selectors.filters 伪类过滤器 (我们可以扩展这个实现⾃自⼰己的伪类语法哦)
6. Sizzle.selectors.filter 各种类型的过滤器逻辑
12年3月31⽇日星期六
- 42. $.proxy
⽅方便我们创建特定context和参数的closure
var A = {
init: function() {
var div = $(‘div.mydiv’);
$.ajax(url, {
success: $.proxy(this, ‘success’, div); // 代替 function(o) { self.success(div, o); }
});
},
success: function(div, o) {
this...
}
};
12年3月31⽇日星期六
- 43. elm.attr() / elm.prop()
1. jQuery 1.6之后有了prop, 即把 attribute和 property分开
2. 区分attribute 和 property
前者相当于 elm.getAttribute 后者相当于 elm[key], 如 elm.checked, sel.selectedIndex
3. checkbox.prop(‘checked’), select.prop(‘selectedIndex’)
4. 尽管jQuery在1.64及以后的版本中做了兼容性,但是不要依赖于这个特性
1. 存在jQuery.attrFn⾥里⾯面的,则直接调⽤用相应⽅方法
2. 返回undefined的,尝试使⽤用prop获取
3. 存在于jQuery.attrHook的字段,特殊处理
4. 最后使⽤用getAttribute/setAttribute
12年3月31⽇日星期六
- 46. remove event
在ui/core.js中
var _cleanData = $.cleanData;
$.cleanData = function(elems){
for (var i = 0, elem; (elem = elems[i]) != null; i++) {
$(elem).triggerHandler('remove');
}
_cleanData(elems);
};
12年3月31⽇日星期六
- 47. 应⽤用
所以在引⼊入ui/core的前提下:
$(elm).bind(‘remove’, function() {
// 清理⼯工作
// 组件或框架设计的同学可以⽤用来做清理⼯工作
});
12年3月31⽇日星期六