講了這麼多後端的東西,我們現在又要暫時跳回來講一下前端了。之前有說過,每次你要造訪一個新的頁面,都是瀏覽器發一個 request 給 server,server 回傳 response 之後再由瀏覽器 render 出來。所以每個頁面幾乎都是一個不同的檔案,你有可以很明顯地看到瀏覽器跳頁的動作。
可是,你有沒有發現你在用 Gmail 的時候,你沒有感覺過「跳頁」?好像所有的事情都是在同一頁上面發生的,你不會看到你點了某封信以後,畫面就突然全部變白色開始 loading。
這是因為,你點了某封信以後,儘管瀏覽器的確載入了新資料,但卻沒有跳到另外一個新的頁面。這樣的技術,就叫做 AJAX,全名是:Asynchronous JavaScript and XML,非同步的 JavaScript 與 XML。
總之呢,ajax 的應用隨處可見,只要是可以拿到新資料但又沒有跳頁的,你就可以視為是 ajax。例如說你在 Google 搜尋的時候,他下面不是會跳出一堆的建議關鍵字嗎。而且你每打一個字就會跟著變。那是因為你每次打字的時候,都會利用 ajax 送一個 request 到後端,後端再傳回 Google 建議的搜尋關鍵字清單,前端做處理以後再顯示出來。所以這期間其實你一直在跟 Google 的後端做溝通,只是你不知道而已。
那為什麼我們需要 ajax?很間單嘛,就是使用者體驗可以好很多。假設你現在身處於一個沒有 ajax 的年代,你想想會發生什麼事?你在 Google 搜尋的時候就沒有建議關鍵字可以看;你在申請帳號的時候,你必須按下「送出」,他才會告訴你這個帳號有人用了;或者是你用 Gmail,每點進一封信都要切換一次頁面,載入全部的東西。
所以 ajax 的重點就是能在不切換頁面的前提下,發送 request 並且得到 response。我們現在就來把之前完成的留言板改成用 ajax 吧!使用者體驗會好很多。
要用 ajax 的話,最方便的就是用 jQuery 所提供的$.ajax
這個方法。(如果你想知道不靠 jQuery 的方法,可參考w3schools)
在要把前端改成 ajax 之前,你的後端也必須跟著調整。例如說我們可以先來試試看把刪除文章改成用 ajax,後端的 response 則要輸出成功或者是失敗:
// 刪除文章
app.get('/posts/delete/:id', function (req, res) {
var id = req.params.id;
db.deletePost(id, function (err) {
if (err) {
// 失敗
res.send({
status: 'FAILURE',
err: err
});
} else {
// 成功後輸出成功
res.send({
status: 'SUCCESS'
});
}
})
})
這樣子前端就可以根據 server 回傳的 response 判斷有沒有刪除成功。原本我們的「刪除」按鈕是做成一個超連結直接連去刪除的網頁,現在改用 ajax 之後,就要自己動手寫事件了。
<!DOCTYPE html>
<html>
<head>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" />
<script src="https://code.jquery.com/jquery-1.12.4.min.js"></script>
<script>
$(document).on('click', '.btn-delete' ,function() {
var element = $(this);
var id = element.attr('data-id');
$.ajax({
url: '/posts/delete/' + id,
success: function(response) {
if (response.status === 'SUCCESS') {
// 自己刪除這篇文章
element.parents('.panel').fadeOut();
} else {
alert('刪除失敗');
}
},
error: function(err) {
console.log(err);
alert('刪除失敗');
}
})
})
</script>
</head>
<body>
<div class="container">
<% if (username) { %>
<a class="btn btn-default" href="/logout">登出</a>
<% } else { %>
<a class="btn btn-default" href="/login">登入</a>
<% } %>
<a class="btn btn-primary" href="/posts">發表新留言</a>
<h2>留言列表</h2>
<div>
<% for(var i=0; i<posts.length; i++) { %>
<div class="panel panel-default">
<div class="panel-heading">
<h3 class="panel-title"><%= posts[i].author %>, 發佈時間:<%= posts[i].createTime %></h3>
</div>
<div class="panel-body">
<%= posts[i].content %>
<% if (isAdmin) { %>
<a class="btn-delete" data-id="<%= posts[i]._id %>">刪除</a>
<% } %>
</div>
</div>
<% } %>
</div>
</div>
</body>
</html>
這邊可以看到我們更改了兩個部分,第一個是加上了一段 JavaScript 的程式碼,第二個是原本的刪除按鈕換了,我們自訂了一個data-id
的屬性來存說到底我們要刪除的是哪一個文章。
JavaScript 的那一段程式碼就是在監聽任何.btn-delete
的 click 事件,當被點擊的時候先把 id 抓出來,再用 ajax 發送一個 request 給 server 跟他說我要刪除文章,最後再看 response 決定到底是成功還是失敗。記得成功之後要自己手動寫程式把元素給移除掉,才會有刪除的效果,不然就沒有用了。
在 Chrome dev tool 裡面的 Network tab,可以看到瀏覽器發出的每一個 request。你可以找一些網站來試試看,看你做了一些動作會有哪些 request 發出去。如果跳頁的話,就會整個被清空(除非你把 preserve logs 勾起來)。
除了刪除以外,其實你可以把任何的動作都變成是 ajax。例如說發表文章,你可以設計成按按鈕之後跳出一個視窗讓你輸入資料,按下確認之後透過 ajax 把表單送出。這樣子整個發表文章的流程就會變得很順暢。只是通常使用到 ajax 的時候,都會需要有一個 loading 的小動畫,不然使用者可能不知道他真的有送出資料,就會覺得很奇怪。
總之呢,ajax 的出現讓我們在網頁前端可以做出更好的使用者體驗,不必每一次跟伺服器交換資料就換頁一次。希望你可以試試看把發文或者是登入也改成 ajax 試試看,就當作練習吧!