zepto源码ajax模块学习
在学习zepto的源码的时候,不得不称赞这些人的厉害,我虽然能看明白,但是要我写,估计吭哧吭哧写不出来。虽然现在很少人使用zepto了,但是学习这些源码我相信每次看都能给咱们不同的感受。Deferred对象待会讲,deferred我认为是zepto设计最巧妙的地方。先来看zepto的ajax模块。
;(function($){ var jsonpID = +new Date(); function triggerAndReturn(context, eventName, data) { var event = $.Event(eventName); $(context).trigger(event, data); return !event.isDefaultPrevented(); } })
这里为什么文件开头都要使用';'这是因为在对多个js文件进行打包的时候,如果使用换行分隔代码,当合并压缩多个文件之后,换行符会被删掉,连在一起可能出错,加上分号就保险了。
jsonpID在跨域jsonp的时候会使用到,这是为了禁止使用cache。triggerAndReturn()的目的在于创建一个Event事件,然后在context触发他,如果默认行为被取消了,则返回false。
$.ajaxSettings = { type: 'GET', success: empty, xhr: function () { return new window.XMLHttpRequest() }, cache: true, crossDomain: false }
这是ajax的初始化,默认是GET请求,xhr是新建的XMLHttpRequest()对象,cache表示浏览器是否应该被允许缓存GET响应。crossDomain表示是否可以来自另外一个域。
下面来看看最核心的$.ajax方法。
$.ajax = function (options) { var settings = $.extend({}, options || {}), deferred = $.Deferred && $.Deferred(), urlAnchor, hashIndex for (key in $.ajaxSettings) if (settings[key] === undefined) settings[key] = $.ajaxSettings[key] ajaxStart(settings) ... }
首先传入options,然后将传入的options存储到本地,deferred咱们暂时可以把它看成一个promise对象,遍历$.ajaxSettings,如果用户没有设置里边有的属性,那就使用默认的属性。然后调用ajaxStart开始的函数。
if (!settings.crossDomain) { urlAnchor = document.createElement('a'); urlAnchor.href = settings.url; urlAnchor.href = urlAnchor.href; settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host) }
这里首先crossDomain为false的情况下,进入该逻辑,通过判断我们传入的url和当前window.location.href做对比,判断是不是跨域。
if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex) var dataType = settings.dataType, hasPlaceholder = /\?.+=\?/.test(settings.url); if (hasPlaceholder) dataType = 'jsonp'; if (settings.cache === false || ( (!options || options.cache !== true) && ('script' == dataType || 'jsonp' == dataType) )) settings.url = appendQuery(settings.url, '_=' + Date.now()) function appendQuery(url, query) { if (query == '') return url; return (url + '&' + query).replace(/[&?]{1,2}/, '?') } if ('jsonp' == dataType) { if (!hasPlaceholder) settings.url = appendQuery(settings.url, settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?') return $.ajaxJSONP(settings, deferred) }
如果我们没有传入url,那么url就是window.location.href。'#'代表网页中的一个位置,右边的字符,就是该位置的标识符,#是用来指导浏览器动作的,对浏览器没有用,所以在截取url的时候,没必要把后面的部分传给服务器。hasPlaceholder这里的正则表达式,用于匹配类似'?name=?'这种字符串,如果hasPlaceholder为true,则dataType为jsonp。下面的时不使用留在缓存的数据,第一种是设置cache为false,或者dataType为script和jsonp的情况,需要在url后面添加事件。下面是appendQuery方法,就是把字符串拼接到url后边,但是需要把'&'替换成'?'。如果我们的请求是jsonp请求,需要在url后面添加一些callback=?这种参数。
var mime = settings.accepts[dataType], headers = {}, setHeader = function(name, value) {headers[name.toLowerCase()] = [name, value]}, protocol = /^([\w-]+:)\/\//.test(settings.url) ? RegExp.$1 : window.location.protocol, xhr = settings.xhr(), nativeSetHeader = xhr.setRequestHeader, abortTimeout if (deferred) deferred.promise(xhr); if (!settings.crossDomain) setHeader('X-Requested-With', 'XMLHttpRequest'); setHeader('Accept', mime || '*/*') if (mime = settings.mimeType || mime) { if (mime.indexOf(',') > -1) mime = mime.split(',', 2)[0] xhr.overrideMimeType && xhr.overrideMimeType(mime) }
settings中的accepts表示从服务器请求的MIME类型,指定dataType的值,包括script、json、xml、html、text。mime存储的就是类似'application/json'的字符串。protocol就是匹配咱们类似'http://'的这种协议。
如果deferred存在,就把xhr转换为deferred对象。接着看下去,如果不是跨域的,那就是ajax请求。然后后面的判断条件查了一下是针对一些mozillar浏览器进行修正(有个问题就是他们上哪儿知道的用这种方式来修正啊)。
xhr.onreadystatechange = function() { if (xhr.readyState == 4) { xhr.onreadystatechange = empty if (/*如果成功*/){ // 对返回结果进行处理 } } } xhr.open(settings.type, settings.url, async, settings.username, settings.password)
没什么难的,就是根据传入的参数不同进行处理。
$.param
这个函数的作用在于序列化传入对象,下面是他的代码:
$.param = function (obj, traditional) { var params = [] params.add = function (key, value) { if ($.isFunction(value)) value = value() if (value == null) value = "" this.push(escape(key) + '=' + escape(value)) } serialize(params, obj, traditional) return params.join('&').replace(/%20/g, '+') }
传入一个对象,和一个标记,这个traditional表示激活传统的方式通过$.param来得到data。首先定义一个空数组,如果在上面添加方法,这个方法的主要作用向params里边添加序列化的对象,escape=encodeURIComponent,然后调用serialize,将obj对象添加到params中,最后返回将params数组用'&'拼接,然后这里的%20表示空格,意思是将空格替换成'+'
serialize
下面是代码
function serialize(params, obj, traditional, scope) { var type, array = $.isArray(obj), hash = $.isPlainObject(obj) $.each(obj, function (key, value) { type = $.type(value) if (scope) key = traditional ? scope : scope + '[' + (hash || type == 'object' || type == 'array' ? key : '') + ']' // handle data in serializeArray() format if (!scope && array) params.add(value.name, value.value) // recurse into nested objects else if (type == "array" || (!traditional && type == "object")) serialize(params, value, traditional, key) else params.add(key, value) }) }
params是带有add方法的数组,遍历obj的键值对,属性值可能是对象也可能是数组或者字符串分别进行处理。如果obj的某个属性值是对象或者数组,scope就代表是该属性,那么这时候向params传入的key就需要变化,变成类似'a[b]'这里的b是obj的某个属性值是对象的一个属性。
相关推荐
结束数据方法的参数,该如何定义?-- 集合为自定义实体类中的结合属性,有几个实体类,改变下标就行了。<input id="add" type="button" value="新增visitor&quo