iT邦幫忙

第 12 屆 iT 邦幫忙鐵人賽

DAY 8
0
自我挑戰組

不用前端框架 手把手打造基礎SPA網站系列 第 8

[DAY08]小試身手-使用history做出SPA

大家好,自從開賽已過了一周了,首先給自己一點掌聲,耶呼,大家一起繼續努力吧! /images/emoticon/emoticon34.gif

第一周主要聊些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>

這樣畫面的部分就算準備完畢了。

準備Javascript

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….一開始載入畫面似乎少了什麼...

情境思考1:預設畫面

一開始載入頁面的內容預設應該要是主頁,因為一般情況user不會在路徑後輸入index.html,我們希望一開始預設頁面等於主頁的內容。所以這邊我們對window增加監聽事件,用來觸發判斷頁面的函式,script裡繼續加入以下程式碼:

//監聽載入後的頁面
window.addEventListener("load", function () {
    //這時location.pathname為'/'
    renderByUrl(location.pathname);
});

載入時可以順利看到主頁了,但在切換上/下頁時好像哪裡怪怪的...

情境思考2:切換上/下頁

呃...內容竟然沒有跟著變化,這樣感覺有點瞎,所以這時也需要加入監聽歷史紀錄變化的事件,裡面一樣放入判斷頁面的函式:

//監聽歷史紀錄變化
window.addEventListener("popstate", function (e) {
    //使用location.pathname判斷頁面內容
    renderByUrl(location.pathname);
});

沒問題,除了可以去指定頁面,在切換上下頁時也能正常運作。一切看似順利,但當我們想在網址列輸入/About直接前往關於頁面時,卻回傳了錯誤的畫面:

情境思考3:透過網址列尋找對應頁面

在經過仔細看了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>

上一篇
[DAY07]SPA路由的應用(下篇)
下一篇
[DAY09]小試身手-使用hash做出SPA
系列文
不用前端框架 手把手打造基礎SPA網站30

尚未有邦友留言

立即登入留言