JS 重構購物車案例 理解AJAX前後端傳輸資料的解碼編碼以及AJAX事件(node做後端)

thomas_001發表於2022-01-26

AJAX簡介

首先我們來看w3c給的示例圖

JS 重構購物車案例 理解AJAX前後端傳輸資料的解碼編碼以及AJAX事件(node做後端)

在我工作中,用的最多的也是用來傳輸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", "http://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 ) );

        }

那我們的列印出來的就是這樣的

JS 重構購物車案例 理解AJAX前後端傳輸資料的解碼編碼以及AJAX事件(node做後端)

這種通常用在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", "http://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", "http://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>

 

 

相關文章