使用AJAX技術發送異步請求
什么是AJAX
AJAX指一步Javascript和XML(Asynchronous JavaScript And XML),它是一些列技術的組合,簡單來說AJAX基於XMLHttpRequest讓我們在不重載頁面的情況下和服務器進行數據交換。
加上JavaScript和DOM(Document Object Model,文檔對象模型),我們就可以在接收到響應數據后局部更新頁面。XML指的是數據的交互模式,可以是純文本(Plain Text)、HTML或JSON。
使用jQuery發送AJAX請求
jQuery是流行的JavaScript庫,它包裝了JavaScript,讓我們通過更簡單的方式編寫JavaScript代碼。對於AJAX,它提供了多個相關的方法,使用它可以很方便地實現AJAX操作。更重要的是,jQuery處理了不同瀏覽器的AJAX兼容問題,我們只需要編寫一套代碼,就可以在所有主流瀏覽器上運行。
在示例中,使用全局jQuery函數ajax()發送AJAX請求。ajax()函數是底層函數,有豐富的自定義配置,支持的主要參數如下:
jQuery還提供了快捷方法:用戶發送GET請求的get()方法和用於發送POST請求的post()方法,還有直接用於獲取json數據的getjson()以及獲取腳本的getscript()方法。這些方法都是基於ajax()方法實現的。
返回“局部數據”
對於處理AJAX請求的視圖函數來說,不會返回完整的HTML響應,這時一般會返回局部數據,常見的類型有純文本或局部HTML模板、JSON數據或者空值
下面程序對應的頁面中,我們顯示一片很長的虛擬文章,文章正下方有一個”Load More”的按鈕,當按鈕被單擊時,會發送一個AJAX請求獲取文章的更多內容並直接動態插入到文章下方。
用來顯示虛擬文章視圖是show_post
文章的隨機正文通過Jinja2提供的generate_Jorem_ipsum()函數生成,n參數用來指定段落的數量,默認為5,它會返回由隨機字符組成的虛擬文章。文章下面添加了一個”Load More”按鈕。按鈕下面是兩個<script></script>代碼塊,第一個script是從CDN加載jQuery資源。
在第二個script標簽中,我們在代碼的最外層創建了一個$(function(){…})函數,這個函數是常見的$(document).ready(function(){…})函數的簡寫形式。這個函數用來在頁面DOM加載完畢后執行代碼,類似傳統JavaScript中的window.onload方法,所以我們通常會將代碼包裝在這個函數中。美元符號是jQuery的簡寫,我們通過它來調用jQuery提供的多個方法,所以$.ajax()等同於jQuery.ajax()。
在$(function(){…})中,$(‘#load’)被稱為選擇器,我們在括號中傳入目標元素的id、class或是其他屬性來定位對應的元素,將其創建為jQuery對象。我們傳入了”Load More”按鈕的id值以定位到加載按鈕。在這個選擇器上,我們附加了.click(function(){…}),這會為加載按鈕注冊一個單擊事件處理函數,當加載按鈕被單擊時就會執行單擊事件回調函數。在這個回調函數中,我們使用$.ajax()方法發送一個AJAX請求到服務器,通過url將目標URL設為”/more”,通過type參數將請求的類型設為GET。當請求成功處理並返回2xx響應或304響應,會觸發success回調函數。success回調函數接收的第一個參數為服務器端返回的響應主體,在這個回調函數中,我們在文章正文(通過$(‘.body’)選擇)底部使用append()方法插入返回的data數據。
from jinja2.utils import generate_lorem_ipsum
@app.route('/post')
def show_post():
post_body = generate_lorem_ipsum(n=2)#生成兩段隨機文本
return u'''
<h1>A very long post</h1>
<div class="body">%s</div>
<button id="load"> Load More</button>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script type="text/javascript">
$(function() {
$('#load').click(function() {
$.ajax({
url: '/more', // 目標URL
type: 'get', // 請求方法
success: function(data){ // 返回2XX響應后觸發的回調函數
$('.body').append(data); // 將返回的響應插入到頁面中
}
})
})
})
</script>''' %post_body
@app.route('/more')
def load_post():
return generate_lorem_ipsum(n=1)
if __name__ == '__main__':
app.run(debug = True)
結果:
訪問127.0.0.1:5000/post頁面
點擊Load More按鈕,瀏覽器會在后台發送一個GET請求到/more,這個視圖返回的隨機字符會被動態的插入到文章下方
可以看到頁面沒有刷新,只是在底部多顯示了一段內容
再次點擊按鈕
HTTP服務器端推送
不論是傳統的HTTP請求-響應式的通信模式,還是一部的AJAX式請求,服務器端始終處於被動的應答狀態,只有在客戶端發出請求的情況下,服務器端才會返回響應。這種通信模式被稱為客戶端拉取。
在某些場景下,需要的通信模式是服務器端的主動推送。比如一個聊天室有很多用戶,當某個用戶發送消息后,服務器接收到這個請求,然后把消息推送給聊天室的所有用戶。
類似這種關注實時性的情況還有很多,比如社交網站的導航欄實時顯示新提醒和私信的數量,用戶的在線狀態更新,股價行情監控、顯示商品庫存信息、多人游戲、文檔協作等。
實施服務器桂松的一些列技術被合稱為HTTP Server push(http服務器端推送),目前常用的推送技術如下:
輪詢(polling)這類使用AJAX技術模擬服務器端推送的方法實現起來比較簡單,但通常會造成服務器資源上的浪費,增加服務器的負擔,而且會讓用戶的設備消耗更多的電量(頻繁的發起異步請求)。SSE效率更高,在瀏覽器的兼容性方面,除了windows IE/Edge,SSE(server-sent event)基本支持所有主流瀏覽器,但瀏覽器通常會限制標簽頁的鏈接數量。
除了這些推送技術,在HTML5的API中還包含了一個WebSocket協議,和HTTP不同,它是一種基於TCP協議的全雙工通信協議。和前面說的服務器端推送技術相比,WebSocket實時性更強,而且可以實現雙向通信。另外,WebSocket的瀏覽器兼容性要強於SSE。