AJAX
- 传统网站的弊端
应用场景
- 动态数据加载
- 表单验证
实现
原生方式
let xhr = new XMLHttpRequest();// 发送get请求 通过url传递参数xhr.open('get',"/home");xhr.send();xhr.onload = function(){ console.log(xhr.responseText);}
- post
xhr.open('post',"/home");xhr.setRequestHeader('Content-Type',"application/json");xhr.send('{"name":"jntm"}');
ajax状态码
- 0:请求未初始化(还没有调用open())
- 1:请求已经建立,但是还没有发送(还没有调用send())
- 2:请求已经发送
- 3:请求正在处理中,通常响应中已经有部分数据可以用了
- 4:响应已经完成,可以获取并使用服务器的响应了
xhr.onreadystatechange = function(){ console.log(xhr.readyState);}
区别描述 | onload事件 | onreadystatechange事件 |
---|---|---|
是否兼容IE低版本 | 不兼容 | 兼容 |
是否需要判断Ajax状态码 | 不需要 | 需要 |
被调用次数 | 一次 | 多次 |
错误处理
- 后端能调通,但是返回错误
xhr.status
- 网络中断,后端无法调通
会触发xhr对象下面的onerror事件,在onerror事件处理函数中对错误进行处理
- 低版本ie存在ajax缓存问题
ajax封装
function ajax(req) { let xhr = new XMLHttpRequest(); let params = ""; for (let attr in req.data) { params += attr + "=" + req.data[attr] + "&"; } params = params.substr(0, params.length - 1); xhr.open(req.type, req.type != 'get' ? req.url:req.url + "?" + params); for(let attr in req.headers){ xhr.setRequestHeader(attr,req.headers[attr]); } if (req.type == 'get'){ xhr.send(); }else{ if (req.headers['Content-Type']=='application/json'){ xhr.send(JSON.stringify(req.data)); }else{ xhr.send(params); } } xhr.onload = function () { let contentType = xhr.getResponseHeader("Content-Type"); let responseText = xhr.responseText; if (contentType.includes("application/json")){ responseText = JSON.parse(responseText); } if (xhr.status === 200) { req.success && req.success(responseText); } else { req.error && req.error(responseText); } };}ajax({ url: '/home', type: 'get', data: {name: "cxk", age: 18}, headers:{ "Content-Type":"application/json" }, success: function (res) { console.log('normal res', res); }, error: function (res) { console.error('error res', res); }})
JQuery实现方式
- ajax
$.ajax({ url:"./" , // 请求路径 type:"POST" , //请求方式 //data: "username=jack&age=23",//请求参数 data:{"username":"jack","age":23}, success:function (data) { alert(data); },//响应成功后的回调函数 error:function () { alert("出错啦...") },//表示如果请求响应出现错误,会执行的回调函数});
发送jsonp请求
$.ajax({ url: 'http://www.example.com', // 指定当前发送jsonp请求 dataType: 'jsonp', // 修改callback参数名称 jsonp: 'cb', // 指定函数名称 jsonCallback: 'fnName', success: function (response) {} })
- $.get
- $.post
- serialize
$("#form").serialize(); // 将表单输入的内容转换成如k=v&a=b这种形式
全局事件
.ajaxStart() // 当请求开始发送时触发.ajaxComplete() // 当请求完成时触发
配合nprogress来实现页面加载进度条
FormData
- 表单上传
- 二进制文件上传
简单使用
<form id="form"> <input type="text" name="username"/> <input type="password" name="password"/> <input type="file" name="file"> <input type="button" id="btn"/></form>
let form = document.querySelector("#form");let formData = new FormData(form);let xhr = new XMLHttpRequest();xhr.open('post','/formData');xhr.send(formData);
实例方法
// 获取表单对象属性formData.get('username');// 设置表单对象属性formData.set("username","cxk");// 删除表单对象属性formData.delete("username");// 追加表单对象属性,属性名已存在的情况下,set会覆盖,append会保留formData.append("username","cxk");
文件上传进度展示
xhr.upload.onprogress = function (ev) { console.log(ev.loaded / ev.total)}
图片上传实时预览
var file = document.querySelector('#file');file.onchange = function(){ var reader = new FileReader(); reader.readAsDataURL(this.files[0]); reader.onload = function(){ document.querySelector('.img-thumbnail').src = reader.result; }}
使用Promise发送ajax
function query() { return new Promise((resolve, reject) => { let xhr = new XMLHttpRequest(); xhr.onreadystatechange = () => { if (xhr.readyState != 4) { return; } if (xhr.readyState == 4 && xhr.status == 200) { resolve(xhr.responseText); } else { reject(xhr.status); } }; xhr.open('get', '/home'); xhr.send(); });}query().then(r => console.log(r)).catch(r => console.error(r))
fetch
Fetch API是新的ajax解决方案
fetch不是ajax的进一步封装,而是原生js,没有使用XMLHttpRequest对象
fetch('/home').then(data=>{ return data.text();}).then(data=>{ console.log(data);});
data是一个response对象,其中包括返回的一堆原始字节,这些字节需要在收到后,需要我们通过调用方法将其转换为相应格式的数据,比如JSON
,BLOB
或者TEXT
等等
- 传递参数
let formData = new FormData();formData.append('username','cxk');formData.append('password','jntm');fetch('/formData',{ method:'post', body:formData})
axios
axios.get('/home') .then(res=>{ console.log(res); })
let formData = new FormData();formData.append('username','cxk');formData.append('password','jntm');// 默认传递的是jsonaxios.post('/formData',formData) .then(res=>{ console.log(res); })
- 同步调用
async function f(){ let res = await axios.post('/formData',formData); console.log(res);}
全局参数
// 配置公共的请求头 axios.defaults.baseURL = 'https://api.example.com';// 配置 超时时间axios.defaults.timeout = 2500;// 配置公共的请求头axios.defaults.headers.common['Authorization'] = AUTH_TOKEN;// 配置公共的 post 的 Content-Typeaxios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded';
拦截器
// 请求拦截器axios.interceptors.request.use(function(config) { console.log(config.url) // 这里一定要return 否则配置不成功 return config;}, function(err){ // 对请求错误做点什么 console.log(err)})// 响应拦截器axios.interceptors.response.use(function(res) { console.log(res.data); return res.data;}, function(err){ console.log(err)});
同源策略
ajax受同源策略限制
JSONP
原理:
客户端定义一个函数:
function fn (data) { // 接收到服务器data后的一些处理 }
服务端返回一个函数调用:
const data = 'fn({name: "张三", age: "20"})';
客户端使用script可以跨域加载数据
<script src="server_ajax_address"></script>
这样客户端就可以获取服务端的数据
function fn(data){ console.log('server res',data);}let script = document.createElement('script');script.src = '/jsonp?v=1';document.body.appendChild(script);
@GetMapping("/jsonp")public String jsonp(){ return "fn({name:'cxk',age:18})";}
优化
- 回调函数名称参数化
- 回调函数参数化
- 注意回调函数覆盖问题
- url参数化
- 接收到回调后删除script标签
CORS
全称为 Cross-origin resource sharing,即跨域资源共享,它允许浏览器向跨域服务器发送 Ajax 请求,克服了 Ajax 只能同源使用的限制
通过设置Access-Control-Allow-Origin
和Access-Control-Allow-Methods
响应头来允许哪些源可以使用哪些方法访问
@RestController@CrossOrigin("*")public class Controller {...}
withCredentials:指定在涉及到跨域请求时,是否携带cookie信息,默认值为false