Ajax实现、超详细

400 阅读13分钟

注:这是在学习相关课程时做的总结内容,还没有进行实践,若想要更生动的学习,可以观看B站的相关视频

概述

它是浏览器提供的一套方法,可以实现页面无刷新更新数据,提高用户浏览网站的应用体验

在用户浏览网页的同时,可以局部的刷新页面

应用场景:

  1. 页面上拉加载更多数据
  2. 列表数据无刷新分页
  3. 表单下那个离开焦点数据验证
  4. 搜索框提示文字下拉列表

ajax02.png 注:需要运行在网站环境中才能生效

运行及原理实现

创建Ajax对象

  1. 创建

    var xhr = new XMLHttpRequest();
    
  2. 告诉Ajax请求的地址及方式

    告诉Ajax要向哪发送请求,以什么样的方式发送

    xhr.open('get','http://www.example.com');
    

    这个地址实际上就是服务器端对应的路由地址

  3. 发送请求

    xhr.send();
    
  4. 获取服务器端给客户端的响应数据

    xhr.onload = function () {
      console.log(xhr.responseText);
    }
    

​ 当Ajax接收完服务器端的响应之后,触发onload事件

在真实的项目中,服务器端大多数情况下会以JSON对象作为响应数据的格式。当客户端拿到响应数据时,要将JSON数据和HTML字符串进行拼接,然后将拼接后的结果在页面中展示。

在http请求与响应的过程中,无论是请求参数还是响应内容,如果是对象类型,最终都会被转化为对象字符串进行传输

JSON.parse();    //将js字符串转化为JSON对象

传递请求参数

在传统的形式当中,请求都是通过表单的形式来进行传递的。只要表单提交,根据请求方式的不同,表单内容会变成请求参数被自动拼接到对应的位置。get 请求方式会被拼接到请求地址的后面,post请求方式会被放在请求体当中,但无论是哪种请求方式,请求参数的格式都是“请求名称=参数值”的格式,多个参数之间用&符进行连接

而在Ajax中,我们需要之间拼接请求参数,根据请求方式的不同,将参数放到对应的位置上。

get方式:

xhr.open('get','http://example.com?name=zhangsan&age=18');

post方式:

xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
xhr.send('name=zhangsan&age=20');

参数直接放在send方法当中,但需要注意的是,post请求需要在请求报文中明确设置请求参数的内容的类型,也就是Content-Type属性

请求参数格式

  1. Content-Type属性值为application/x-www-form-urlencoded

    name=zhangsan&age=20
    
  2. Content-Type属性值为application/json

    {name: 'zhangsan',age: 20}
    

    需要注意的是我们需要将JSON转化为字符串的格式,放入send方法中

    JSON.stringify()//将json对象转换为json字符串
    

    服务器中的bodyParser也需要重新进行配置,例如

    app.use(bodyParser.json());   //接收json格式的参数
    

注意:get请求不能提交json对象数据格式,传统网站的表单提交也不支持json对象数据格式

Ajex状态码

表示Ajax请求的过程状态 是由Ajax对象返回的

ajax01.png onreadystatechange事件监听状态码的变化,先添加这个事件,再发送请求,写在send方法前面

Ajax错误处理

  1. 情况一:网络畅通的情况下,服务器能接收到请求,但是返回的结果不是预期的结果

    处理方式:判断服务器返回的状态码,分别进行处理

    xhr.status  //获取HTTP状态码
    
  2. 情况二:网络畅通,服务器没有接收到请求,返回404状态码

    处理方式:检查请求地址是否出错

  3. 情况三:网络畅通,服务器能接受到请求,服务器端返回500状态码

    处理方式:找后端程序员进行沟通

  4. 情况四:网络中断,请求无法发送到服务器

    处理方式:会触发xhr对象下的onerror事件,在onerror事件处理函数中对错误进行处理

    xhr.onerror = funcion (){
      alert('网络中断');
    }
    

低版本IE浏览器缓存问题

在低版本的IE浏览器中,Ajax请求会有严重的缓存问题,在请求地址不发生变化的情况下,只有第一次请求回真正发送到服务器端,后续的请求都会从浏览器的缓存中拿到结果。即使服务器端的数据更新了,客户端依然拿到的是旧数据。

解决方法:在请求地址后面加一的请求参数,保证每一次请求中的请求参数的值不同

xhr.open('get','http://example.com?t=' + Math.random);

Ajax异步编程

同步与异步

同步 :一个人同一时间只能做一件事情,只有一件事情做完,才能做另一件事情;落实到代码中就是,一行代码执行完,才能执行下一行代码,即代码逐行执行。

异步:一个人一件事情做了一般,转而去做其它事情,当其他事情做完之后,再回过头来继续做之前未完成的事情;落实到代码上,就是异步代码虽然需要花时间去执行,但是程序不会等待异步代码执行完成后再去继续执行后面的代码,而是直接执行后面的代码,当后面的代码执行完之后,再查看异步代码的返回结果,如果已有返回结果,再调用事先准备好的回调函数处理异步代码执行的结果。

Ajax发送请求的过程就是异步代码执行的过程;

Ajax封装

  1. 参数设置成对象的形式,可以帮助我们更好的知道每个参数代表的意思;
  2. 传递对象数据类型对于函数的调用者更加友好,在函数内部对象数据类型转换为字符串数据类型更加方便;
  3. onload 事件触发的时候,只能说明这次请求完成了,不能代表这次请求一定是成功的,因此需要对http状态码进行判断。
  4. 处理服务器返回值,在服务器进行返回的数据时候,会在响应头中设置数据的返回类型,我们可以对响应头中的数据类型进行判断;获取响应头当中的数据xhr.getReponseHeader
  5. Ajax函数参数太多,会给调用者造成麻烦,因此我们可以给一些参数设置默认值,让调用者只传递必要的参数即可
function ajax (options){
  //设置默认对象
  var defaults = {
    type: 'get',
    url: '',
    data: {},
    header:{
      'Content-Type': 'application/x-www-form-urlencoded'
    },
    success: function(){},
    error: function(){}
  }
  
  //对默认对象进行覆盖
  Object.assign(defaults,options);
  //创建Ajax对象
  var xhr = new XMLHttpRequest();
  var params = '';
  //将传递过来的参数格式,转化为字符串格式
  for(var attr in defaults.data) {
    params += attr + '=' + defaults.data[attr] + '&';
  }
  //截取最后的&符号
  params.substr(0,params.length - 1);
  
  //判断请求方式
  if (defaults.type == get){
    defaults.url += '?' + params;
  }
  
  //配置Ajax对象
  xhr.open(defaults.type,defaults.url);
  
  if (defaults.type == post){
      //发送请求
    var contentType = defaults.header['Content-Type']
    xhr.setRuquestHeader('Content-Type',contentType);
    //判断用户希望请求参数格式的类型
    //如果为Json
		if(contentType == 'application/json'){
      //向服务器返回Json格式的数据
       		xhr.send(JSON.Stringify(defaults.data));
    }else{
      //如果不是,向服务器传递普通类型参数
       		xhr.send(params);
    }

  }else{
    xhr.send();
  }

  //监听xhr对象下的onload事件,当对象接受完响应数据后触发
  
  
  xhr.onload = function(){
    
    //判断返回数据的类型
    var contentType = xhr.getReponseHeader('Content-Type');
    var responseText = xhr.responseText//如果是json,还需要进一步转换
    if(contentType.includes('application/json'){
       //将json字符串转化为json对象
       responseText = JSON.parse(responseText)
    } 
    //当http状态码为200时,表示请求成功,调用成功的代码
    if(xhr.atutas == 200){
      defaults.success(responseText,xhr);
    }else{
      //否则,表示请求失败,调用失败的代码
      defaults.error(responseText,xhr);
    }
  } 
}

ajax ({
  url: 'http://www.example.com',
  data: {
    name: 'zhangsan',
    age: 20
  },
  success: function(data,xhr){
    console.log(data);
  },
  error: function(data,xhr){
    console.log(data);
  }
})

模板引擎

作用:使用模板引擎提供的语法,可以将数据和HTML拼接起来

官方地址

FromData 对象

作用与使用方法

作用:

  1. 模拟HTML表单,相当于将HTML表单映射成表单对象,自动将表单中对象中的数据拼接成请求参数的格式
  2. 异步上传二进制文件(如图片、视频)

创建formData对象

var form  = document.getElementById('form');
var formData = new FormData(form);

提交表单对象

xhr.send(formData);

注意:不能使用get方法请求,因为formData对象内容要放在send方法中。

这是Html5提供的,还有兼容性的问题

实例方法

这些方法都是对表单当中数据的操作,一般来说,表单数据是不会直接被i叫到服务器端,在用户点击完提交按钮后,客户端一般还要对数据进行校验。

  1. 获取表单对象中属性的值

    formData.get('key');  //key代表表单控件中name属性的值
    
  2. 设置表单控件的属性值

    formData.set('key','value'); 
    

    注意:设置的时候,如果表单属性的值是存在的会把原有的值替换掉;若设置的这个属性是表单当中原本不存在的,那么将会创建出这个属性。

  3. 删除表单对象的属性值

    例如,我们一些表单会输入两次密码,那么我们只需要提交一次,需要删掉一个

    formData.delete('key');
    
  4. 向表单对象中追加值

    应用场景:我们在创建formData对象的时候也可以创建一个空的表单对象,然后进行追加

    formData.append('key','value'); 
    

    注意:set方法和append方法都可以给表单对象追加属性,但是区别是,在属性名已存在的情况下,set方法会覆盖已有的值,而append方法会保留两个值,但是,若不在服务器进行特殊设置的时候,它默认提交的是最后一个值。

二进制文件上传

ajax03.png

ajax04.png

Ajax请求限制

Ajax无法向非同源地址发送请求;Ajax只能向自己的服务器发送请求。如果客户端向非同源网站发送了请求,那么浏览器的控制太会报错,但实际上,这个请求是能够发送出去的,只不过是浏览器拒绝接收服务器端的返回结果,座椅最终请求还是失败的。

同源

多个请求或者页面来自同一个服务器端。

如果两个页面有相同的协议、域名、端口,那么这两个页面就属于同一个源,其中只要有一个不相同,就是不同源。

同源政策

同源政策是为了保证用户信息的安全,防止恶意的网站窃取数据。最初的同源政策是指A网站的Cookie,B网站是不能访问的。

解决同源限制

JSONP解决

使用JSONP解决同源限制问题 (实际上已经不属于Ajax请求的范围了,但是可以模拟出Ajax的请求效果,它绕过了浏览器同源政策的限制,向非同源页面发送请求)

JSONP :json with padding 的缩写

首先,将不同源服务器请求地址写在script标签的src属性当中

<script src="www.example.com"> </script>

在客户端,并不是所有能发送请求的方式都受同源政策的限制,src 就是一种。

之后,服务器端的响应数据必须是一个函数的调用,真正要发送给客户端的数据需要作为函数调用的参数

const data = 'fn({name: 'zhangsan', age: 20})';
res.send(data);

在客户端全局作用域下定义被调用的函数,还要写在script的标签前面;

function fn (data){}

最后,在函数内部对服务器返回的数据进行处理

function fn (data) {console.log(data)};

JSONP代码优化

  1. 解决页面一加载就发送请求的问题,让请求根据用户的需求来进行发送,例如点击按钮动态发送请求;

方法:通过JS动态创建Script标签

btn.onclick = function (){
  //创建script标签
  var script = document.createElement('script');
  //设置src属性
  script.src = 'http://www.example.com';
  //追加到页面当中
  document.body.appendChild(script);
  //为script标签添加onload事件
  script.onload = function (){
    //当script执行完成之后,删除掉这个标签,避免出现因多次点击,页面中有多个相同的script标签的现象
    document.body.removeChild(script);
  } 
}

  1. 客户端需要将函数名称传递到服务器,可以做到客户端修改函数名称而不影响服务器端的效果
<script src="www.example.com?callback=fname"> </script>
  1. 封装JSONP函数
function jsonp (options) {
  //动态创建script标签
  var script = document.createElement('script');
  
  var params = '';
  for(var attr in options.data){
    params += '&' + attr + '=' + options.data[attr];
  }
  //随机生成一个函数名
  var fnName = 'myjsonp' + Math.random().toString().replace('.','');
  //将传递过来的函数变成全局函数
  window[fnName] = options.success;
  //添加src属性
  script.src = options.url + '?callback=' + fnName + params;
  //追加到页面当中
  document.body.appendChild(script);
  script.onload = function (){
    document.body.removeChild(script);
  } 
}

jsonp ({
  data:{
    name: 'lisi',
    age: 30
  },
  url:'http://www.example.com',
  success: function(data){
    console.log(data);
  }
})

服务器端优化

app.get('/xxxx',(req,res) =>{
  res.jsonp({name: 'lisi',age: 20});
})

CORS 跨域资源共享

全称:Cross-origin resourse sharing,即跨域资源共享,它允许浏览器向跨域服务器发送Ajax请求,克服了Ajax只能同源使用的限制。

简单来说就是,服务器端允许你跨域访问它,你就可以跨域访问它;服务器端不允许你跨域访问它,你就不能访问。

ajax05.png 服务器端要1.设置允许哪些客户端可以访问 ;2.要设置允许哪些请求方法可以访问

app.use((req,res,next) => {
  res.header('Access-Control-Allow-Origin','*');
  res.header('Access-Control-Allow-Origin','get,post')
  next();
})

同源政策

ajax06.png

cookie 复习

在http协议中规定,客户端与服务器端的交互属于无状态,谁也不认识谁;为例实现登录功能,识别客户端的身份,这个时候就有了cookie,它就是用来识别客户端与服务器端身份的一种技术。应用起来就是,当客户端第一次访问服务器端的时候,服务器会给这个客户端发一个身份证,也就是响应一个cookie,当客户端第二次访问服务器时,这个身份证会随着请求一起发送给服务端,服务器就会知道这个客户端是谁了,这样也就建立了持久的联系。

ajax07.png

jQuery中的方法

&.ajax()方法

ajax08.png 注意:

data还可以传递拼接好的字符串

{
  data: 'name=zhangsan&age=20'
}

在写请求地址的时候,如果遇到了协议、域名、端口都相同的情况;可以省略

serialize方法

作用:将表单中的数据自动拼接成字符串类型的参数

var params = $('#form').serialize()
//name=zhangsan&age=20

function serializeObject(obj) {
  //处理结果对象
  var result = {};
  
  //转化成数组格式,数组里面的内容以对象的形式存在[{name:'username',value: 'zhangsan'},{name:'password',value: '12345']
  var params = obj.serializeArray();
  //循环数据,将数组转化为对象类型
  $.each(params,function(index, value){
    result[value.name] = value.value;
  })
  return result;
}

发送jsonp请求

$.ajax({
  url:...,
  dataType: 'jsonp',
  //可选参数 ,修改callback参数名称
  jsonp: 'cb',
  //可选参数 , 指定函数名称
  jsonCallback: 'fnName',
  success: function(response){}
})

.get().get()、post()方法

分别用来发送get请求和post请求

$.get('http://www.example.com',{name; 'zhangsan',age: 30},function(response){})

$.post('http://www.example.com',{name; 'zhangsan',age: 30},function(response){})

//第二个参数可以是对象类型的也可以是字符串类型的,如果不需要,可以省略,第三个参数实际上也就是success函数

Jquery中的全局事件

需求:只要页面中有Ajax请求被发送,对应的全局事件就会被触发

.ajaxStart()  //当请求开始的时候触发
.ajaxComplete() //当请求完成的时候触发

告诉用户进度,可以使用NProgress插件

RESTful 风格的API

aja09.png 传统请求缺点:

语义不明且混乱,没有统一的规范来约束

RESTful API 概述: 一套关于设计请求的规范

GET: 获取数据

POST: 添加数据

PUT: 更新数据

DELETE: 删除数据

不需要在请求地址中,添加动词了

ajax10.png 特点:请求地址相同,请求方式不同,所做的事情也就不同