Ajax详解(手写jq和axios部分实现)

栏目: JavaScript · 发布时间: 5年前

内容简介:GET通过url字符串传参,POST通过请求主体因为 GET是基于”问号传参“把信息传递给服务器的,容易被hack进行url劫持,post是基于请求主体传递的。不可控:是浏览器的自主记忆,无法通过JS控制。 解决方案
//一、创建Ajax实例
let xhr = new XMLHttpRequest();//IE下为ActiveObject对象
//二、打开请求: 发送请求之前的一些配置项
//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])
//三、事件监听:一般监听的都是readystatechange事件(Ajax状态改变事件),基于这个事件可以获取服务器返回的响应头响应主体
xhr.onreadystatechange = () => {
	if(xhr.readyState === 4 && xhr.status === 200){
		console.log(xhr.responseText);
	}
};
//四、发送Ajax请求:从这步开始,当前Ajax任务开始,如果Ajax是同步的,后续代码不会执行,要等到Ajax状态成功后再执行
xhr.send([请求主体内容])
复制代码

二、关于HTTP请求方式:

GET: 从服务器获取数据

POST: 向服务器推送数据

DELETE: 删除服务器端的某些内容

PUT: 向 服务器存放一些内容

HEAD: 只想获取服务器返回的响应头信息,不要响应主体的内容

OPTIONS: 一般使用它向服务器发送一个探测性请求,如果返回了信息,说明当前客户端和服务器端建立了连接,可以继续执行其他请求

TRACE: axios这个Ajax类库基于cross-domain进行跨域请求的时候,就是先发送OPTIONS进行探测尝试。如果能连通服务器,才会继续发送其它的请求。

GET 和 POST的区别:

【传递给服务器信息的方式不一样】

GET通过url字符串传参,POST通过请求主体

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

[POST]
xhr.send('xxx=xxx')
(一般是url-encode格式)
复制代码

【GET不安全 POST相对安全】

因为 GET是基于”问号传参“把信息传递给服务器的,容易被hack进行url劫持,post是基于请求主体传递的。

【GET会产生不可控制的缓存,POST不会】

不可控:是浏览器的自主记忆,无法通过JS控制。 解决方案

xhr.open('GET', `/temp/list?lx=1000&_=${Math.random()}`);
复制代码

其他区别:

  • GET在浏览器回退时是无害的,而POST会再次提交请求
  • GET请求会被浏览器主动缓存,而POST不会,除非手动设置
  • GET请求参数会被完整保留在浏览器历史记录中,而POST不会
  • GET请求只能进行url编码,而POST支持多种编码方式

三、Ajax状态 ready--state

0 => UNSENT 刚开始创建xhr, 还没有发送

1 => OPENED 已经执行了open这个操作

2 => HEADERS_RESERVED 已经发送Ajax,响应头已经被客户端接受

3 => LOADING 响应主体内容正在返回

4 => DONE 响应主体已经被客户端接收

四、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

五、关于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);
	}
}
复制代码

六、异步和同步的区别

异步:

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
复制代码

七、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: () => {}
})
复制代码

八、无敌手写

原生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一点小小的总结,希望对大家有帮助!


以上所述就是小编给大家介绍的《Ajax详解(手写jq和axios部分实现)》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Math Adventures with Python

Math Adventures with Python

Peter Farrell / No Starch Press / 2018-11-13 / GBP 24.99

Learn math by getting creative with code! Use the Python programming language to transform learning high school-level math topics like algebra, geometry, trigonometry, and calculus! In Math Adventu......一起来看看 《Math Adventures with Python》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具