Ajax詳解(手寫jq和axios部分實現)

含義: async javascript and xml 異步的js和xmljavascript

1、Ajax原生JS操做

//1、建立Ajax實例
let xhr = new XMLHttpRequest();//IE下爲ActiveObject對象
//2、打開請求: 發送請求以前的一些配置項
//1.HTTP METHOD:GET/POST/PUT/DELETE/HEAD/OPTIONS/TRACE/CONNECT/
//2.url:接口地址
//3.async:設置Ajax的同步異步,默認是異步
//4.user-name/user-pass用戶名和密碼,通常不用
xhr.open(method, url, async, [user-name], [user-pass])
//3、事件監聽:通常監聽的都是readystatechange事件(Ajax狀態改變事件),基於這個事件能夠獲取服務器返回的響應頭響應主體
xhr.onreadystatechange = () => {
	if(xhr.readyState === 4 && xhr.status === 200){
		console.log(xhr.responseText);
	}
};
//4、發送Ajax請求:從這步開始,當前Ajax任務開始,若是Ajax是同步的,後續代碼不會執行,要等到Ajax狀態成功後再執行
xhr.send([請求主體內容])
複製代碼

2、關於HTTP請求方式:

GET: 從服務器獲取數據

POST: 向服務器推送數據

DELETE: 刪除服務器端的某些內容

PUT: 向 服務器存放一些內容

HEAD: 只想獲取服務器返回的響應頭信息,不要響應主體的內容

OPTIONS: 通常使用它向服務器發送一個探測性請求,若是返回了信息,說明當前客戶端和服務器端創建了鏈接,能夠繼續執行其餘請求

TRACE: axios這個Ajax類庫基於cross-domain進行跨域請求的時候,就是先發送OPTIONS進行探測嘗試。若是能連通服務器,纔會繼續發送其它的請求。

GET 和 POST的區別:

【傳遞給服務器信息的方式不同】

GET經過url字符串傳參,POST經過請求主體java

[GET]
xhr.open('GET', '/tmp/list?xxx=xxx&xxx=xxx')

[POST]
xhr.send('xxx=xxx')
(通常是url-encode格式)
複製代碼
【GET不安全 POST相對安全】

由於 GET是基於」問號傳參「把信息傳遞給服務器的,容易被hack進行url劫持,post是基於請求主體傳遞的。ios

【GET會產生不可控制的緩存,POST不會】

不可控:是瀏覽器的自主記憶,沒法經過JS控制。 解決方案ajax

xhr.open('GET', `/temp/list?lx=1000&_=${Math.random()}`);
複製代碼
其餘區別:
  • GET在瀏覽器回退時是無害的,而POST會再次提交請求
  • GET請求會被瀏覽器主動緩存,而POST不會,除非手動設置
  • GET請求參數會被完整保留在瀏覽器歷史記錄中,而POST不會
  • GET請求只能進行url編碼,而POST支持多種編碼方式

3、Ajax狀態 ready--state

0 => UNSENT 剛開始建立xhr, 尚未發送編程

1 => OPENED 已經執行了open這個操做json

2 => HEADERS_RESERVED 已經發送Ajax,響應頭已經被客戶端接受axios

3 => LOADING 響應主體內容正在返回設計模式

4 => DONE 響應主體已經被客戶端接收跨域

4、HTTP網絡狀態碼 status

根據狀態碼可以清楚的反映出當前交互的結果和緣由瀏覽器

1XX :指示信息-表示請求已接受、繼續處理

2XX :成功 - 表示請求已被成功接收

3XX :成功,可是已經重定向

4XX : 客戶端錯誤

5XX : 服務端錯誤

具體舉例:

200 OK: 客戶端請求成功

206 Partial Content: 客戶發送了一個帶有Range頭的GET請求,服務器完成了它

301 Moved Permamently: 已經永久轉至新的url

302 Found: 臨時轉至新的url,當一臺服務器達到最大併發數,會轉移服務器處理

304 Not Modified: 服務器告訴客戶,原來的緩存能夠繼續使用,如CSS/JS/HTML/IMG,Ctrl+F5 304緩存失效

400 Bad Request: 客戶端有語法錯誤,服務器不能理解

401 Unauthorized: 請求未經受權

403 Forbidden: 對被請求頁面的訪問被禁止

404 Not Found: 請求資源不存在

413 Request Entity Too Large 和服務器交互的內容資源超過最大大小

500 Interval Server Error 服務器錯誤,原來的緩存還能使用

503 Service Unavailable

5、關於XHR的屬性和方法

xhr.response 響應主體內容

xhr.responseText 響應的內容是字符串(JSON或XML文檔)

xhr.responseXML 響應的內容是xml

xhr.status 返回的HTTP狀態碼

xhr.statusText 狀態碼的描述

xhr.timeout 設置請求超時的時間

xhr.timeout = 1000
xhr.ontimeout = () => {
	console.log(‘請求超時’)
}
複製代碼

xhr.withCredentials 是否容許跨域(false)

xhr.abort() 強制中斷Ajax請求

xhr.abort();
xhr.onabort = () => {}
複製代碼

xhr.getAllResponseHeaders() 獲取全部響應頭信息

xhr.getResponseHeader([key])例如:xhr.getResponseHeader('date')就是獲取響應頭中的服務器時間

xhr.open() 打開url請求

xhr.overrideMimeType() 重寫MIME類型

xhr.send()發送Ajax請求,參數爲請求主體對象

xhr.setRequestHeader() 設置自定義請求頭信息(不能出現中文),必須在open以後設置

//小例子
xhr.onreadystatechange = () => {
	if(!/^(2|3)\d{2}$/.test(xhr.status))return;//證實服務器已經返回內容了
	if(xhr.readyState === 2){
		let time = xhr.getResponseHeader('date');
	}
	if(xhr.readyState === 4 && xhr.status === 200){
		JSON.parse(xhr.responseText);
	}
}
複製代碼

6、異步和同步的區別

異步:

let xhr = new XMLHttpRequest();
xhr.open('GET', 'xxx', true);
xhr.onreadystatechange = () => {
	if(xhr.readyState === 2) {
		console.log(1);
	}
	if(xhr.readyState === 4) {
		console.log(2)
	} 
}
xhr.send(); 
console.log(3)
//3 1 2 
複製代碼

同步:

let xhr = new XMLHttpRequest();
xhr.open('GET', 'xxx', false);
xhr.onreadystatechange = () => {
	if(xhr.readyState === 2) {
		console.log(1);
	}
	if(xhr.readyState === 4) {
		console.log(2)
	} 
}
xhr.send(); //任務開始,只要當前Ajax請求這件事沒完成(readyState沒到4),什麼都不能作
console.log(3)
//2 3 爲何呢?
//因爲是同步編程,主任務隊列在狀態沒有變成4以前一直被Ajax請求佔用,其餘事件作不了。
//因此,只有readyState變成4才能執行方法。

let xhr = new XMLHttpRequest();
xhr.open('GET', 'xxx', false);
xhr.send(); //任務開始,只要當前Ajax請求這件事沒完成(readyState沒到4),什麼都不能作
//如今狀態已經爲4
xhr.onreadystatechange = () => {
	if(xhr.readyState === 2) {
		console.log(1);
	}
	if(xhr.readyState === 4) {
		console.log(2)
	} 
}

console.log(3)
//3
//所以採用異步Ajax
複製代碼

7、jQuery中Ajax

/** * DATA: * 若是是GET請求是基於問號傳參過去的 * 若是是POST請求是基於請求主體傳遞過去的 * data的值能夠是對象也能夠是字符串(通常經常使用對象): * 若是是對象,jq會把對象轉換爲 xxx=xxx 的模式(x-www-form-urlencoded) * DATA-TYPE:預設置獲取結果的數據格式 TEXT/JSON/JSONP/HTML/XML/SCRIPT(服務器返回給客戶端的響應主體中的內容通常是字符串, * 而設置DATA-TYPE='json',jq會內部把獲取的字符串轉化爲JSON格式的對象 => 它不影響服務器返回的結果,只是二次處理結果) * ASYNC:設置是否異步 * CACHE:設置是否緩存,當設置FALSE,而且get請求,JQ會在請求的url地址末尾加隨機數 * SUCCESS:回調函數,當Ajax請求成功執行,JQ執行回調函數時把響應主體中獲取的結果(二次處理)當作參數 * ERROR: 請求失敗後執行的回調函數 */
$.ajax({
	url: 'xxx',
	method: 'GET',
	data: null,
	dataType: 'json',
	async: true,
	cache: true,
	success: (result, textStatus, xhr) => {},
	error: () => {}
})
複製代碼

8、無敵手寫

原生JS封裝 ajax(jQ版本)

~ function (window) {
  function AJAX(options) {
    return new AJAX.prototype.init(options);
  }
  function init(options = {}){
    let {
      url,
      method = 'GET',
      data = null,
      dataType = 'JSON',
      async = true,
      cache = true,
      success,
      error
    } = options;
    //=>MOUNT 把配置項掛載到實例上
    ['url', 'method', 'data', 'dataType', 'async', 'cache', 'success', 
    'error'].forEach(item => {
      this[item] = eval(item);
    });
  }
  
  AJAX.prototype = {
    constructor: AJAX,
    init,
    sendAjax(){
      this.handleCache();
      this.handleData();
      //send
      let {method, url, async, error, success} = this;
      //SEND發送請求
      let xhr = new XMLHttpRequest();
      xhr.open(method, url, async);
      xhr.onreadystatechange = () => {
        if(xhr.readyState === 4){
          if(!/^(2|3)\d{2}$/.test(xhr.status)){
            error && error(xhr.statusText, xhr)
          }
          //處理DATA-TYPE
          let result = this.handleDataType(xhr);
          success && success(result, xhr);
        }
      };
      xhr.send();
    },
    handleDataType(xhr) { 
      let dataType = this.dataType.toUpperCase(),
          result = xhr.responseText;
      switch (dataType) {
        case 'TEXT':
          break;
        case 'JSON':
          result = JSON.parse(result);
          break;
        case 'XML':
          result = xhr.responseXML;
          break;
      }  
      return result;    
    },
    handleCache() {
      let {url, method, cache} = this;
      if(/^GET$/i.test(method) && cache==false){
        url += `${this.check()}=${+(new Date())}`;
      }
    },
    handleData() {
      let {data, method} = this;
      if(!data) return;
      if(typeof data === 'object'){
        //若是是一個對象,咱們把它轉換爲x-www-form-urlencoeded模式
        for(let key in data){
          if(data.hasOwnProperty(key)){
            str += `${key}=${data[key]}`;
          }
        }
        data=str.substring(0,str.length);
      }
      if(/^(GET|DELETE|HEAD|TRACE|OPTIONS)$/i.test(method)){
        this.url += `${this.check()}${data}`;
        this.data = null;
        return;
      }
      this.data = data; //POST處理方式
    },
    check() {
      return this.url.indexOf('?')>-1?'&':'?';
    }
  }
  init.prototype = AJAX.prototype;

  window.ajax = AJAX;
}(window)
複製代碼

基於Promise用原生JS手擼Ajax(axios版本)

~ function (window) {
  //設置默認的參數配置項 
  let _default = {
    method: 'GET',
    url: '',
    baseURL: '',
    headers: {},
    dataType: 'JSON',
    data: null, //POST系列
    params: null, //GET系列
    cache: true
  };
  //基於Promise設計模式管理Ajax
  let ajaxPromise = function axios() {
    let {
      url,
      baseURL,
      data,
      dataType,
      headers,
      cache,
      params
    } = options;
    //=>把傳遞的參數進一步進行處理
    if(/^(GET|DELETE|HEAD|OPTIONS)$/.test(method)){
      //GET參數
      if(params) {
        url += `${ajaxPromise.check(url)}${ajaxPromise.formatData(params)}`
      }
      if(cache === false){
        url += `${ajaxPromise.check(url)}_=${+(new Date())}`
      }
      data= null;//GET系列請求主體爲空
    }else{
      //POST系列
      if(data){
        data = ajaxPromise.formatData(data);
      }
    }
    //=>基於Promise發送Ajax
    return new Promise((resolve, reject) => {
      let xhr = new XMLHttpRequest();
      xhr.open(method, `${baseURL}${url}`);
      if(headers != null && typeof headers === 'object'){
        for(let attr in headers){
          if(headers.hasOwnProperty(attr)){
            let val = headers[attr];
            if(/[\u4e00-\u9fa5]/.test(val)){
              val = encodeURIComponent(val);
            }
            xhr.setRequestHeader(attr, headers[attr]);
          }
        }
      }
      //=>若是headers存在,咱們須要設置請求頭
      xhr.onreadystatechange = () => {
        if (xhr.readyState === 4){
          if(/^(2|3)\d{2}$/.test(xhr.status)){
            let result = xhr.responseText;
            dataType = dataType.toUpperCase();
            dataType === 'JSON'?result = JSON.parse(result):(dataType === 'XML'?result = xhr.responseXML : null);
            resolve(result, xhr);
            return;
          }
          reject(xhr.statusText, xhr);
        }
      }
      xhr.send(data);
    })
  }

  ajaxPromise.defaults = _default;

  ajaxPromise.formatData = function formatData(){
    let str = ``;
    for(let attr in obj) {
      if(obj.hasOwnProperty(attr)){
        str += `${attr}=${obj[attr]}&`;
      }
      return str.substring(0, str.length-1)
    }
  }

  ajaxPromise.check = function check(url){
    return url.indexOf('?')>-1?'&':'?';
  }

  //GET系列 
  ['get', 'delete', 'head', 'options'].forEach(item => {
    ajaxPromise[item] = (url, options = {}) => {
      options = {
        ..._default,
        ...options, 
        url, 
        method: item.toUpperCase()
      };
      return ajaxPromise(options);
    }
  })
  //POST系列
  ['post', 'put', 'patch'].forEach(item => {
    ajaxPromise[item] = (url, data = {}, options = {}) => {
      options = {
        ..._default,
        ...options, 
        url, 
        method: item.toUpperCase(),
        data
      };
      return ajaxPromise(options);
    }
  })

  window.ajaxPromise = ajaxPromise;
}(window)
複製代碼

關於Ajax一點小小的總結,但願對你們有幫助!

相關文章
相關標籤/搜索