大家好,自從開賽已過了一周了,首先給自己一點掌聲,耶呼,大家一起繼續努力吧!
第一周主要聊些SPA的基本概念,今天我們來點有趣的,直接用簡單的範例來試做SPA。延續前兩天的內容,今天我們先用history方式來示範:
首先開一個HTML檔案,做個ul選單置於body,並用超連結與li來做選單連結項目:
<ul class="nav">
<li><a href="Home">主頁</a></li>
<li><a href="About">關於</a></li>
<li><a href="Contact">聯繫</a></li>
</ul>
接著在body裡的選單下方,放入render後內容的容器:
<section id="wrapper"></section>
這樣畫面的部分就算準備完畢了。
UI準備完畢後就可以開始寫程式了,首先在body結束前面先引入JQuery的cdn,後續在使用監聽事件實作上比較容易:
<script src="https://code.jquery.com/jquery-3.5.1.slim.js" integrity="sha256-DrT5NfxfbHvMHux31Lkhxg42LY6of8TaYyK50jnxRnM=" crossorigin="anonymous"></script>
接著我們需要建立一個簡單的函式,用來判斷目前路徑及要產生的內容:
//綁定渲染元素
const wrapper = document.querySelector("#wrapper");
//判斷目前頁面
function renderByUrl(url) {
console.log("目前路徑為:", url);
if (url === "/" || url === "/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "/Contact") {
wrapper.innerHTML = "這是聯繫頁";
}
}
最後就是用JQuery來監聽click在選單連結事件(相較addEventListener,JQuery多個連結的click監聽比較容易):
//監聽網址改變事件
$(".nav a").on("click", function (e) {
//取消超連結預設行為
e.preventDefault();
//改變網址
history.pushState(null, null, $(this).attr("href"));
//使用location.pathname判斷頁面內容
renderByUrl(location.pathname);
});
這邊需要注意一點,在上面選單按下連結後,會去請求超連結的路徑,除了會重新發送請求,因遠端沒有檔案會出現找不到的錯誤畫面,所以我們需要用preventDefault這個停止事件,取消超連結導向的行為。
緊接著我們來看看成果,範例為使用VS Code在本地端開啟live server,並在網址列去掉了index.html,讓這邊看起來就像一般的網址:
可以看到在點擊連結時,網址跟內容都產生變化了,console裡的紀錄也是完美輸出,But….一開始載入畫面似乎少了什麼...
一開始載入頁面的內容預設應該要是主頁,因為一般情況user不會在路徑後輸入index.html,我們希望一開始預設頁面等於主頁的內容。所以這邊我們對window增加監聽事件,用來觸發判斷頁面的函式,script裡繼續加入以下程式碼:
//監聽載入後的頁面
window.addEventListener("load", function () {
//這時location.pathname為'/'
renderByUrl(location.pathname);
});
載入時可以順利看到主頁了,但在切換上/下頁時好像哪裡怪怪的...
呃...內容竟然沒有跟著變化,這樣感覺有點瞎,所以這時也需要加入監聽歷史紀錄變化的事件,裡面一樣放入判斷頁面的函式:
//監聽歷史紀錄變化
window.addEventListener("popstate", function (e) {
//使用location.pathname判斷頁面內容
renderByUrl(location.pathname);
});
沒問題,除了可以去指定頁面,在切換上下頁時也能正常運作。一切看似順利,但當我們想在網址列輸入/About直接前往關於頁面時,卻回傳了錯誤的畫面:
在經過仔細看了Network紀錄發現,找不到畫面是因為瀏覽器向伺服器請求,去尋找/About這個路徑,理論上這會去找該路徑下的index.html,但因為不存在這個檔案,所以也返回了錯誤畫面,這情況該怎麼解呢?就是在server端設定路徑導向,不管如何都只重新導向到主路徑下的index.html,這樣就能確保讀取到的是SPA的頁面,並且能夠正確找出對應的畫面。
今天我們主要使用history變化的方式,透過實際的範例讓各位了解這部份SPA是如何運作的。下一篇我們將來實做使用location.hash做出SPA的效果,明天見!
今日範例完整程式碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Day08 | Simple SPA with history</title>
</head>
<body>
<ul class="nav">
<li><a href="Home">主頁</a></li>
<li><a href="About">關於</a></li>
<li><a href="Contact">聯繫</a></li>
</ul>
<section id="wrapper"></section>
<script
src="https://code.jquery.com/jquery-3.5.1.slim.js"
integrity="sha256-DrT5NfxfbHvMHux31Lkhxg42LY6of8TaYyK50jnxRnM="
crossorigin="anonymous"
></script>
<script>
//綁定渲染元素
const wrapper = document.querySelector("#wrapper");
//判斷目前頁面
function renderByUrl(url) {
console.log("目前路徑為:", url);
if (url === "/" || url === "/Home") {
wrapper.innerHTML = "這是主頁";
} else if (url === "/About") {
wrapper.innerHTML = "這是關於頁";
} else if (url === "/Contact") {
wrapper.innerHTML = "這是聯繫頁";
}
}
//監聽網址改變事件
$(".nav a").on("click", function (e) {
//取消超連結預設行為
e.preventDefault();
//改變網址
history.pushState(null, null, $(this).attr("href"));
//使用location.pathname判斷頁面內容
renderByUrl(location.pathname);
});
//監聽載入後的頁面
window.addEventListener("load", function () {
//這時location.pathname為'/'
renderByUrl(location.pathname);
});
//監聽歷史紀錄變化
window.addEventListener("popstate", function () {
//使用location.pathname判斷頁面內容
renderByUrl(location.pathname);
});
</script>
</body>
</html>