前端路由 1.路由是什么 1.1 路由的概念 路由就是“分发请求 ”,是将互联网的网络信息从源地址传输到目的地址活动。路由通常根据路由表 来引导分组传送。将它做成硬件的话,就称作路由器。
前端中的路由就是由URL到界面的映射。
1.2 默认路由和 404 路由
默认路由:是对IP数据包中的目的地址找不到存在的其他路由时,路由器所选择的路由。即,URL 的 hash 为空时默认选择的 hash值。
404 路由 / 保底路由:是对IP数据包中的目的地址找不到存在的映射对象时,路由器所选择的路由。即,URL的 hash 在路由表中找不到时(错误时),选择预设的 404 hash。
2.前端路由的三种实现 前两种模式利用了 window.location 接口,将路由存入URL。这个接口表示 window 对象所展示的页面的地址等信息,对于一个完整的 url,location 的 protocol,host,pathname,search ,hash 分别表示了每一部分字段。
例如,对于http://www.google.com.hk/news?s=a#page1, location.protocol = “http:” location.host = “www.google.com.hk"(包括hostname和port(80)) location.pathname = “/news” location.search = “?s=a” location.hash = “#page1”
2.1 hash 模式 hash 模式通过构建路由表,建立URL到界面之间的映射。然后通过监听 hashchange 来响应URL变化,进而切换界面。
具体地,当用户点击 a 标签时,href 发生变化进而URL变化,随即触发 hashchange 事件。
index.html
<body > <a href ="#1" > go to 1</a > <a href ="#2" > go to 2</a > <a href ="#3" > go to 3</a > <a href ="#4" > go to 4</a > <div id ="app" > </div > <div id ="div404" style ="display: none;" > 你访问的内容不存在</div > <script src ="main.js" > </script > </body >
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 const app = document .querySelector("#app" );const div1 = document .createElement("div" ); div1.innerHTML = '1' ;const div2 = document .createElement("div" ); div2.innerHTML = '2' ;const div3 = document .createElement("div" ); div3.innerHTML = '3' ;const div4 = document .createElement("div" ); div4.innerHTML = '4' ;const routeTable = { "1" : div1, "2" : div2, "3" : div3, "4" : div4 };function route (container ) { let number = window .location.hash.substr(1 ); number = number || 1 ; let div = routeTable[number.toString()]; if (!div) { div = document .querySelector("#div404" ); } div.style.display = "block" ; container.innerHTML = "" ; container.appendChild(div); } route(app);window .addEventListener("hashchange" , () => { route(app); })
hash 模式的优缺点 优点:兼容性好。任何情况下都可以使用前端路由,例如本地模式下适合使用hash模式做路由。
缺点:SEO不友好。服务器并不能接收 hash 。URL中的 hash 只是在客户端的一种状态标记,当客户端向服务器发送请求时,hash 部分不会被发送。
2.2 history 模式 当服务器将所有前端路由都渲染在同一个页面下时 ,才可以使用history模式。
history 模式先取消 a 标签的所有默认操作,然后使用 windo.history.pushState() 方法重新设置URL的 location.pathname; 最后封装 onStateChange() 方法通知浏览器重新路由。
index.html
<body > <a href ="/1" class ="link" > go to 1</a > <a href ="/2" class ="link" > go to 2</a > <a href ="/3" class ="link" > go to 3</a > <a href ="/4" class ="link" > go to 4</a > <div id ="app" > </div > <div id ="div404" style ="display: none;" > 你访问的内容不存在</div > <script src ="main.js" > </script > </body >
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 const app = document .querySelector("#app" );const div1 = document .createElement("div" ); div1.innerHTML = "1" ;const div2 = document .createElement("div" ); div2.innerHTML = "2" ;const div3 = document .createElement("div" ); div3.innerHTML = "3" ;const div4 = document .createElement("div" ); div4.innerHTML = "4" ;const routeTable = { "/1" : div1, "/2" : div2, "/3" : div3, "/4" : div4 };function route (container ) { let number = window .location.pathname; if (number === "/" ) { number = "/1" ; } let div = routeTable[number.toString()]; if (!div) { div = document .querySelector("#div404" ); } div.style.display = "block" ; container.innerHTML = "" ; container.appendChild(div); }const allA = document .querySelectorAll("a.link" );for (let a of allA) { a.addEventListener("click" , e => { e.preventDefault(); const href = a.getAttribute("href" ); window .history.pushState(null , `page ${href} ` , href); onStateChange(); }); } route(app);function onStateChange ( ) { route(app); }
history 模式的优缺点 优点:URL 美观,可设置与当前URL同源的任意 URL
缺点:IE8 一下不支持,兼容性没有 hash 模式好,对服务器端的设置有要求。单机版路由不适合使用这种模式。
2.3 memory模式 memory 模式不使用URL存储(不使用 window.location API) ,而是使用本地数据库存储。前端路由一般使用 localStorage 进行存储。
index.html
<body > <a href ="/1" class ="link" > go to 1</a > <a href ="/2" class ="link" > go to 2</a > <a href ="/3" class ="link" > go to 3</a > <a href ="/4" class ="link" > go to 4</a > <div id ="app" > </div > <div id ="div404" style ="display: none;" > 你访问的内容不存在</div > <script src ="main.js" > </script > </body >
main.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 const app = document .querySelector("#app" );const div1 = document .createElement("div" ); div1.innerHTML = "1" ;const div2 = document .createElement("div" ); div2.innerHTML = "2" ;const div3 = document .createElement("div" ); div3.innerHTML = "3" ;const div4 = document .createElement("div" ); div4.innerHTML = "4" ;const routeTable = { "/1" : div1, "/2" : div2, "/3" : div3, "/4" : div4 };function route (container ) { let number = window .localStorage.getItem("pagePath" ); if (!number) number = "/1" ; let div = routeTable[number.toString()]; if (!div) div = document .querySelector("#div404" ); div.style.display = "block" ; container.innerHTML = "" ; container.appendChild(div); }const allA = document .querySelectorAll("a.link" );for (let a of allA) { a.addEventListener("click" , e => { e.preventDefault(); const href = a.getAttribute("href" ); window .localStorage.setItem("pagePath" , href); route(app); }) } route(app);
memory 模式的优缺点 memory 模式应用场景:适合于没有使用路径的本地app。例如react native、weex
缺点:由于 memory 模式没有使用URL,因此本质上就是一个单机版路由,路由不可分享。