Ajax异步请求

目录

  • GET请求
  • Post请求
  • 服务器返回结果是JSON
  • IE缓存问题
  • AJAX请求超时和网络异常处理
  • 取消请求
  • 重复请求
  • 将Ajax封装成Promise
  • JSONP
    • 原理
    • 实践
  • 面试常考题
    • Ajax
    • 目的
    • 运用场景
    • 工作原理
    • readyState

服务器代码:

//1. 引入express
const express = require('express');

//2. 创建应用对象
const app = express();

//3. 创建路由规则
// request 是对请求报文的封装
// response 是对响应报文的封装
/*
如果这里写的是/server的话 那么当客户端浏览器向服务器发送get请求时 若url的路径 即请求行的第二段内容的路径是/server
就会执行回调函数(request, response)=>{});里面的代码 并由response做出响应
 */
app.get('/server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.send('HELLO AJAX GET');
});
app.all('/json-server', (request, response)=>{
    //all表示接收任意类型的http请求 不管是post get options delete
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Header','*');//接收所有头信息
    //响应一个数据
    const data = {
        name:'cara'
    };
    let str = JSON.stringify(data);//对对象进行字符串转换
    response.send(str);//send中只能接收字符串或buffer
});
//post请求 同get
app.post('/server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.send('HELLO AJAX POST');
});
//针对ie缓存的请求
app.get('/ie', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.send('HELLO IE -2');
});
//服务器延时2S
app.get('/delay', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    setTimeout(() => {
        response.send('延时响应');
    },3000)
});
//jquery服务
app.all('/jquery-server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Header','*');//接收所有头信息
    const data = {name:'cara'};
    response.send(JSON.stringify(data));
});
//axios服务
app.all('/axios-server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Header','*');//接收所有头信息
    const data = {name:'cara'};
    response.send(JSON.stringify(data));
});
//fetch服务
app.all('/axios-server', (request, response)=>{
    //设置响应头 设置允许跨域
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Header','*');//接收所有头信息
    const data = {name:'cara'};
    response.send(JSON.stringify(data));
});
//跨域
app.all('/jsonp-server', (request, response)=>{
    const data = {
        name:'cara'
    };
    //将数据转化为字符串
    let str =JSON.stringify(data);
    //返回结果
    response.end('handle(${str})')//用end取代send 因为end不会加特殊响应头
    //此处返回的是函数调用 函数的实参是我们想给客户端返回的结果数据 这就是JSONP跨域的原理:服务器返回函数(这个函数前端必须提前声明) 传入想要返回的结果数据
});
//JSONP实践:检测用户名是否存在
app.all('/check-username', (request, response)=>{
    const data = {
        exist:'1',
        msg:'用户名已存在'
    };
    //将数据转化为字符串
    let str =JSON.stringify(data);
    //返回结果
    response.end(`handle(${str})`)//用end取代send 因为end不会加特殊响应头
    //此处返回的是函数调用 函数的实参是我们想给客户端返回的结果数据 这就是JSONP跨域的原理:服务器返回函数(这个函数前端必须提前声明) 传入想要返回的结果数据
});

app.all('/jquery-jsonp-server', (request, response)=>{
    const data = {
        name:'尚硅谷',
        city:['北京','上海','深圳']
    };
    //将数据转化为字符串
    let str =JSON.stringify(data);
    //接收callback参数
    let cb = request.query.callback;
    //返回结果
    response.end(`${cb}(${str})`)//用end取代send 因为end不会加特殊响应头
    //此处返回的是函数调用 函数的实参是我们想给客户端返回的结果数据 这就是JSONP跨域的原理:服务器返回函数(这个函数前端必须提前声明) 传入想要返回的结果数据
});

app.all('/jquery-jsonp-server', (request, response)=>{
    const data = {
        name:'尚硅谷',
        city:['北京','上海','深圳']
    };
    //将数据转化为字符串
    let str =JSON.stringify(data);
    //接收callback参数
    let cb = request.query.callback;
    //返回结果
    response.end(`${cb}(${str})`)//用end取代send 因为end不会加特殊响应头
    //此处返回的是函数调用 函数的实参是我们想给客户端返回的结果数据 这就是JSONP跨域的原理:服务器返回函数(这个函数前端必须提前声明) 传入想要返回的结果数据
});

app.all('/cors-server', (request, response)=>{
    //哪些网页可以给我们发请求 *代表所有网页 如果你只想允许某一个URL 则将*改为该URL即可
    response.setHeader('Access-Control-Allow-Origin','*');
    response.setHeader('Access-Control-Allow-Headers','*');//任何请求头都可以
    response.setHeader('Access-Control-Allow-Method','*');//任何请求方法都可以
    //返回结果
    response.send(`${cb}(${str})`)//用end取代send 因为end不会加特殊响应头
    //此处返回的是函数调用 函数的实参是我们想给客户端返回的结果数据 这就是JSONP跨域的原理:服务器返回函数(这个函数前端必须提前声明) 传入想要返回的结果数据
});

//4. 监听端口启动服务
app.listen(8000, ()=>{
    console.log("服务已经启动, 8000 端口监听中....");
});
//最后在终端进入D:\JAVA projects\nodejs\src文件后输入node express基本使用.js
//在浏览器地址栏输入http://127.0.0.1:8000/     即可
/*
安装nodemon:先确保安装了nodejs 然后在下面终端输入npm install -g nodemon 而后在终端输入nodemon server.js     即可启动服务
nodemon是一种工具,可以自动检测到目录中的文件更改时通过重新启动应用程序来调试基于node.js的应用程序。
如果不安装nodemon 那么我们每次更改了代码都需要在终端重新启动服务器 也就是在终端输入nodemon server.js
安装了nodemon之后 启动了服务器后更改代码 服务器会自动重新启动 不用再每次在终端输入nodemon server.js 手动重启服务器
*/

GET请求

创建ajax过程

  1. 创建XMLHttpRequest对象,也就是创建一个异步调用对象.
  2. 创建一个新的HTTP请求,并指定该HTTP请求的方法、URL及验证信息.
  3. 设置响应HTTP请求状态变化的函数.
  4. 发送HTTP请求.
  5. 获取异步调用返回的数据.
  6. 使用JavaScript和DOM实现局部刷新.
<body>
<!--
ajax请求发送前的准备:前端准备:GET 服务器准备:SERVER
项目需求:点击按钮向服务端发送请求 服务端返回的响应体结果显示在div中 页面不刷新
-->

<button>点击发送请求</button>
<div id="result"></div>

<script>
    //获取button元素
    const btn = document.getElementsByTagName('button')[0];
    const result = document.getElementById('result');
    //绑定事件
    btn.onclick = function (){
        //创建对象
        const xhr = new XMLHttpRequest();
        //使用open函数初始化 设置请求方法和url
        //在ajax中设置GET的参数:在url中 ?后面就是参数 键=值 &是多个参数的分隔符
        //http://127.0.0.1:8000/server?a=100&b=200&c=300
        xhr.open('GET','http://127.0.0.1:8000/server');
        //发送
        xhr.send();
        //事件绑定 处理服务端返回的结果
        //on:when 当什么时候
        //readystate:xhr对象中的属性 表示状态
        //有5个取值:0(表示未初始化 readystate最开始属性值就是0) 1(表示open方法调用完毕) 2(表示send方法调用完毕) 3(服务端返回了部分结果) 4(服务端返回了全部结果)
        //有5个取值 所以一共触发4次 0到1一次 1到2一次 2到3一次 3到4一次
        //所以我们在取值为4时 处理服务端返回的结果
        //change:改变
        xhr.onreadystatechange = function (){
            if (xhr.readyState === 4){//判断(服务器返回了所有结果)
                //判断响应状态码:200 404 403 401 500等 只要是2xx都表示成功
                if (xhr.status >= 200 && xhr.status < 300){
                    //响应成功 处理结果 服务端返回的结果:行 头 空行 体
                    //响应
                    // console.log(xhr.status);//状态码
                    // console.log(xhr.statusText);//状态字符串
                    // console.log(xhr.getAllResponseHeaders());//所有响应头
                    // console.log(xhr.response);//响应体

                    result.innerHTML = xhr.response;//设置result的文本
                }
            }
        }

    }
</script>
</body>

Post请求

<body>
<!--
需求:鼠标停在div上面 客户端浏览器向服务器发送POST请求 然后服务端返回的响应体结果显示在div中 页面不刷新
-->
<div id="result"></div>
<script>
    //获取元素对象
    const result = document.getElementById('result');
    //绑定事件
    result.addEventListener("mouseover",function (){
        //1创建对象
        const xhr = new XMLHttpRequest();
        //2使用open函数初始化 设置请求方法和url
        xhr.open('POST','http://127.0.0.1:8000/server');
        //用setRequestHeader来设置请求头:
        //Content-Type:设置请求体类型 application/x-www-form-urlencoded:参数查询字符串'a=100&b=200&c=300'的类型 固定写法
        xhr.setRequestHeader('Content-Type','application/x-www-form-urlencoded');
        //使用自定义的请求头 但是此时需要在服务器声明:response.setHeader('Access-Control-Allow-Header','*');表示接收所有请求头信息 并且使用app.all
        xhr.setRequestHeader('name',"cara");
        //3发送
        //post的参数在send函数中设置 参数用''引起来 参数格式:键=值或键:值或只写值(只要服务器看得懂 能解析 这里可以灵活书写) &是多个参数的分隔符
        xhr.send('a=100&b=200&c=300');
        // xhr.send('a:100&b:200&c:300');
        // xhr.send('100');
        //4事件绑定 处理服务端返回的结果
        xhr.onreadystatechange = function (){
            if (xhr.readyState === 4){//判断(服务器返回了所有结果)
                //判断响应状态码:200 404 403 401 500等 只要是2xx都表示成功
                if (xhr.status >= 200 && xhr.status < 300){
                    result.innerHTML = xhr.response;//设置result的文本
                }
            }
        }
    })
</script>
</body>

服务器返回结果是JSON

<body>
<!--
需求:按下键盘上的按键 客户端浏览器向服务器发送GET请求 然后服务端返回的响应体结果显示在div中 页面不刷新
结果是一个json格式字符串 数据复杂时想要提取其中某一个数字就很困难 所以我们可以手动对字符进行转换 自动对字符进行转换
-->
<div id="result"></div>
<script>
    //获取元素对象
    const result = document.getElementById('result');
    //绑定事件
    window.onkeydown = function (){
        //创建对象
        const xhr = new XMLHttpRequest();
        xhr.responseType = 'json';//自动对字符进行转换:设置响应体数据的类型
        //使用open函数初始化 设置请求方法和url
        xhr.open('GET','http://127.0.0.1:8000/json-server');
        //发送
        xhr.send();
        //事件绑定 处理服务端返回的结果
        xhr.onreadystatechange = function (){
            if (xhr.readyState === 4){//判断(服务器返回了所有结果)
                //判断响应状态码:200 404 403 401 500等 只要是2xx都表示成功
                if (xhr.status >= 200 && xhr.status < 300){
                    //结果是一个json格式字符串 数据复杂时想要提取其中某一个数字就很困难 所以我们可以手动对字符进行转换 自动对字符进行转换
                    // let data = JSON.parse(xhr.response);
                    // console.log(data);
                    // result.innerHTML = data.name;

                    //自动对字符进行转换:如上所示:xhr.responseType = 'json';
                    result.innerHTML = xhr.response.name;
                }
            }
        }
    }
</script>
</body>

IE缓存问题

<body>
<!--
ajax请求发送前的准备:前端准备:GET 服务器准备:SERVER
项目需求:点击按钮向服务端发送请求 服务端返回的响应体结果显示在div中 页面不刷新
-->

<button>点击发送请求</button>
<div id="result"></div>

<script>
    //获取button元素
    const btn = document.getElementsByTagName('button')[0];
    const result = document.getElementById('result');
    //绑定事件
    btn.onclick = function (){
        //创建对象
        const xhr = new XMLHttpRequest();
        //使用open函数初始化 设置请求方法和url
        //在ajax中设置GET的参数:在url中 ?后面就是参数 键=值 &是多个参数的分隔符
        //http://127.0.0.1:8000/server?a=100&b=200&c=300
        /*
            IE浏览器会对AJAX的请求结果做一个缓存 下一次再发送请求时走的是本地缓存 而非服务器返回的最新数据
            对于时效性比较强的场景 AJAX的缓存会影响结果 不能显示服务器正确的返回结果
            AJAX解决IE缓存的方法:在连接后面加时间戳 因为时间戳时不一样的 所以浏览器会认为这是两次请求 就不会走本地缓存
         */
        xhr.open('GET','http://127.0.0.1:8000/ie?t='+Data.now());//?t='+Data.now()加上时间戳
        //发送
        xhr.send();
        //事件绑定 处理服务端返回的结果
        //on:when 当什么时候
        //readystate:xhr对象中的属性 表示状态
        //有5个取值:0(表示未初始化 readystate最开始属性值就是0) 1(表示open方法调用完毕) 2(表示send方法调用完毕) 3(服务端返回了部分结果) 4(服务端返回了全部结果)
        //有5个取值 所以一共触发4次 0到1一次 1到2一次 2到3一次 3到4一次
        //所以我们在取值为4时 处理服务端返回的结果
        //change:改变
        xhr.onreadystatechange = function (){
            if (xhr.readyState === 4){//判断(服务器返回了所有结果)
                //判断响应状态码:200 404 403 401 500等 只要是2xx都表示成功
                if (xhr.status >= 200 && xhr.status < 300){
                    //响应成功 处理结果 服务端返回的结果:行 头 空行 体
                    //响应
                    // console.log(xhr.status);//状态码
                    // console.log(xhr.statusText);//状态字符串
                    // console.log(xhr.getAllResponseHeaders());//所有响应头
                    // console.log(xhr.response);//响应体

                    result.innerHTML = xhr.response;//设置result的文本
                }
            }
        }

    }
</script>
</body>

AJAX请求超时和网络异常处理

<body>
<!--
ajax请求发送前的准备:前端准备:GET 服务器准备:SERVER
项目需求:点击按钮向服务端发送请求 服务端返回的响应体结果 代码中加入超时设置 超时2s后还未返回结果 则提醒用户:网络超时 请稍后重试
-->

<button>点击发送请求</button>
<div id="result"></div>

<script>
    //获取button元素
    const btn = document.getElementsByTagName('button')[0];
    const result = document.getElementById('result');
    //绑定事件
    btn.onclick = function (){
        //创建对象
        const xhr = new XMLHttpRequest();
        //服务器超时返回数据 前端页面的操作:
        xhr.timeout = 2000;//超时设置2S 2S还没有结果返回 就取消请求
        //超时回调
        xhr.ontimeout = function (){// 超时后会执行这个回调函数ontimeout
            alert("网络异常 请稍后重试!");
        }
        //网络异常回调onerror
        xhr.onerror = function (){
            alert("你的网络似乎出现了一些问题!");//如何模拟真的断网:在浏览器中开发者工具中选择network 点击offline
        }
        xhr.open('GET','http://127.0.0.1:8000/delay');
        //发送
        xhr.send();
        //事件绑定 处理服务端返回的结果
        //on:when 当什么时候
        //readystate:xhr对象中的属性 表示状态
        //有5个取值:0(表示未初始化 readystate最开始属性值就是0) 1(表示open方法调用完毕) 2(表示send方法调用完毕) 3(服务端返回了部分结果) 4(服务端返回了全部结果)
        //有5个取值 所以一共触发4次 0到1一次 1到2一次 2到3一次 3到4一次
        //所以我们在取值为4时 处理服务端返回的结果
        //change:改变
        xhr.onreadystatechange = function (){
            if (xhr.readyState === 4){//判断(服务器返回了所有结果)
                //判断响应状态码:200 404 403 401 500等 只要是2xx都表示成功
                if (xhr.status >= 200 && xhr.status < 300){
                    //响应成功 处理结果 服务端返回的结果:行 头 空行 体
                    //响应
                    // console.log(xhr.status);//状态码
                    // console.log(xhr.statusText);//状态字符串
                    // console.log(xhr.getAllResponseHeaders());//所有响应头
                    // console.log(xhr.response);//响应体

                    result.innerHTML = xhr.response;//设置result的文本
                }
            }
        }

    }
</script>
</body>

取消请求

<body>
<!--
ajax请求发送前的准备:前端准备:GET 服务器准备:SERVER
项目需求:AJAX请求超时和网络异常处理中 我们利用xhr.timeout = 2000;来取消发送请求 本文介绍人工手动取消请求
-->

<button>点击发送</button>
<button>点击取消</button>

<script>
    //获取button元素
    const btns = document.querySelectorAll('button');
    let x = null;
    //绑定事件
    btns[0].onclick = function (){
        //创建对象
        x = new XMLHttpRequest();
        x.open('GET','http://127.0.0.1:8000/delay');
        //发送
        x.send();
    }

    //abort:用来取消请求 abort是XMLHttpRequest的方法 所以用x直接调用即可
    btns[1].onclick = function(){
        x.abort();
    }
</script>

重复请求

<body>
<!--
ajax请求发送前的准备:前端准备:GET 服务器准备:SERVER
项目需求:如果用户狂点按钮 不停向服务器发送相同的请求 此时会增大服务器压力
如何解决:点击按钮后查看之前是否有相同请求 若有 则取消掉之前的请求发送新的请求
-->

<button>点击发送</button>

<script>
    //获取button元素
    const btns = document.querySelectorAll('button');
    let x = null;
    let isSending = false;//标识变量 是否正在发送AJAX请求
    //绑定事件
    btns[0].onclick = function (){
        //判断标识变量
        if (isSending) x.abort();//如果正在发送 则取消该请求 创建一个新的请求
        //创建对象
        x = new XMLHttpRequest();
        isSending = true;
        x.open('GET','http://127.0.0.1:8000/delay');
        //发送
        x.send();
        x.onreadystatechange = function (){
            if (x.readyState === 4){
                isSending = false;//修改标识变量
            }
        }
    }
</script>
</body>

将Ajax封装成Promise

/*** ajax数据请求接口
  	@param {string} type 请求方式 {"get(默认)" | "GET" | "post" | "POST"}
 	@param {string} url 请求接口地址
  	@param {Object} data 请求时后台所需参数
  	@param {bool} async 是否异步(true)或同步(false)请求{true(默认) | false}	  	
  	@example: $ajax({
  					type: "post", 
  					url: "", 
  					data: {}
  				}).then(function(ret){
  				
  				}).then(function(err){
  				
  				});
*/
var ajax = function({type, url, data, async}){
	let ajax;		
	ajax = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject("Microsoft.XMLHTTP");
	type = !type ? "get" : type;
	data = !data ? {} : data;
	async != false ? !async ? async = true : async = async : '';
	return new Promise(function(resolve,reject){
		let type = type.toUpperCase();
		// get 跟post  需要分别写不同的代码
		if (type === "GET") {// get请求
			if (data) {// 如果有值
				url += '?';										
				if( typeof data === 'object' ){ // 如果有值 从send发送
					var convertResult = "" ;
					for(var c in data){
						convertResult += c + "=" + data[c] + "&";
					}						
					url += convertResult.substring(0,convertResult.length-1);
				}else{
					url += data;
				}
			}
			ajax.open(type, url, async); // 设置 方法 以及 url
			ajax.send();// send即可
		}
		else if(type === "POST"){// post请求
			ajax.open(type, url); // post请求 url 是不需要改变
			// 需要设置请求报文
			ajax.setRequestHeader("Content-type","application/x-www-form-urlencoded"); 
			if(data){ // 判断data send发送数据
				if( typeof data === 'object' ){// 如果有值 从send发送
					var convertResult = "" ;
					for(var c in data){
					  convertResult += c + "=" + data[c] + "&";
					}
					convertResult = convertResult.substring(0,convertResult.length-1);
					ajax.send(convertResult);
				}else{
					ajax.send(data);
				}
			} else{
				ajax.send(); // 木有值 直接发送即可
			}
		}
		// 注册事件
		ajax.onreadystatechange = function () {
			// 在事件中 获取数据 并修改界面显示
			if (ajax.readyState == 4){
				if(ajax.status===200){ // 返回值: ajax.responseText;
					if(ajax.response && typeof ajax.response !== 'object'){
						resolve(JSON.parse(ajax.response));							
					}else{
						resolve(ajax.response);
					}
				}else{
					reject(ajax.status);
				}
			}
		}
	});		
}

JSONP

原理

<body>
    <div id="result"></div>
    <script>
        //处理数据
        function handle(data) {
            //获取 result 元素
            const result = document.getElementById('result');
            result.innerHTML = data.name;
        }
    </script>
    <!-- <script src="http://127.0.0.1:5500/%E8%AF%BE%E5%A0%82/%E4%BB%A3%E7%A0%81/7-%E8%B7%A8%E5%9F%9F/2-JSONP/js/app.js"></script> -->
    <script src="http://127.0.0.1:8000/jsonp-server"></script>
<!--上面script中的链接向服务器发送请求 需要返回的是函数调用 即js代码 前端浏览器引擎才能解析和执行里面的内容-->
</body>

实践

<body>
    用户名: <input type="text" id="username">
    <p></p>
    <script>
        //获取 input 元素
        const input = document.querySelector('input');
        const p = document.querySelector('p');
        
        //声明 handle 函数
        function handle(data){
            input.style.border = "solid 1px #f00";
            //修改 p 标签的提示文本
            p.innerHTML = data.msg;
        }

        //绑定事件
        input.onblur = function(){
            //获取用户的输入值
            let username = this.value;
            //向服务器端发送请求 检测用户名是否存在
            //1. 创建 script 标签
            const script = document.createElement('script');
            //2. 设置标签的 src 属性
            script.src = 'http://127.0.0.1:8000/check-username';
            //3. 将 script 插入到文档中
            document.body.appendChild(script);
        }
    </script>
</body>

面试常考题

Ajax

Ajax:一种异步请求数据的web开发技术。在不需要重新刷新页面的情况下,Ajax 通过异步请求加载后台数据,并在网页上呈现出来。

目的

1.提高用户体验
2.较少网络数据的传输量
3.由于AJAX请求获取的是数据而不是HTML文档,因此它也节省了网络带宽,让用户的体验变得更加顺畅

运用场景

表单验证是否登入成功、百度搜索下拉框提示、快递单号查询

工作原理

相当于在用户和服务器之间加了—个中间层(AJAX引擎),使用户操作与服务器响应异步化。并不是所有的用户请求都提交给服务器,像—些数据验证和数据处理等都交给Ajax引擎自己来做, 只有确定需要从服务器读取新数据时再由Ajax引擎代为向服务器提交请求。Ajax其核心有JavaScript、XMLHTTPRequest、DOM对象组成,通过XmlHttpRequest对象来向服务器发异步请求,从服务器获得数据,然后用JavaScript来操作DOM而更新页面

readyState

readyState:是XMLHttpRequest对象的一个属性,用来标识当前XMLHttpRequest对象处于什么状态。
0:初始化,XMLHttpRequest对象还没有完成初始化
1:载入,XMLHttpRequest对象开始发送请求
2:载入完成,XMLHttpRequest对象的请求发送完成
3:解析,XMLHttpRequest对象开始读取服务器的响应
4:完成,XMLHttpRequest对象读取服务器响应结束

你可能感兴趣的:(前端,ajax,javascript)