JS 重构购物车案例 理解AJAX前后端传输数据的解码编码以及AJAX事件(node做后端)

AJAX简介

首先我们来看w3c给的示例图

在我工作中,用的最多的也是用来传输JSON数据,像官网介绍的这样,Ajax 允许通过与场景后面的 Web 服务器交换数据来异步更新网页。这意味着可以更新网页的部分,而不需要重新加载整个页面,这也是ajax的核心,了解以后那我们来简单的写一个AJAX的步骤

AJAX基本步骤

<script>
		let times = 0;
		let xhr = new XMLHttpRequest();
		xhr.addEventListener( "readystatechange", readyStateChangeHandler );
		xhr.addEventListener( "timeout", timeoutHandler );
		xhr.open();
		xhr.timeout = 2000;
		xhr.send();

		function readyStateChangeHandler( e ) {
			if ( this.readyState === 4 && this.states === 200 ) {
				console.log( this.response )
			}
		}

		function timeoutHandler( e ) {
			this.abort();
			times++;
			
			sendAJAX();

		}
	</script>

接下来我们一步步去理解其中的含义

我们先来用node写一个server.js作为我们的后端

server

//加载库中http.js,加载进入赋值给变量http,是一个对象
var http = require('http');
//req就是客户端向服务器端请求的数据
//res就服务器项客户端发送的数据
//http.createServer创建服务
var server = http.createServer(function (req, res) {
	//客户端请求的数据正在接收中的事件
	req.on('data', function (d) {
		console.log(d);
	});
	//客户端请求的数据接收完成的后事件
	req.on('end', function () {
		//写入头部信息,允许传输任意文本信息,并且允许任意域访问
		res.writeHead(200, { 'Content-Type': 'text/plain', 'Access-Control-Allow-Origin': '*' });
		res.write();
		res.end();
	});
});
server.listen(3002, '10.9.170.152', function () {
	console.log('服务开启,开始侦听');
});

 那么基础的步骤写好了,别急,我们一步一步来慢慢完善我们的代码

解码以及编码

我们在发送数据的时候,我们也不知道我们会遇到怎样的后端,那他这个数据到底有没有经过加码呢,同样的,我们将数据发给后端的时候我们要不要经过加码呢,然后后端接受

到我们的数据以后需要先进行解码,所以我们在不知道的情况下,我们默认就是

前端发送之前最后一步时先编码,在发送
前端接收之后第一步先做解码,再做其它内容
例如我们先看看我们发送之前的的数据
var xhr = new XMLHttpRequest();
        xhr.addEventListener( "load", loadHandler );
        var str = encodeURIComponent( "name=王大锤" );
        xhr.open( "get", "//192.168.0.103:3002?" + str );
        //    xhr.setRequestHeader("Content-Length","200");

        //        将所有的URI编码格式解码为中文
        console.log( decodeURIComponent( str ) );
        //        将所有的中文转换为URI编码格式
        console.log( encodeURIComponent( str ) );
        xhr.send();
        //        如果长时间没有响应我们需要使用abort来关闭当前的AJAX通信
        //        xhr.abort();
        function loadHandler( e ) {
            console.log( decodeURIComponent( this.response ) );

        }

那我们的打印出来的就是这样的

这种通常用在get发送的时候,

如果是get发送方式,通常在node.js服务中需要写入decodeURIComponent来做解码
var obj = JSON.parse(decodeURIComponent(data));

这里呢还有一个知识点,经常用起来的时候就忘了~~~

在JS中,通过JSON.stringify()方法,可以将JSON对象转化为JSON字符串;通过JSON.parse()方法,可以将JSON字符串转化为JSON对象

发送JSON数据

好了,那么了解了中文的解码以及编码以后,那我们拿到我们之前写的购物车那组数据,那组数据里我们是有中文的,我们尝试用post来发送数据,

我们现在有个需求,后端拿到我们数据以后,把里面的num都加+1,然后再返回给我们

<script>
		var data = [ {
				id: 1001,
				icon: "img/1.png",
				name: "餐饮0",
				num: 1,
				price: 10
			},
			{
				id: 1002,
				icon: "img/2.png",
				name: "餐饮1",
				num: 1,
				price: 20
			},
			{
				id: 1003,
				icon: "img/3.png",
				name: "餐饮2",
				num: 1,
				price: 30
			},
			{
				id: 1004,
				icon: "img/4.png",
				name: "餐饮3",
				num: 1,
				price: 40
			},
			{
				id: 1005,
				icon: "img/5.png",
				name: "餐饮4",
				num: 1,
				price: 50
			},
			{
				id: 1006,
				icon: "img/6.png",
				name: "餐饮5",
				num: 1,
				price: 60
			},
			{
				id: 1007,
				icon: "img/7.png",
				name: "餐饮6",
				num: 1,
				price: 70
			},
			{
				id: 1008,
				icon: "img/8.png",
				name: "餐饮7",
				num: 1,
				price: 80
			},
			{
				id: 1009,
				icon: "img/9.png",
				name: "餐饮8",
				num: 1,
				price: 90
			},
			{
				id: 1010,
				icon: "img/10.png",
				name: "餐饮9",
				num: 1,
				price: 100
			}
		];


		var xhr = new XMLHttpRequest();
		xhr.addEventListener( "load", loadHandler );
		xhr.open( "POST", "//192.168.0.103:3002" );
		xhr.send( encodeURIComponent( JSON.stringify( data ) ) );

		function loadHandler( e ) {
			console.log( JSON.parse( decodeURIComponent( this.response ) ) );
		}
	</script>

我们在后端接受到以后也要做处理,让我们来看一下我们的server.js

 

var http = require('http');
var server = http.createServer(function (req, res) {
	var data = '';
	req.on('data', function (d) {
		data += d;
	});
	req.on('end', function () {
		//获取请求头
		var obj = JSON.parse(decodeURIComponent(data));
		obj.map(function (t) {
			t.num++;
		});
		//writeHead()写入请求头
		res.writeHead(200, { 'Content-Type': 'text/html', 'Access-Control-Allow-Origin': '*' });
		//服务器发送数据时,最后发送时编码
		res.write(encodeURIComponent(JSON.stringify(obj)));
		res.end();
	});
});
server.listen(3002, '192.168.0.103', function () {
	console.log('开启服务');
});

 接下来我们开启服务

 

 那么我们在前端将会收到返回的数据,我们打开看看

 

 好了,我们的效果已经实现了,综上呢,我们就完成了数据传给后端,后端修改完再传给我们这样的一个过程

AJAX的事件

接下来我们深入研究下ajax当中的readystatechange事件机制以及完整的写法

那我们继续把我们之前的data数据拿过来使用一下

 

如果我们这样直接执行,会发生什么问题,我们来打开浏览器看一下

 

 

可以看到先报了一堆错,然后再去执行,我们可以看一下一共执行了4次,那么这4次过程中,我们有四种不同的情况

我们打印一下xhr看看

 

 这里显示的4,我们先不管他,因为他是个对象,对象属性会动态改变,我们刷新一下,并且打印一下,console.log(xhr.readyState)看看

 

 这里代表我们通信道哪一步了,这里看到进行到第一步了

readyState状态

 这里不得不说一下我们的readyState的五个状态

值    状态             描述

0    UNSENT            代理被创建,但是尚未调用open的方法

1    OPENED          open()方法已经被调用

2    HEADERS_RECEIVED    send()方法已经被调用,并且头部和状态已经获得

3    LOADING           下载中,responseText 属性已经包含部分数据 

4    DONE            下载操作完成

这里是五种,不是四种,最早的这种的话,我们的侦听就要放在new之前,基本没什么大用

status状态改变

我们接着打印一下xhr这个对象下面的status看看

 

 

 

 可以看到他代表了我们的状态的改变,在第一步的时候的时候他是0,下载结束以后他的状态改变为200,代表成功

常见的状态码还有常见的404,502等等

那么我们接下来完善我们的ajax

完善AJAX

接着上面的需求,也就回到了我们首文,我们继续改进这个需求,把购物车的数据返回给后端,修改num为2,然后再把数据返回给我们

这时候我们需要在下面调用ajax,所以为了防止代码重复,我们把ajax写入函数,然后调用它

这里也要用到我们的timeout,写在send之前,设定一个时间,如果过了这个时间还没有接收到,那么他就是超时 

<script>
		let data = [ {
				id: 1001,
				icon: "img/1.png",
				name: "餐饮0",
				num: 1,
				price: 10
			},
			{
				id: 1002,
				icon: "img/2.png",
				name: "餐饮1",
				num: 1,
				price: 20
			},
			{
				id: 1003,
				icon: "img/3.png",
				name: "餐饮2",
				num: 1,
				price: 30
			},
			{
				id: 1004,
				icon: "img/4.png",
				name: "餐饮3",
				num: 1,
				price: 40
			},
			{
				id: 1005,
				icon: "img/5.png",
				name: "餐饮4",
				num: 1,
				price: 50
			},
			{
				id: 1006,
				icon: "img/6.png",
				name: "餐饮5",
				num: 1,
				price: 60
			},
			{
				id: 1007,
				icon: "img/7.png",
				name: "餐饮6",
				num: 1,
				price: 70
			},
			{
				id: 1008,
				icon: "img/8.png",
				name: "餐饮7",
				num: 1,
				price: 80
			},
			{
				id: 1009,
				icon: "img/9.png",
				name: "餐饮8",
				num: 1,
				price: 90
			},
			{
				id: 1010,
				icon: "img/10.png",
				name: "餐饮9",
				num: 1,
				price: 100
			}
		];
		let times = 0;

		function sendAJAX() {
			let xhr = new XMLHttpRequest();
			xhr.addEventListener( "readystatechange", readyStateChangeHandler );
			xhr.addEventListener( "timeout", timeoutHandler );
			xhr.open( "post", "//192.168.0.103:3002" );
			xhr.timeout = 2000;
			xhr.send( encodeURIComponent( JSON.stringify( data ) ) );
		}

		function readyStateChangeHandler( e ) {

			//            console.log(xhr.readyState);
			//            console.log(xhr.status);
			if ( this.readyState === 4 && this.status === 200 ) {
				console.log( JSON.parse( decodeURIComponent( this.response ) ) );
			}
		}

		function timeoutHandler( e ) {
			//如果超时了,我们先终止请求,然后重新发起请求,下面我设置了请求三次,三次都没有回应,那我们便return出去
			this.abort();

			times++;
			if ( times > 2 ) {
				return;
			}
			sendAJAX();

		}
	</script>