我們經常會看到一些網站可以實時的向我們的頁面推送一些信息,比如網頁版的聊天、或者一些社交網站上的消息推送等等。那么怎樣才能做到呢?我提供一種方法,不一定是最優解,但能實現基本的需求。
首先我們必須說明一點:要有一些后端的知識,因為這次我們要同時寫前、后端。
我下面就以nodeJS為例,當然其他的后端實現方法也是可以的,基本原理是一樣的。
第一步:確定思路。
頁面加載后向后台發送一個Ajax請求(如果對Ajax還不了解可以到W3C上學習一下,上面有很棒的講解),作為長連接的發起。當后端收到請求后,延時處理20秒后再回應前端頁面。前台收到后端響應后就立即發起下一次請求,這樣無限循環下去。就完成了長連接的基本功能。下一步我們先實現一下,完成后我們在對它進行優化。
前端:
在你的ready()里面添加如下代碼:
function longLink(){ var data = { //'type': 'longLink' }; $.post('/longLink', { data: data, "_csrf": token }, function (result) { //console.log('long link data: ', result); longLink(); }); } longLink();
我們這里使用的是POST請求。既然我們向后台發了請求,后台哪有讓老朋友吃閉門羹的道理!
后端:
用你喜歡的方式(express或kraken等等都可以)創建nodejs工程,下面在route或controller中添加你的路由處理邏輯
1 router.post('/longLink', function (req, res) { 2 var data = req.body.data; 3 var curRes = res; 4 5 //20秒定時鏈接 並告知瀏覽器無openid 6 var longLinkTimeCtl = setTimeout(function(){ 7 curRes.send({ 8 code: 0, 9 detail:{ 10 withData: false 11 } 12 }); 13 }, 20000); 14 15 });
這樣就有了一個基本的長連接。后端在req.body中拿到前端發來的數據然后再延時響應。20秒的時間可以根據自己的需求修改,如果有對前端的通知可以夾雜在send中的對象內。
進階:
看到nodejs(后端)send中的withData:false了嗎?作為“例行公事”的響應中,當然不會有數據啦,那么僅僅是不斷循環的請求--響應,又有什么意義呢?換句話說,什么時候發送攜帶數據的響應呢?
當我們的后端收到某個數據(其他的請求或有新來的數據,總之就是一個需要后端馬上響應前端的時機)的時候,我們就要打斷原有的延時(clearTimeout),立即響應並攜帶信息。下面我們假設這一時機是一個事件監聽:
router.post('/longLink', function (req, res) { var data = req.body.data; var curRes = res; //20秒定時鏈接 並告知瀏覽器無openid var longLinkTimeCtl = setTimeout(function(){ curRes.send({ code: 0, detail:{ withData: false } }); //解綁 防止多次綁定 global.event_getData.removeListener('getData', getDataCallback); }, 20000); // 接到 獲取openid的事件后 打斷20秒定時 立即響應瀏覽器,並攜帶openid var getDataCallback = function(data){ clearTimeout(longLinkTimeCtl); if(data.xxx){ var xxx= data.xxx; curRes.send({ code: 0, detail:{ withData: true, data:{ xxx: xxx } } }); } //解綁 防止多次綁定 global.event_getData.removeListener('getData', getDataCallback); } global.event_getData.on('getData', getDataCallback); });
假設服務器的某個邏輯收到了某個數據,碰巧我們的需求中要求我們必須馬上把這個數據發送到前端頁面,就可以在拿到數據的邏輯處觸發一個全局的事件(至於nodejs中如何處理事件,我將在下一篇文章中簡單的分享一下),這里你只要知道我們可以向jquery中那樣用on()來監聽。我的例子中事件的回調函數名為getDataCallback,記住這個名字,一會我們還要解除綁定。按照getDataCallback中邏輯,我們要在接到數據后清除延時而馬上把消息send給頁面。我想細心的你一定會發現下面這句是干什么的?而且還用了兩次。
global.event_getData.removeListener('getData', getDataCallback);
由於在長連接中前端頁面一直在不斷的向后端發請求,on()的綁定也會綁定多次,我們需要解綁來保證只綁定了一個,以防多次響應。我想一定會有人問我為什么不直接在綁定之前解綁,而是在兩個出口解綁?原因是:每一次進入路由后,我們就和上一次進入的不是同一個空間了,所以在綁定之前解綁並不能達到預期的效果,回調依然會觸發多次。我們在兩個出口解綁,可以在當前的空間解綁。
當我們發送的響應中攜帶了數據而且withData: true,剩下的工作就是前端頁面了。
1 function longLink(){ 2 var data = { 3 //'type': 'longLink' 4 }; 5 $.post('/longLink', { 6 data: data, 7 "_csrf": token 8 }, function (result) { 9 //console.log('long link data: ', result); 10 longLink(); 11 if(result.code == 0){ 12 //判斷是否攜帶了信息 13 if(result.detail.withData){ 14 yyyyyyyyyyyy(result.detail.data); 15 } 16 } 17 }); 18 } 19 20 longLink();
這里yyyyyyyyyyyy是處理數據的程序。
小結:
到這里一個簡單的長連接就完成了。