單頁面路由的實(shí)現(xiàn)原理

單頁面應(yīng)用(SPA,Single Page Application)是一種web應(yīng)用程序模型鬓催,其核心特點(diǎn)在于僅加載一個(gè)HTML頁面同诫,并在用戶與應(yīng)用程序交互時(shí)尊勿,通過動(dòng)態(tài)更新該頁面內(nèi)容來響應(yīng)。

在單頁面應(yīng)用中畜侦,所有的功能與交互都在這唯一加載的HTML頁面內(nèi)完成元扔,首次加載時(shí),瀏覽器會(huì)加載必需的HTML旋膳、CSS和JavaScript文件澎语。后續(xù)的用戶操作通過JavaScript動(dòng)態(tài)地改變頁面的內(nèi)容,而不是通過重新加載整個(gè)頁面來實(shí)現(xiàn)验懊。

與傳統(tǒng)多頁面應(yīng)用(MPA擅羞,Multiple Page Application)相比,SPA在用戶操作時(shí)不會(huì)導(dǎo)致頁面的重新加載或跳轉(zhuǎn)义图,這提升了用戶體驗(yàn)并減少了網(wǎng)絡(luò)帶寬的使用减俏。同時(shí),SPA也促進(jìn)了前后端分離的開發(fā)模式碱工,其中后端主要負(fù)責(zé)數(shù)據(jù)處理和API提供娃承,而前端則負(fù)責(zé)頁面的邏輯和渲染。

單頁面應(yīng)用怕篷,通常使用hash模式历筝、history模式進(jìn)行構(gòu)建,下面簡單了解如何實(shí)現(xiàn)單頁面路由廊谓。

hash模式

hash模式梳猪,在URL中的hash(#號(hào)后面的部分),用于標(biāo)記網(wǎng)頁的不同部分或狀態(tài)蒸痹。通過hashchange監(jiān)聽事件春弥,對(duì)URL變化,進(jìn)行頁面更新叠荠。

  • hash方式更改URL
    var  path="index";
    window.localtion.hash=path;
  • 監(jiān)聽hash URL變化
  window.addEventListener('hashchange',function(e){
          //路由發(fā)生改變時(shí)執(zhí)行的事件...
   });
  • hash模式案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>前端單頁面路由</title>
    <style>
        .warp{
            width:400px;
            height:400px;
            border:1px solid grey;
            margin:0 auto;
        }
        .nav{
            border-bottom:1px solid grey;
        }
        .nav li{
            display:inline-block;
            list-style:none;
        }
        .nav li a{
            display:inline-block;
            text-decoration: none;
            padding:10px 15px;
        }
        .router{
            padding:20px;
        }
        a{
            cursor: pointer;
        }
    </style>

</head>
<body>
<section class="warp">
    <div class="nav">
        <ul>
            <li><a href="javascript:void(0)" data-path="index">首頁</a></li>
            <li><a href="javascript:void(0)" data-path="news">新聞</a></li>
            <li><a href="javascript:void(0)" data-path="about">關(guān)于</a></li>
        </ul>
    </div>
    <div id="root" class="router">
        <!-- 內(nèi)容加載區(qū)域 -->
    </div>
</section>
<script>
    (function(){
        //獲取 DOM 元素
        var aList = document.getElementsByTagName("a");
        var root = document.querySelector("#root");
        //路由配置
        var routers = [
            {path:"index",component:"首頁"},
            {path:"news",component:"新聞"},
            {path:"about",component:"關(guān)于"}
            ];
        //更改頁面視圖
        function refresh(path) {
           routers.map(item=>{
               if(path == item.path){
                   root.innerHTML = item.component;
               }
           })
        }
        //點(diǎn)擊頁面時(shí)觸發(fā)
        for(let i=0;i<aList.length;i++){
            aList[i].addEventListener('click',function (e) {
                var path = this["attributes"]["data-path"].value;
                window.location.hash = "/"+path;
                refresh(path);
            });
        }
        window.addEventListener('hashchange',function(e){
            var oldPath = e.oldURL.split("#/")[1];
            var newPath = e.newURL.split("#/")[1];
            //hash值不一致時(shí)更新視圖
            if(oldPath != newPath){
                refresh(newPath);
            }
        });
        //瀏覽器刷新時(shí)觸發(fā)
        window.addEventListener('load',function(){
            var path = "index";
            if(location.hash){
                path=location.hash.slice(2);
            }
            refresh(path);
        });
    })()
</script>
</body>
</html>
  • history模式

history 是 HTML5 提供的新特性匿沛,允許開發(fā)者直接更改前端路由,也就是更改 url 地址而無需向后端發(fā)送 http 請(qǐng)求蝙叛。
因此可對(duì) history 對(duì)象改變URL時(shí)俺祠,并通過 popstate 監(jiān)聽事件,進(jìn)行瀏覽器前進(jìn)后退時(shí)視圖變化監(jiān)聽借帘。

  • history方式更改URL

history.pushState(stete,title,url);

var path = "index";
history.pushState(null,'',path);

state:一個(gè)對(duì)象蜘渣,popState 事件觸發(fā)時(shí),state 對(duì)象會(huì)傳入回調(diào)函數(shù)肺然。如無需傳參蔫缸,則設(shè)置為 null 。
title:新頁面的標(biāo)題际起,但是所有瀏覽器目前都忽略這個(gè)值拾碌,因此可以設(shè)置為空字符串 "" 或者 null 吐葱。
url:新的網(wǎng)址地址,必須與當(dāng)前頁面處于同一個(gè)域下校翔,瀏覽器的地址欄將顯示這個(gè)網(wǎng)址弟跑。

  • 監(jiān)聽 history變化
window.addEventListener('popstate',function(e){
    //瀏覽器前進(jìn)后退時(shí)執(zhí)行的事件...
 });;
  • history模式案例
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>前端單頁面路由</title>
    <style>
        .warp{
            width:400px;
            height:400px;
            border:1px solid grey;
            margin:0 auto;
        }
        .nav{
            border-bottom:1px solid grey;
        }
        .nav li{
            display:inline-block;
            list-style:none;
        }
        .nav li a{
            display:inline-block;
            text-decoration: none;
            padding:10px 15px;
        }
        .router{
            padding:20px;
        }
        a{
            cursor: pointer;
        }
    </style>

</head>
<body>
<section class="warp">
    <div class="nav">
        <ul>
            <li><a href="javascript:void(0)" data-path="index">首頁</a></li>
            <li><a href="javascript:void(0)" data-path="news">新聞</a></li>
            <li><a href="javascript:void(0)" data-path="about">關(guān)于</a></li>
        </ul>
    </div>
    <div id="root" class="router">
        <!-- 內(nèi)容加載區(qū)域 -->
    </div>
</section>
<script>
    (function(){
        //獲取 DOM 元素
        var aList = document.getElementsByTagName("a");
        var root = document.querySelector("#root");
        //路由配置
        var routers = [
            {path:"index",component:"首頁"},
            {path:"news",component:"新聞"},
            {path:"about",component:"關(guān)于"}
            ];
        //更改頁面視圖
        function refresh(path) {
           routers.map(item=>{
               if(path == item.path){
                   root.innerHTML = item.component;
               }
           })
        }
        //點(diǎn)擊頁面時(shí)觸發(fā)
        for(let i=0;i<aList.length;i++){
            aList[i].addEventListener('click',function (e) {
                var path = this["attributes"]["data-path"].value;
                history.pushState({},null,'#/'+path);
                refresh(path);
            });
        }
        //瀏覽器前進(jìn)后退時(shí)觸發(fā)
        window.addEventListener('popstate',function(e){
            var path = location.hash.slice(2)||"index";
            refresh(path);
        });
        //瀏覽器刷新時(shí)觸發(fā)
        window.addEventListener('load',function(){
            var path = location.hash.slice(2)||"index";
            history.replaceState({},null,'#/'+path);
            refresh(path);
        });
    })()
</script>
</body>
</html>
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市防症,隨后出現(xiàn)的幾起案子孟辑,更是在濱河造成了極大的恐慌,老刑警劉巖蔫敲,帶你破解...
    沈念sama閱讀 218,640評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件饲嗽,死亡現(xiàn)場離奇詭異,居然都是意外死亡奈嘿,警方通過查閱死者的電腦和手機(jī)貌虾,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,254評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來裙犹,“玉大人尽狠,你說我怎么就攤上這事〔埽” “怎么了晚唇?”我有些...
    開封第一講書人閱讀 165,011評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長盗似。 經(jīng)常有香客問我,道長平项,這世上最難降的妖魔是什么赫舒? 我笑而不...
    開封第一講書人閱讀 58,755評(píng)論 1 294
  • 正文 為了忘掉前任,我火速辦了婚禮闽瓢,結(jié)果婚禮上接癌,老公的妹妹穿的比我還像新娘。我一直安慰自己扣讼,他們只是感情好缺猛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,774評(píng)論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著椭符,像睡著了一般荔燎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上销钝,一...
    開封第一講書人閱讀 51,610評(píng)論 1 305
  • 那天有咨,我揣著相機(jī)與錄音,去河邊找鬼蒸健。 笑死座享,一個(gè)胖子當(dāng)著我的面吹牛婉商,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播渣叛,決...
    沈念sama閱讀 40,352評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼丈秩,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了淳衙?” 一聲冷哼從身側(cè)響起蘑秽,我...
    開封第一講書人閱讀 39,257評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎滤祖,沒想到半個(gè)月后筷狼,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,717評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡匠童,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,894評(píng)論 3 336
  • 正文 我和宋清朗相戀三年埂材,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片汤求。...
    茶點(diǎn)故事閱讀 40,021評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡俏险,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出扬绪,到底是詐尸還是另有隱情竖独,我是刑警寧澤,帶...
    沈念sama閱讀 35,735評(píng)論 5 346
  • 正文 年R本政府宣布挤牛,位于F島的核電站莹痢,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏墓赴。R本人自食惡果不足惜竞膳,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,354評(píng)論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望诫硕。 院中可真熱鬧坦辟,春花似錦、人聲如沸章办。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,936評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽藕届。三九已至挪蹭,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間翰舌,已是汗流浹背嚣潜。 一陣腳步聲響...
    開封第一講書人閱讀 33,054評(píng)論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留椅贱,地道東北人懂算。 一個(gè)月前我還...
    沈念sama閱讀 48,224評(píng)論 3 371
  • 正文 我出身青樓只冻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親计技。 傳聞我的和親對(duì)象是個(gè)殘疾皇子喜德,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,974評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容