手寫單頁面應用路由

HuangBingQuan發表於2024-11-27

hash模式

<html>
   <head>
   	<meta charset="UTF-8">
   	<title>Hash</title>
   	<style>
   		.nav_container {
   			display: grid;
   			justify-content: center;
               width: 100%;
   			height: 80px;
   			background: #000;
   			grid-template-columns: repeat(auto-fit, minmax(100px, max-content));
   			grid-template-rows: 80px;
   		}
   		.nav_container > div a {
   			display: block;
   			height: 100%;
   			color: #fff;
   			padding: 0 30px;
   			line-height: 80px;
   			cursor: pointer;
   		}
           .nav_container > div a.ac {
   			color: red !important;
   		}
           a:link {
               color:white;
           }
           a:visited {
               color:white;
           }
           a:hover {
               color:white;
           }
   	</style>
   </head>
   <body>
<!--		<div id="test">Hello</div>-->
<!--		<div style="height: 2000px"></div>-->
<!--		<div style="cursor: pointer; position: fixed; right: 0; bottom: 0;"><a href="#test">⬆️</a></div>-->
   	<div class="nav_container">
   		<div><a class="ac" href="#home">Home</a></div>
   		<div><a href="#about">About</a></div>
   		<div><a href="#contact">Contact</a></div>
   		<div><a href="#other">Other</a></div>
   	</div>
   	<div id="app"></div>
   </body>
   <script>
   	const app = document.querySelector('#app');
   	const nav = document.querySelector('.nav_container');
   	const nav_child = document.querySelectorAll('.nav_container div a')
   	function init() {
           nav.addEventListener('click', (e)=> {
               console.log(e.parentNode);
               if(e.target.nodeName === 'A') {
                   nav_child.forEach(el => {
                       el.className = '';
   				});
                   e.target.className = 'ac';
   			}
           })
           console.log(nav_child, nav);
       }
   	init();
   	const routerCompMap = new Map([
           ["#home", "<h1>home</h1>"],
   		["#about", "<h1>about</h1>"],
   		["#contact", "<h1>Contact</h1>"]
   	]);
   	function renderPage(hash) {
           const domStr = routerCompMap.get(hash) || "<h1>404</h1>";
           app.innerHTML = domStr;
           console.log("domStr", domStr);
       }

   	document.addEventListener('DOMContentLoaded', ()=> {
   		window.addEventListener('hashchange', ()=> {
               renderPage(window.location.hash);
   		});
           renderPage(window.location.hash || '#home');
   	})
   </script>
</html>

history模式

<html>
	<head>
		<meta charset="UTF-8">
		<title>history</title>
		<style>
            .nav_container {
                display: grid;
                justify-content: center;
                width: 100%;
                height: 80px;
                background: #000;
                grid-template-columns: repeat(auto-fit, minmax(100px, max-content));
                grid-template-rows: 80px;
            }
            .nav_container > div a {
                display: block;
                height: 100%;
                color: #fff;
                padding: 0 30px;
                line-height: 80px;
                cursor: pointer;
            }
            .nav_container > div a.ac {
                color: red !important;
            }
            a:link {
                color:white;
            }
            a:visited {
                color:white;
            }
            a:hover {
                color:white;
            }
			a[data-link] {
                font-size: 30px;
			}
		</style>
	</head>
	<body>
		<div class="nav_container">
			<div><a class="ac" href="/home" data-link>Home</a></div>
			<div><a href="/about" data-link>About</a></div>
			<div><a href="/contact" data-link>Contact</a></div>
			<div><a href="/other" data-link>Other</a></div>
		</div>
		<div id="app"></div>
		<script>
			let app = document.querySelector('#app');
            const routerCompMap = new Map([
                ["/home", "<h1>home</h1>"],
                ["/about", "<h1>about</h1>"],
                ["/contact", "<h1>Contact</h1>"]
            ]);
            function renderPage(hash) {
                const domStr = routerCompMap.get(hash) || "<h1>404</h1>";
                app.innerHTML = domStr;
                console.log("domStr", domStr);
            }
			document.addEventListener('DOMContentLoaded', ()=> {
                renderPage('/home');
                document.querySelectorAll('a[data-link]').forEach(el => {
                   el.addEventListener('click', (e)=> {
                       e.preventDefault();
                       let path = e.target.getAttribute('href');
                       history.pushState(null, null, path);
                       renderPage(path);
                       console.log(path);
                   })
			   })
			});
		</script>
	</body>
</html>

hash和history模式的區別

  1. 外觀:hash的url有個#符號,history沒有,history外觀更好看。
  2. 重新整理:hash重新整理會載入到位址列對應的頁面,history重新整理瀏覽器會重新發起請求,如果服務端沒有匹配當前的url,就會出現404頁面。
  3. 相容性:hash能相容到IE8,history只能相容到IE10。
  4. 服務端支援:hash(#及後面的內容)的變化不會導致瀏覽器向伺服器發起請求;history重新整理會重新向伺服器發起請求,需要在服務端做處理:如果沒有匹配到
  5. 原理:hash透過監聽瀏覽器的onhashchange()事件,查詢對應的路由規則;history利用H5新增的pushState()和replaceState()方法改變url。
  6. 記錄:hash模式只有#後面的內容被修改才會新增新的記錄棧;history透過pushState()設定的url於當前url一模一樣也會被記錄到歷史記錄棧。

相關文章