一步一步DIY zepto庫,研究zepto原始碼4 -- ajax模組
上面的博文介紹的都是原始碼src下的基礎模組zepto.js
檔案和事件模組event.js
,下面接著看另外一個獨立的模組–ajax模組ajax.js
1.ajax的過程
- 當global: true時。在Ajax請求生命週期內,以下這些事件將被觸發。
- ajaxStart (global):如果沒有其他Ajax請求當前活躍將會被觸發。
- ajaxBeforeSend (data: xhr, options):再發送請求前,可以被取消。
- ajaxSend (data: xhr, options):像 ajaxBeforeSend,但不能取消。
- ajaxSuccess (data: xhr, options, data):當返回成功時。
- ajaxError (data: xhr, options, error):當有錯誤時。
- ajaxComplete (data: xhr, options):請求已經完成後,無論請求是成功或者失敗。
- ajaxStop (global):如果這是最後一個活躍著的Ajax請求,將會被觸發。
下面我們就首先來看這些過程的原始碼:
- 實現邏輯函式:
// trigger a custom event and return false if it was cancelled
function triggerAndReturn(context, eventName, data) {
var event = $.Event(eventName); //包裝成事件
$(context).trigger(event, data); //觸發
return !event.isDefaultPrevented();
}
// trigger an Ajax "global" event
function triggerGlobal(settings, context, eventName, data) {
if (settings.global) return triggerAndReturn(context || document, eventName, data);
}
// Number of active Ajax requests
// 傳送中的ajax請求個數
$.active = 0;
//如果沒有其他Ajax請求當前活躍將會被觸發
ajaxStart
//如果沒有其他Ajax請求當前活躍將會被觸發
function ajaxStart(settings) {
if (settings.global && $.active++ === 0) triggerGlobal(settings, null, 'ajaxStart');
}
ajaxBeforeSend
// triggers an extra global event "ajaxBeforeSend" that's like "ajaxSend" but cancelable
// 觸發選項中beforeSend回撥函式和觸發ajaxBeforeSend事件
// 上述的兩步中的回撥函式中返回false可以停止傳送ajax請求,否則就觸發ajaxSend事件
function ajaxBeforeSend(xhr, settings) {
var context = settings.context;
if (settings.beforeSend.call(context, xhr, settings) === false ||
triggerGlobal(settings, context, 'ajaxBeforeSend', [xhr, settings]) === false)
return false;
triggerGlobal(settings, context, 'ajaxSend', [xhr, settings]);
}
ajaxSuccess
function ajaxSuccess(data, xhr, settings, deferred) {
var context = settings.context,
status = 'success';
settings.success.call(context, data, status, xhr);
if (deferred) deferred.resolveWith(context, [data, status, xhr]);
triggerGlobal(settings, context, 'ajaxSuccess', [xhr, settings, data]);
ajaxComplete(status, xhr, settings);
}
ajaxError
// type: "timeout", "error", "abort", "parsererror"
function ajaxError(error, type, xhr, settings, deferred) {
var context = settings.context;
settings.error.call(context, xhr, type, error);
if (deferred) deferred.rejectWith(context, [xhr, type, error]);
triggerGlobal(settings, context, 'ajaxError', [xhr, settings, error || type]);
ajaxComplete(type, xhr, settings);
}
ajaxComplete
// status: "success", "notmodified", "error", "timeout", "abort", "parsererror"
function ajaxComplete(status, xhr, settings) {
var context = settings.context;
settings.complete.call(context, xhr, status);
triggerGlobal(settings, context, 'ajaxComplete', [xhr, settings]);
ajaxStop(settings);
}
ajaxStop
// 所有ajax請求都完成後才觸發
function ajaxStop(settings) {
if (settings.global && !(--$.active)) triggerGlobal(settings, null, 'ajaxStop');
}
我們注意到,我們只是自定義了ajaxXXX事件,並沒有實際的意義,這時候我們就需要將這些事件的邏輯實現。這部分邏輯放在了$.ajax
中,在適當的時候觸發事件,這些很值得我們去思考的它的巧妙。
2.$.ajax
2.1 全域性變數,工具函式定義:
var jsonpID = +new Date(),
document = window.document,
key,
name,
rscript = /<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, //用來除掉html程式碼中的<script>標籤
scriptTypeRE = /^(?:text|application)\/javascript/i,
xmlTypeRE = /^(?:text|application)\/xml/i, //用來判斷是不是js的mime
jsonType = 'application/json',
htmlType = 'text/html',
blankRE = /^\s*$/,
originAnchor = document.createElement('a');
originAnchor.href = window.location.href;
// Empty function, used as default callback
// 空函式,被用作預設的回撥函式
function empty() {}
function mimeToDataType(mime) {
if (mime) mime = mime.split(';', 2)[0];
return mime && (mime == htmlType ? 'html' :
mime == jsonType ? 'json' :
scriptTypeRE.test(mime) ? 'script' :
xmlTypeRE.test(mime) && 'xml') || 'text';
}
//把引數新增到url上
function appendQuery(url, query) {
if (query == '') return url;
return (url + '&' + query).replace(/[&?]{1,2}/, '?');
//將&、&&、&?、?、?、&?&? 轉化為 ?
}
2.2 $.ajaxSetting
$.ajaxSettings = {
type: 'GET',
beforeSend: empty,
success: empty,
error: empty,
complete: empty,
context: null,
global: true,
xhr: function() {
return new window.XMLHttpRequest();
},
accepts: {
script: 'text/javascript, application/javascript, application/x-javascript',
json: jsonType,
xml: 'application/xml, text/xml',
html: htmlType,
text: 'text/plain'
},
crossDomain: false,
timeout: 0,
processData: true,
cache: true,
dataFilter: empty
};
2.3 序列化
// 列化data引數,並且如果是GET方法的話把引數新增到url引數上
function serializeData(options) {
//options.data是個物件
if (options.processData && options.data && $.type(options.data) != "string")
console.info(options.data);
options.data = $.param(options.data, options.traditional);
// 請求方法為GET,data引數新增到url上
if (options.data && (!options.type || options.type.toUpperCase() == 'GET' || 'jsonp' == options.dataType))
options.url = appendQuery(options.url, options.data), options.data = undefined;
}
var escape = encodeURIComponent;
//序列化
//在Ajax post請求中將用作提交的表單元素的值編譯成 URL編碼的 字串
function serialize(params, obj, traditional, scope) {
var type, array = $.isArray(obj),
hash = $.isPlainObject(obj);
// debugger;
$.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) { //obj是個陣列
params.add(value.name, value.value);
}
// obj的value是個陣列/物件
else if (type == "array" || (!traditional && type == "object")) {
serialize(params, value, traditional, key);
} else {
params.add(key, value);
}
});
}
$.param = function(obj, traditional) {
var params = [];
//serialize函式使用add
params.add = function(key, value) {
if ($.isFunction(value)) value = value();
if (value == null) value = "";
this.push(escape(key) + '=' + escape(value));
};
serialize(params, obj, traditional); //處理obj
return params.join('&').replace(/%20/g, '+');
};
常用方法:
函式/屬性 | 作用 |
---|---|
setRequestHeader() | 給指定的HTTP請求頭賦值.在這之前,你必須確認已經呼叫 open() 方法打開了一個url |
overrideMimeType() | 重寫由伺服器返回的MIME type |
onreadystatechange | readyState屬性改變時會呼叫它 |
open() | 初始化一個請求 |
send() | 傳送請求. 如果該請求是非同步模式(預設),該方法會立刻返回. 相反,如果請求是同步模式,則直到請求的響應完全接受以後,該方法才會返回 |
readyState的狀態:
值 | 狀態 | 描述 |
---|---|---|
0 |
UNSENT (未開啟) |
open() 方法還未被呼叫. |
1 |
OPENED (未傳送) |
send() 方法還未被呼叫. |
2 |
HEADERS_RECEIVED (已獲取響應頭) |
send() 方法已經被呼叫, 響應頭和響應狀態已經返回. |
3 |
LOADING (正在下載響應體) |
響應體下載中; responseText 中已經獲取了部分資料. |
4 |
DONE (請求完成) |
整個請求過程已經完畢. |
2.5 $.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); //[email protected]開始
// 如果沒有傳入crossDomain引數,就通過檢測setting.url和網址的protocol、host是否一致判斷該請求是否跨域
if (!settings.crossDomain) {
// 通過設定a元素的href就可以很方便的獲取一個url的各組成部分
urlAnchor = document.createElement('a');
urlAnchor.href = settings.url;
// cleans up URL for .href (IE only), see https://github.com/madrobby/zepto/pull/1049
urlAnchor.href = urlAnchor.href;
settings.crossDomain = (originAnchor.protocol + '//' + originAnchor.host) !== (urlAnchor.protocol + '//' + urlAnchor.host);
}
// 沒有傳入url引數,使用網站的網址為url引數
// window.location.toString() 等於 window.location.href
if (!settings.url) settings.url = window.location.toString();
//去掉url上的hash部分
if ((hashIndex = settings.url.indexOf('#')) > -1) settings.url = settings.url.slice(0, hashIndex);
serializeData(settings); // 序列化data引數,並且如果是GET方法的話把引數新增到url引數上
var dataType = settings.dataType,
hasPlaceholder = /\?.+=\?/.test(settings.url); // 判斷url引數是否包含=?
if (hasPlaceholder) dataType = 'jsonp'; //jsonp url 舉例http://www.xxx.com/xx.php?callback=?
// 設定了cache引數為false,或者cache引數不為true而且請求資料的型別是script或jsonp,就在url上新增時間戳防止瀏覽器快取
// (cache設定為true也不一定會快取,具體要看快取相關的http響應首部)
if (settings.cache === false || (
(!options || options.cache !== true) &&
('script' == dataType || 'jsonp' == dataType)
))
settings.url = appendQuery(settings.url, '_=' + Date.now());
// jsonp呼叫$.ajaxJSONP實現
if ('jsonp' == dataType) {
if (!hasPlaceholder)
settings.url = appendQuery(settings.url,
settings.jsonp ? (settings.jsonp + '=?') : settings.jsonp === false ? '' : 'callback=?');
return $.ajaxJSONP(settings, deferred);
}
// 下面程式碼用來設定請求的頭部、相應的mime型別等
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(), //XMLHttpRequest
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];
//重寫由伺服器返回的MIME type 注意,這個方法必須在send()之前被呼叫
xhr.overrideMimeType && xhr.overrideMimeType(mime);
}
//設定contentType
if (settings.contentType || (settings.contentType !== false && settings.data && settings.type.toUpperCase() != 'GET'))
setHeader('Content-Type', settings.contentType || 'application/x-www-form-urlencoded');
//如果配置中有對headers內容
if (settings.headers)
for (name in settings.headers) setHeader(name, settings.headers[name]);
xhr.setRequestHeader = setHeader; //設定頭資訊
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) { //請求完成
xhr.onreadystatechange = empty;
clearTimeout(abortTimeout);
var result, error = false;
//請求成功
//在本地調動ajax,也就是請求url以file開頭,也代表請求成功
if ((xhr.status >= 200 && xhr.status < 300) || xhr.status == 304 || (xhr.status == 0 && protocol == 'file:')) {
dataType = dataType || mimeToDataType(settings.mimeType || xhr.getResponseHeader('content-type'));
// 根據xhr.responseType和dataType處理返回的資料
if (xhr.responseType == 'arraybuffer' || xhr.responseType == 'blob') {
result = xhr.response;
} else {
result = xhr.responseText;
try {
// http://perfectionkills.com/global-eval-what-are-the-options/
// (1,eval)(result) 這樣寫還可以讓result裡面的程式碼在全域性作用域裡面執行
result = ajaxDataFilter(result, dataType, settings);
if (dataType == 'script') {
(1, eval)(result);
} else if (dataType == 'xml') {
result = xhr.responseXML;
} else if (dataType == 'json') {
result = blankRE.test(result) ? null : $.parseJSON(result);
}
} catch (e) { error = e; }
if (error) return ajaxError(error, 'parsererror', xhr, settings, deferred);//[email protected]
}
ajaxSuccess(result, xhr, settings, deferred);//[email protected]
} else {
ajaxError(xhr.statusText || null, xhr.status ? 'error' : 'abort', xhr, settings, deferred);//[email protected]
}
}
};
//必須send()之前//[email protected]
if (ajaxBeforeSend(xhr, settings) === false) {
xhr.abort();
ajaxError(null, 'abort', xhr, settings, deferred);
return xhr;
}
var async = 'async' in settings ? settings.async : true;
/**
* void open(
DOMString method,
DOMString url,
optional boolean async,
optional DOMString user,
optional DOMString password
);
*/
xhr.open(settings.type, settings.url, async, settings.username, settings.password);
if (settings.xhrFields)
for (name in settings.xhrFields) xhr[name] = settings.xhrFields[name];
for (name in headers) nativeSetHeader.apply(xhr, headers[name]);
// 超時丟棄請求
if (settings.timeout > 0) abortTimeout = setTimeout(function() {
xhr.onreadystatechange = empty;
xhr.abort();
ajaxError(null, 'timeout', xhr, settings, deferred);
}, settings.timeout);
// avoid sending empty string (#319)
xhr.send(settings.data ? settings.data : null);
return xhr;
};
2.6 示例Demo
ajax讀取本地的json,安裝個小伺服器,或者使用其他伺服器,如apache,tomcat等。
npm install anywhere -g //安裝
anywhere 8860 //開啟埠(任意沒被佔用的)為伺服器使用
$.ajax({
type: 'GET',
url: '/projects.json',
data: {
name: 'Hello'
},
dataType: 'json',
timeout: 300,
success: function(data) {
console.log(data);
},
error: function(xhr, type) {
alert('Ajax error!');
}
})
3.jsonp
使用jsonp跨域的核心思想是:
script = document.createElement('script')
script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName);
document.head.appendChild(script)
具體實現的程式碼:
$.ajaxJSONP = function(options, deferred) {
// 沒有type選項,呼叫$.ajax實現
if (!('type' in options)) return $.ajax(options);
var _callbackName = options.jsonpCallback,
//options配置寫了jsonpCallback,那麼回撥函式的名字就是options.jsonpCallback
//沒有就是'Zepto' + (jsonpID++)
callbackName = ($.isFunction(_callbackName) ?
_callbackName() : _callbackName) || ('Zepto' + (jsonpID++)),
script = document.createElement('script'),
originalCallback = window[callbackName],
responseData,
abort = function(errorType) {
$(script).triggerHandler('error', errorType || 'abort');
},
xhr = { abort: abort },
abortTimeout;
if (deferred) deferred.promise(xhr);
// 載入成功或者失敗觸發相應的回撥函式
// load error 在event.js
$(script).on('load error', function(e, errorType) {
clearTimeout(abortTimeout);
// 載入成功或者失敗都會移除掉新增到頁面的script標籤和繫結的事件
$(script).off().remove();
if (e.type == 'error' || !responseData) { //失敗
ajaxError(null, errorType || 'error', xhr, options, deferred);
} else { //成功
ajaxSuccess(responseData[0], xhr, options, deferred);
}
window[callbackName] = originalCallback;
if (responseData && $.isFunction(originalCallback))
originalCallback(responseData[0]);
originalCallback = responseData = undefined;
});
// 在beforeSend回撥函式或者ajaxBeforeSend事件中返回了false,取消ajax請求
if (ajaxBeforeSend(xhr, options) === false) {
abort('abort');
return xhr;
}
window[callbackName] = function() {
responseData = arguments;
};
// 引數中新增上變數名
script.src = options.url.replace(/\?(.+)=\?/, '?$1=' + callbackName);
document.head.appendChild(script);
//超時處理
if (options.timeout > 0) abortTimeout = setTimeout(function() {
abort('timeout');
}, options.timeout);
return xhr;
};
4.get、post、getJSON、load 方法
篇幅所限,這裡就不再單列出來了,他們都是處理好引數,呼叫$.ajax
方法。詳細的實現方式去我的github 下載。
相關推薦
一步一步DIY zepto庫,研究zepto原始碼3 -- event模組
上面的博文介紹的都是原始碼src下的zepto.js檔案,接著我們來看看zepto的事件模組,對應檔案是event.js 1.繫結事件 例項Demo <div id="foo1">foo1</div> &
一步一步DIY zepto庫,研究zepto原始碼4 -- ajax模組
上面的博文介紹的都是原始碼src下的基礎模組zepto.js檔案和事件模組event.js,下面接著看另外一個獨立的模組–ajax模組ajax.js 1.ajax的過程 當global: true時。在Ajax請求生命週期內,以下這些事件將被觸發。
一例mysql主從數據庫,從庫宕機後無法啟動的解決方案
mysql starting 啟動時報錯信息: Starting MySQL... ERROR! The server quit without updating PID file (/usr/local/mysql/data/qkzhi-appzookeeper-1.novalocal.pid
Light libraries是一組通用的C基礎庫,目標是為減少重復造輪子而寫(全部用POSIX C實現)
six clas 原子操作 roi 實現 class 動態庫 readme tps Light libraries是一組通用的C基礎庫,目標是為減少重復造輪子而寫實現了日誌、原子操作、哈希字典、紅黑樹、動態庫加載、線程、鎖操作、配置文件、os適配層、事件驅動、工作隊列、RP
如何在 webpack 中引入未模組化的庫,如 Zepto
前言 最近我在研究多頁面 webpack 模組打包的完整解決方案時,發現用 import 匯入 Zepto 時,會報 Uncaught TypeError: Cannot read property 'createElement' of undefined 錯誤,
用Html5/CSS3做Winform,一步一步教你搭建CefSharp開發環境(附JavaScript異步調用C#例子,及全部源代碼)上
轉載 界面設計 右鍵 異步 一個 由於 編寫 scrip 調用 本文為雞毛巾原創,原文地址:http://www.cnblogs.com/jimaojin/p/7077131.html,轉載請註明 CefSharp說白了就是Chromium瀏覽器的嵌入式核心,我們用此開發W
1.一步一步學開發(遊戲賬服數據庫的使用 Erlang 服務器)
http ats 日誌收集 yield data obj 開發 用戶 nbsp mysql 與mongodb的特點與優劣 http://www.cnblogs.com/eternal1025/p/5419905.html 首先我們來分析下mysql 與mongodb的特
一步一印,有印為證
有物為證 之前一段時間告誡自己和家人:"路很長也很短,很短也很長,每天進步一點點,自律",最近沈下心態,思索最近半年的工作經歷和當下生活狀態,借用馬未都先生節目中的一句話“觀復嘟嘟,有物為證”稍作修改來描述近來心態變化,那就是“一步一腳印,有印為證”,思考良久其實二者的關系並不矛盾,之前強調的是放慢角度
利用 Siblings一步實現多個同級div,只改變當前點擊的div樣式
ima 可選參數 cto 一個 五個 點擊 rem wid bin 記錄一點,小技巧。直接上代碼嘍,因為今天還沒有功夫扯皮呢。 <!DOCTYPE html> <html> <head&g
微信小程序的認證流程,極限工坊一步一步教會你
nag 進行 text 根據 服務號 登陸 dad 訂閱號 手機號碼 微信小程序作為一種輕巧靈活的手機應用,改變著手機互聯網形態的同時,也在改變著我們的生活方式。 下面淘小咖具體教大家如何輕松快捷註冊小程序,看圖教程一步一步來! 1、小程序賬號註冊 百度搜索或者直接在公眾平
U盤安裝蘋果系統教程,菜鳥一步一步也能成大牛
女朋友蘋果電腦遇到問題,女朋友不會整,又怕自己整壞了,就跑去電腦城修,結果被坑了150塊,本來說要雙系統Mac和Windows,結果店家拿著電腦就跑出去了(並不知道幹啥去了),回來發現只有win7,沒有Mac,並且索要正版的價格,我尼瑪,就坑我小女朋友,還好小女朋友沒給他,本來就是盜版系
Spring的前世今生: Spring5.0已經出來了,Springboot已經風靡全球, Spring怎麼一步一步走過來的, 讓我們看看其前世今生~~~
Spring5.0已經出來了,Springboot已經風靡全球, Spring怎麼一步一步走過來的, 讓我們看看其前世今生~~~ 這是關於什麼是Spring Framework,但它是如何開發的?嗯,背後有一個有趣的歷史。讓我們一瞥Spring Framework的
中國正攜區塊鏈下一步新金融的大棋,深圳極可能成為落子的第一步
當眾人還在為加密貨幣的漲跌而喋喋不休時,中國已悄然攜區塊鏈下一盤關乎中國新金融新經濟的大棋,而深圳極有可能成為落子的第一步。 金融業務一直被視為區塊鏈技術的第一大應用,8月中旬以來,儘管監管層接連收緊了對加密貨幣的監管,但對區塊鏈在金融創新上的推動卻始終低調而急促,國
Jquery 分頁外掛, 帶你一步一步接入後臺資料
目錄 一、效果圖 二、分頁 js 原始碼講解 三、分頁樣式 css 原始碼 三、實現前後臺對接 一、效果圖 二、分頁 js 原始碼講解 新建一個 js 檔案,基本直接複製貼上就行,記得引入到需要的頁面中。 需要注意的是: 前面的建構函式
抖音、吃雞、王者榮耀:你的自律,是如何被頂級產品經理一步一步毀掉的
作者:書單君 來源:書單(ID:BookSelection) 01 你的沉迷 跟這個時代有關 這是個特別容易沉迷的時代。 抖音、煲劇、王者榮耀、吃雞遊戲……你的時間和注意力悄悄被它們偷走,卻從不說再
(原創)超詳細一步一步在eclipse中配置Struts2環境,無基礎也能看懂
(原創)超詳細一步一步在eclipse中配置Struts2環境,無基礎也能看懂 1. 在官網https://struts.apache.org下載Struts2,建議下載2.3系列版本。從圖中可以看出,既可以分開下載子檔案,又可以一次全部下載。 這裡我後
分享我這8年,是如何一步一步走向架構師的
摘要:心血經驗分享,架構師更多的是一個不斷學習,不斷積累的過程,希望可以幫到同行業的朋友們 前言 成為優秀的架構師是大部分初中級工程師的階段性目標。優秀的架構師往往具備七種核心能力:程式設計能力、除錯能力、編譯部署能力、效能優化能力、業務架構能力、線上運維能力、專案管理能力和規劃能力。 這幾種能力之
自定義實現SpringMvc框架,自定義@Controller、@RequestMapping註解,自己也是一步一步的對程式碼的理解出來的,只是比較簡單的例子
1.自定義的DispatcherServlet,就是SpringMvc執行載入類 /*** * 手寫SpringMvc框架<br> * 思路:<br> * 1.手動建立一個DispatcherServlet 攔截專案的所有請求 SpringMv
stm8 觸控庫使用教程 一步一步
配套的檔案資料會上傳 1、檢視觸控庫說明文件,根據需要選取晶片(主要支援幾個鍵):STMTouch Driver User Manual 一個channel為一個按鈕,比如STM8L101F 就支援3個按鈕,通過按鈕擴充方式可以增加按鈕,參照: 增加觸控感測按鈕數量