iT邦幫忙

第 12 屆 iThome 鐵人賽

DAY 9
0
自我挑戰組

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

[DAY09]小試身手-使用hash做出SPA

大家好,昨天我們用history API來實做簡單的SPA,今天我們要來實做第二種方式,使用location.hash做出SPA。

準備畫面

跟昨天模式一樣,先建立一個HTML後,在body放入一樣的UI:

<ul>
  <li><a href="#/Home">主頁</a></li>
  <li><a href="#/About">關於</a></li>
  <li><a href="#/Contact">聯繫</a></li>
</ul>

<section id="wrapper"></section>

首先說明一下,這邊的超連結屬性因為放入的僅有hash部份,在hash後面多接了斜線與路徑名稱,我們在 [DAY07]SPA路由的應用(下篇) 提到,如此除了可以改變hash值,也不會觸發重新導向請求,所以不需跟昨天一樣,使用監聽點擊事件把超連結預設行為取消。

接著我們來準備script的內容,一樣需要一個判斷路徑對應內容的函式,用來實現Controller與View的功能:

function renderByUrl(url) {
    console.log("目前路徑為:", url);
    //路徑對應頁面內容
    if (url === "#/" || url === "#/Home") {
        wrapper.innerHTML = "這是主頁";
    } else if (url === "#/About") {
        wrapper.innerHTML = "這是關於頁";
    } else if (url === "#/Contact") {
        wrapper.innerHTML = "這是聯繫頁";
    } else {
        wrapper.innerHTML = "找不到頁面";
    }
}

函式預設url參數我們將會帶入loaction.hash的值,loaction.hash比較特別的是,hash後面有值才會連同#輸出,若是空值或僅有一個#,則僅會返回空值。接著我們需要頁面加載完畢時,判斷目前的hash值來決定內容,所以這邊來加入監聽事件:

//載入事件
window.addEventListener("load", function () {
    //使用location.hash判斷頁面內容
    renderByUrl(location.hash);
});

最後的是在hash值變化時可以載入對應的內容,這邊事件類型使用hashchange,加入監聽事件:

//監聽網址改變事件
window.addEventListener("hashchange", function () {
    //使用location.hash判斷頁面內容
    renderByUrl(location.hash);
});

上面的程式碼完成後,我們來看看成果(記得把網址列index.html去掉比較像一般網址):

情境思考:預設畫面

跟昨天一樣的狀況,主頁預設畫面沒辦法正常顯示,因為在主路徑時沒有hash值,所以也找不到對應的內容。OK,你可能想說,那我們就在renderByUrl函式裡流程控制多加入一個條件,當hash路徑等於空值時內容為主頁,例如這樣:

//判斷目前頁面
function renderByUrl(url) {
    console.log("目前路徑為:", url);
    //路徑對應頁面內容
    if (url === "" || url === "#/" || url === "#/Home") {
        wrapper.innerHTML = "這是主頁";
    } else if (url === "#/About") {
        wrapper.innerHTML = "這是關於頁";
    } else if (url === "#/Contact") {
        wrapper.innerHTML = "這是聯繫頁";
    } else {
        wrapper.innerHTML = "找不到頁面";
    }
}

雖然情況是解決了,這樣網址列就完全沒有hash,我們希望在預設頁面的網址是#/,一開始使用者在主路徑下且沒有帶任何hash值時,就導向到#/的路徑,這樣才能正確顯示內容。所以稍微取代剛剛的方法,我們一樣在renderByUrl這個函式開頭,加入一段網址重新導向的程式,結果如下:

//判斷目前頁面並渲染
function renderByUrl(url) {
    console.log("目前路徑為:", url);
    //若沒有hash值預設導向至#/
    if (location.hash === "") {
        location.href = "#/";
        return;
    }
    //路徑對應頁面內容
    if (url === "#/" || url === "#/Home") {
        wrapper.innerHTML = "這是主頁";
    } else if (url === "#/About") {
        wrapper.innerHTML = "這是關於頁";
    } else if (url === "#/Contact") {
        wrapper.innerHTML = "這是聯繫頁";
    } else {
        wrapper.innerHTML = "找不到頁面";
    }
}

最終結果:

一切都完美運行,而且當用戶輸入對應不到的頁面也能返回自訂找不到頁面的內容,hash就是這麼直接,雖然網址多了一個#,看起來沒那麼優雅,不過效果處理得很OK,不用特別在server端做其他的設置,只有唯一個情況是,若使用者在輸入網址列時,主路徑少了hash並輸入其他對應不到的路徑時,hash就無法起到作用了,這時就還是得依靠server端的設置,統一導向到主頁,並且用剛剛的方式導至帶有hash的主頁。

小結

完成了第二個範例後,對SPA應該有比較完整的認識。恭喜你完成了SPA的試煉,得到至尊大師的稱號,可以下山回家了,以下是任務獎勵:

今日範例完整程式碼:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Day09 | Simple SPA with hash</title>
  </head>
  <body>
    <ul>
      <li><a href="#/Home">主頁</a></li>
      <li><a href="#/About">關於</a></li>
      <li><a href="#/Contact">聯繫</a></li>
    </ul>

    <section id="wrapper"></section>

    <script>
      //綁定渲染元素
      const wrapper = document.querySelector("#wrapper");

      //判斷目前頁面
      function renderByUrl(url) {
        console.log("目前路徑為:", url);
        //若沒有hash值預設導向至#/
        if (location.hash === "") {
          location.href = "#/";
          return;
        }
        //路徑對應頁面內容
        if (url === "#/" || url === "#/Home") {
          wrapper.innerHTML = "這是主頁";
        } else if (url === "#/About") {
          wrapper.innerHTML = "這是關於頁";
        } else if (url === "#/Contact") {
          wrapper.innerHTML = "這是聯繫頁";
        } else {
          wrapper.innerHTML = "找不到頁面";
        }
      }

      //載入事件
      window.addEventListener("load", function () {
        //使用location.hash判斷頁面內容
        renderByUrl(location.hash);
      });

      //監聽網址改變事件
      window.addEventListener("hashchange", function () {
        //使用location.hash判斷頁面內容
        renderByUrl(location.hash);
      });
    </script>
  </body>
</html>

…?
…??
…???

...欸等等等,看到這邊你應該會想問,實際開發SPA真的有這麼容易嗎?用這方法來寫根本就是胡鬧吧XD。其實一般專案開發不會把全部程式混和寫在同一頁,這樣既不好維護,主頁檔案也會變得很肥大。身為一個前端工程師,有個強而有力偷懶的工具在身上,也是很合理的事情。所以後續我們要來教大家使用模組化的開發方式,搭配前端開發工具與套件,完整建構出屬於自己的SPA,也是此系列正式實戰應用。


上一篇
[DAY08]小試身手-使用history做出SPA
下一篇
[DAY10]用NPM來加速前端開發
系列文
不用前端框架 手把手打造基礎SPA網站30

尚未有邦友留言

立即登入留言