Ajax

一.概述

Web 程序最初的目的就是将信息(数据)放到公共的服务器,让所有网络用户都可以通过浏览器访问。

在次之前,我们可以通过以下几种方式让浏览器发出对服务端的请求,获取服务端的数据:

  • 地址栏输入地址,回车,刷新
  • 特定元素的href或src属性
  • 标点提交

这些方式如果通过代码的方式进行编程,可以通过js直接发送网络请求,那么web的可能就会更多,至少不是“单机游戏”

AJAX(Asynchronous JavaScript and XML),最早出现在 2005 年的 Google Suggest,是在浏览器端进行网络编 程(发送请求、接收响应)的技术方案,它使我们可以通过 JavaScript 直接获取服务端最新的内容而不必重新加载 页面。让 Web 更能接近桌面应用的用户体验。

二.初步使用

使用Ajax可以类比我们访问页面的过程,Ajax其实就是一套API,核心类型:XMLHttpRequest。
为什么是XML,传输的不是JSON吗?历史原因,在JSON还没有流行起来之前,之前不便修改
//1.创建一个XMLHttpRequest类型对象,--安装浏览器(用户代理)
var xhr = new XMLHttpRequest()
//2.打开与一个网址之间的连接, --相当于在地址栏输入访问地址
xhr.open('GET','./time.php')
//3.通过连接发送一次请求 ---相当于回车或者点击访问发送请求
xhr.send(null)
//4.指定xhr状态变化事件处理函数, --相当于处理网页呈现后的操作
//因为客户端永远不知道服务端何时才能返回我们需要的响应
//所以Ajax API采用事件的机制(通知的感觉)
xhr.onreadystatechange = function(){
//通过xhr的readyState判断此次请求的响应是否接收完成
if(this.readyState ==4){
//通过xhr的reponseTexth获取到响应的响应体
console.log(this)
}
} //onreadystatechange,并不是只在响应时触发,时状态改变就触发

2.1readyState

​ 由于 readystatechange 事件是在 xhr 对象状态变化时触发(不单是在得到响应时),也就意味着这个事件会被 触发多次,所以我们有必要了解每一个状态值代表的含义:

readyState 状态描述 说明
0 UNSENT 代理(XHR)被创建,但尚未调用open()方法
1 OPENED open()方法已经被调用,建立了连接
2 HEADERS_RECEIVED send()方法已经被调用,并且已经可以获取状态行和响应头
3 LOADING 响应体下载中,responseText属性可能已经包含部分数据
4 DONE 响应体下载完成,可以直接使用responseText

2.2 时间轴

graph LR
初始化-->建立连接
建立连接-->接收响应头
接收响应头-->响应体加载
响应体加载 -->加载完成
var xhr =new XMLHttpRequest()
console.log(xhr.readyState) // 0 (初始化,请求代理对象) xhr.open('GET','time.php')
console.log(xhr.readyState) //1 (open()方法已经调用,建立一个与服务端特定接口的连接) xhr.send()
xhr.addEventListener('readystatechange',function(){
switch(this.readyState){
case 2: // 2 已经接收到了响应报文的响应头
//可以拿到头 console.log(this.getAllResponseHeaders())
console.log(this.getResponseHeader('server'))
//但是还没有拿到体
console.log(this.reponseText)
break; case 3: //3 正在下载响应报文的响应体,有可能响应体为空,也有可能不完整
//在这里处理响应体不可靠
console.log(this.responseText)
break; case 4: //4 ok(整个响应报文已经完整的下载下来了)
//这里处理响应体
console.log(this.responseText)
break;
}
})
  • 通过理解每一个状态值的含义得出一个结论:一般我们都是在 readyState 值为 4 时,执行响应的后续逻辑。

  • xhr.onreadystatechange = function(){
    if(this.readyState ===4){
    //后续逻辑
    }
    }

2,3遵循HTTP

本质上 XMLHttpRequest 就是 JavaScript 在 Web 平台中发送 HTTP 请求的手段,所以我们发送出去的请求任然是HTTP 请求,同样符合 HTTP 约定的格式:

//设置请求报文的请求行
xhr.open('GET','./time.php')
//设置请求头
xhr.setRequestHeader('Accept','text/plain')
//设置请求体
xhr.send(null) xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 获取响应状态码
console.log(this.status)
// 获取响应状态描述
console.log(this.statusText)
// 获取响应头信息
console.log(this.getResponseHeader('Content‐Type'))// 指定响应头
console.log(this.getAllResponseHeader()) // 全部响应头
// 获取响应体
console.log(this.responseText)// 文本形式
console.log(this.responseXML) // XML 形式,了解即可不用了
}
}

三.具体用法

3.1 GET 系列请求 (获取内容)

get

delete 一般应用于告诉服务器,从服务器上删除一点东西

head 只想获取响应头内容,告诉服务器响应主体内容不要了

options 试探性请求,发个请求给服务器,看看服务器能不能接收到,能不能返回

通常在一次 GET 请求过程中,参数传递都是通过 URL 地址中的 ? 参数传递。

var xhr = new XMLHttpRequest()
//GET请求传递参数通常使用的是问号传递
//这里可以在请求地址后面加上参数,从而传递数据到服务端
xhr.open('GET','./delete.php?id=1')
//一般在GET请求时无需设置响应体,可以传null或者干脆不传
xhr.send(null)
xhr.onreadystatechange=function(){
if(this.readyState===4){
console.log(this.responseText)
}
} //一般情况下 URL 传递的都是参数性质的数据,而 POST 一般都是业务数据

3.2 POST请求(推送内容,多用于表单,需要推送一些内容)

post

put 和delete对应,一般是想让服务器把我传递的信息存储到服务器上(一般应用于文件和大的数据,图片等)

POST 请求过程中,都是采用请求体承载需要提交的数据。

var xhr = new XMLHttpRequest()
//open 方法的第一个参数的作用就是设置请求的method
xhr.open('POST', './add.php')
//设置请求头中的 Content‐Type 为 application/x‐www‐form‐urlencoded
//标识此次请求的请求体格式为 urlencoded 以便于服务端接收数据
xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded')
// 需要提交到服务端的数据可以通过 send 方法的参数传递
// 格式:key1=value1&key2=value2
xhr.send('key1=value1&key2=value2')
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
console.log(this.responseText)
}
}

3.3 同步和异步

关于同步和异步的概念在生活中有很多常见的场景

xhr.open() 方法第三个参数要求传入的是一个 bool 值,其作用就是设置此次请求是否采用异步方式执行,默认 为 true ,如果需要同步执行可以通过传递 false 实现:

console.log('before ajax')
var xhr = new XMLHttpRequest()
// 默认第三个参数为 true 意味着采用异步方式执行
xhr.open('GET', './time.php', true)
xhr.send(null)
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 这里的代码最后执行
console.log('request done')
}
}
console.log('after ajax')

如果采用同步方式执行,则代码会卡死在 xhr.send() 这一步:

console.log('before ajax')
var xhr = new XMLHttpRequest()
// 同步方式
xhr.open('GET', './time.php', false)
// 同步方式 执行需要 先注册事件再调用 send,否则 readystatechange 无法触发
xhr.onreadystatechange = function () {
if (this.readyState === 4) {
// 这里的代码最后执行
console.log('request done')
}
}
xhr.send(null)
console.log('after ajax')

演示同步异步差异。

一定在发送请求 send() 之前注册 readystatechange (不管同步或者异步)

为了让这个事件可以更加可靠(一定触发),一定是先注册

了解同步模式即可,切记不要使用同步模式。

至此,我们已经大致了解了 AJAX 的基本 API 。

3.4 响应数据格式

提问:如果服务端返回一个复杂数据,该如何处理?

我们关心的就是服务端发出何种格式的数据,这种格式如何在客户端用Javascript解析

  • 对于复杂的响应数据(如复杂的表格表单数据):

  • 首先我们需要进行很多的DOM操作
    //先创建行
    //在创建列
    //再将列添加到行
    //再将行添加再tbody
    //拼接字符串
    等等大量的操作
    这里也引出:Vue,React等响应式数据框架的好处

3.5 处理响应数据渲染

模板引擎

模板引擎实际上就是一个 API,模板引擎有很多种,使用方式大同小异

目的为了可以更容易的将数据渲染到 HTML中

3.6 兼容方案

XMLHttpRequest 在老版本浏览器(IE5/6)中有兼容问题,可以通过另外一种方式代替

var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP')

四.封装

4.1 AJAX请求封装

/**
* 发送一个 AJAX 请求
* @param {String} method 请求方法
* @param {String} url 请求地址
* @param {Object} params 请求参数
* @param {Function} done 请求完成过后需要做的事情(委托/回调)
*/
function ajax (method, url, params, done) {
// 统一转换为大写便于后续判断
method = method.toUpperCase() // 对象形式的参数转换为 urlencoded 格式
var pairs = []
for (var key in params) {
pairs.push(key + '=' + params[key])
}
var querystring = pairs.join('&') var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP') xhr.addEventListener('readystatechange', function () { if (this.readyState !== 4) return // 尝试通过 JSON 格式解析响应体
try {
done(JSON.parse(this.responseText))
} catch (e) {
done(this.responseText)
}
}) // 如果是 GET 请求就设置 URL 地址 问号参数
if (method === 'GET') {
url += '?' + querystring
} xhr.open(method, url) // 如果是 POST 请求就设置请求体
var data = null if (method === 'POST') {
xhr.setRequestHeader('Content‐Type', 'application/x‐www‐form‐urlencoded') data = querystring
}
xhr.send(data)
}
ajax('get', './get.php', { id: 123 }, function (data) {
console.log(data)
})
ajax('post', './post.php', { foo: 'posted data' }, function (data) {
console.log(data)
})

4.2 jQuery中的AJAX

jQuery 中有一套专门针对 AJAX 的封装,功能十分完善,经常使用,需要着重注意。

$.ajax
$.ajax({
url: './get.php',
type: 'get',
dataType: 'json',
data: { id: 1 },
beforeSend: function (xhr) {
console.log('before send')
},
success: function (data) {
console.log(data)
},
error: function (err) {
console.log(err)
},
complete: function () {
console.log('request completed')
}
})

常用参数选项:

  • url:请求地址
  • type:请求方法,默认为 get
  • dataType:服务端响应数据类型
  • contentType:请求体内容类型,默认 application/x-www-form-urlencoded
  • data:需要传递到服务端的数据,如果 GET 则通过 URL 传递,如果 POST 则通过请求体传递
  • timeout:请求超时时间
  • beforeSend:请求发起之前触发
  • success:请求成功之后触发(响应状态码 200)
  • error:请求失败触发
  • complete:请求完成触发(不管成功与否都回进行的操作)
$.get
上面的$.ajax接近于底层API
jquery中还有高度封装的API
$.get('time.php',function(res){
console.log(res)
})
$post
$.post('time.php',function(res){
console.log(res)
})

4.3 其他

  • $.getJSON() ----不管返回什么,都转成json格式
  • $(selector).load() ---可实现局部加载
  • $.getScript() ---
  • 还有很多,查阅官方文档(也可查看别人翻译的中文文档)

五.跨域

跨域可以说跟ajax一点关系没有,也可以说与ajax有着绝对性的联系

5.1相关概念

同源策略是浏览器的一种安全策略,所谓同源是指,域名,协议,接口,完全相同,只有同源的地址才可以相互通过AJAX的方式请求。

同源或不同源说的是两个地址之间的关系,不同源地址之间请求我们称之为跨域请求

同源的页面数据能够相互请求

什么是同源?例如:http://www.example.com/detail.html 与一下地址对比

对比地址 是否同源 原因
http://api.example.com/detail.html 不同源 域名不同
https://www.example.com/detail.html 不同源 协议不同
http://www.example.com:8080/detail.html 不同源 端口不同
http://api.example.com:8080/detail.html 不同源 域名,端口不同
https://api.example.com/detail.html 不同源 协议、域名不同
https://www.example.com:8080/detail.html 不同源 端口、协议不同
http://www.example.com/other.html 同源 只是目录不同

5.2 解决方案

5.2.1 JSONP

JSON with Padding,(padding:边距,补充)(jsonp可理解为json的补充)是一种借助于 script 标签发送跨域请求的技巧。

其原理就是在客户端借助 script 标签请求服务端的一个动态网页(php 文件),服务端的这个动态网页返回一 段带有函数调用的 JavaScript 全局函数调用的脚本,将原本需要返回给客户端的数据传递进去。

以后绝大多数情况都是采用 JSONP 的手段完成不同源地址之间的跨域请求

客户端 http://www.zce.me/users-list.html

<script src="http://api.zce.me/users.php?callback=foo"></script>

服务端 http://api.zce.me/users.php?callback=foo 返回的结果

foo(['我', '是', '你', '原', '本', '需', '要', '的', '数', '据'])

总结一下:由于 XMLHttpRequest 无法发送不同源地址之间的跨域请求,所以我们必须要另寻他法,script 这种方 案就是我们最终选择的方式,我们把这种方式称之为 JSONP,如果你不了解原理,先记住怎么用,多用一段时间再 来看原理。

问题:

  1. JSONP 需要服务端配合,服务端按照客户端的要求返回一段 JavaScript 调用客户端的函数

  2. 只能发送 GET 请求

注意:JSONP 用的是 script 标签,更 AJAX 提供的 XMLHttpRequest 没有任何关系!!!

jQuery 中使用 JSONP 就是将 dataType 设置为 jsonp 76

其他常见的 AJAX 封装 库: Axios

5.2.2 CORS

Cross Origin Resource Share ,跨域资源共享 (直接请求跨域资源)

但是因为版本比较新,存在一些兼容问题

// 允许远端访问
header('Access‐Control‐Allow‐Origin: *');

这种方案无需客户端作出任何变化(客户端不用改代码),只是在被请求的服务端响应的时候添加一个 Access- Control-Allow-Origin 的响应头,表示这个资源是否允许指定域请求。

六.ajax倒计时抢购案例

<script>
//new Date()获取客户端本地当前时间(不能拿它做重要依据,因为用户可以随时更改)
//倒计时抢购需要从服务器获取当前时间,而不是从本地获取 AJAX
//问题:时间差(从服务器把时间给客户端,到客户端获取到这个信息,中间经历的时间就是时间差,而时间差是不可避免的,我们应尽可能减少这个误差)
- 从响应头获取时间(AJAX异步)
-基于HEAD请求(只获取响应头信息) let target = new Date('2019/09/14 18:00:00');
now = null ;
timer = null;
//我们不可能每一秒都从服务器请求最新时间,应该基于第一次获取的时间,用这个时间来记时 //=>从服务器获取时间,获取到时间后再做其他事情
function func(callback){
let xhr = new XMLHttpRequest;
xhr.open('HEAD','json/data.json');
xhr.onreadystatechange = function(){
if(!/^(2|3)\d{2}$).test(xhr.status) return ;
if(xhr.readyState ===2){
now = new Date(xhr.getResponseHeader('Date'))
//如果传进来的是个函数,就把它执行
callback && callback();
}
}
xhr.send(null)
} //=>开启倒计时模式
function computed (){
let spanTime = target -now;
if(spanTime <=0){
//=>到抢购点了:结束定时器
clearInterval(timer);
timer = null;
box.innerHTML = "开抢~";
return
}
//还剩多少个小时
let hours = Math.floor(spanTime / (60*60*1000) )
spanTime -= hours* 60*60*1000;
let minutes = Math.floor(spanTime / (60*1000));
spanTime -= minutes *60*1000
let seconds =Math.floor( spanTime /1000)
box.innerHTML = `距离抢购还剩:${hours}:${minutes}:${seconds}` //=>每一次计算完,我们需要让now再原来的基础上增加一秒(第一次从服务器获取到时间,后期直接基于这个时间自己减即可,不要每隔一秒从服务器拿一次,服务器压力会特别大)
//每隔一秒,从服务器获取的时候,增加一
// 错误:now=new Date(now + 1000); 原因:里面编程字符串拼接了,now是字符串
now = new Date(now.getTime()+1000)
}
func(()=>{
//=>这个函数能执行,说明已经从服务器获取时间了
computed();
timer =setInterval(computed ,1000); }) <script> //除了完成这个功能外我们还应该收获到什么?再json传值,或者new Date()获取的时间的时候,很多时候都是字符串,这个时候我们不能直接使用它去做加法,因为可能会造成隐式转换,变成字符串拼接

Ajax和跨域请求的更多相关文章

  1. 原生JS实现Ajax及Ajax的跨域请求

      前  言          如今,从事前端方面的程序猿们,如果,不懂一些前后台的数据交互方面的知识的话,估计都不太好意思说自己是程序猿.当然,如今有着许多的框架,都有相对应的前后台数据交互的方法. ...

  2. Ajax之跨域请求

    一.引子 我现在开启了两个django项目,分别叫Demo1和Demo2,Demo1中有一个路径‘http://127.0.0.1:8000/index/’,对应的视图是index视图返回一个inde ...

  3. 利用Nginx轻松实现Ajax的跨域请求(前后端分离开发调试必备神技)

    利用Nginx轻松实现浏览器中Ajax的跨域请求(前后端分离开发调试必备神技) 前言 为什么会出现跨域? 造成跨域问题的原因是因为浏览器受到同源策略的限制,也就是说js只能访问和操作自己域下的资源,不 ...

  4. 原生JS实现Ajax的跨域请求

    原生JS如何实现Ajax的跨域请求? 在解决这个问题之前,我们务必先清楚为什么我们要跨域请求,以及在什么情况下会跨域请求. 了解一下:“同源策略”,你就知道了: 同源策略限制从一个源加载的文档或脚本如 ...

  5. jQuery的Ajax的跨域请求

    今天碰到一个Ajax跨域请求的问题,我把源码down下来,然后在服务器端写了一个http请求的代理(因为服务器端是不存在跨域问题的),说白了就是用BufferedReader写了个IO流,然后读取到目 ...

  6. jQuery的ajax jsonp跨域请求

    了解:ajax.json.jsonp.“跨域”的关系 要弄清楚以上ajax.json.jsonp概念的关系,我觉得弄清楚ajax是“干什么的”,“怎么实现的”,“有什么问题”,“如果解决存在的问题”等 ...

  7. ajax j跨域请求sonp

    需求 遇到的问题 解决方案 需求 如今,该项目需要获得数据访问外部链接.它是跨域.使用ajax 直提示: 遇到的问题 1. 怎样使用ajax 跨域请求数据 2. 能不能post请求 解决的方法 经过网 ...

  8. ajax的跨域请求

    同源策略 同源策略(Same origin policy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响.可以说Web是构建在同源策略基础之上的 ...

  9. Ajax jsonp 跨域请求实例

    跨域请求 JSONP的缺点则是:它只支持GET请求而不支持POST等其它类型的HTTP请求:它只支持跨域HTTP请求这种情况,不能解决不同域的两个页面之间如何进行JavaScript调用的问题. $. ...

  10. ajax POST跨域请求完美解决

    方式: js前端请求: function getOcrInfo(imageData){$.ajax({   url: 'http://localhost:8080/LSWS/ws/ocr/getWeb ...

随机推荐

  1. MySQL删除大表时潜在的问题(drop table,truncate table)

    来源于:https://www.cnblogs.com/CtripDBA/p/11465315.html,侵删,纯截图,避免吸引流量之嫌 case1,删除大表时,因为清理自适应hash索引占用的内容导 ...

  2. MySQL语句使用。

    目录 MySQL的DDL.DML.DQL语句和单表增.删.改.查 实验准备: 实验开始: DDL语句 DML语句 DQL语句 单表操作的分组统计 MySQL的DDL.DML.DQL语句和单表增.删.改 ...

  3. 12c新特性 在线操作数据文件

    我们都知道,oracle pre-12c之前,若是想要把一个数据文件改名或者迁移, 必须在归档模式下先把这个数据文件offline之后, 然后进行OS上的copy或者rename 操作, 最后在sql ...

  4. Java之Collection接口(单列集合根接口)

    集合概述 集合到底是什么呢?集合:集合是java中提供的一种容器,可以用来存储多个数据 集合和数组既然都是容器,它们有啥区别呢? 区别1: 数组的长度是固定的. 集合的长度是可变的. 区别2:  数组 ...

  5. 20191214 Codeforces Round #606 (Div. 2, based on Technocup 2020 Elimination Round 4)

    概述 切了 ABCE,Room83 第一 还行吧 A - Happy Birthday, Polycarp! 题解 显然这样的数不会很多. 于是可以通过构造法,直接求出 \([1,10^9]\) 内所 ...

  6. 【RTOS】基于V7开发板的uCOS-III,uCOS-II,RTX4,RTX5,FreeRTOS原版和带CMSIS-RTOS V2封装层版全部集齐

    RTOS模板制作好后,后面堆各种中间件就方便了. 1.基于V7开发板的最新版uCOS-II V2.92.16程序模板,含MDK和IAR,支持uC/Probe https://www.cnblogs.c ...

  7. AES 对称加密

    package com.skynet.rimp.common.utils.string; import java.io.UnsupportedEncodingException; import jav ...

  8. 什么是单点登录,php是如何实现单点登录的

    单点登录SSO(Single Sign On)说得简单点就是在一个多系统共存的环境下,用户在一处登录后,就不用在其他系统中登录,也就是用户的一次登录能得到其他所有系统的信任.单点登录在大型网站里使用得 ...

  9. koa2 从入门到进阶之路 (六)

    之前的文章我们介绍了一下 koa post提交数据及 koa-bodyparser中间件,本篇文章我们来看一下 koa-static静态资源中间件. 我们在之前的目录想引入外部的 js,css,img ...

  10. 双链表算法原理【Java实现】(八)

    前言 前面两节内容我们详细介绍了ArrayList,一是手写实现ArrayList数据结构,而是通过分析ArrayList源码看看内置实现,关于集合内容一如既往,本节课我们继续学习集合LinkedLi ...