Vue-Router 前端路由实现思路

140 阅读3分钟

路由

image.png

  • 路由routing)就是通过互联的网络把信息从源地址传输到目的地址的活动。路由引导分组转送,经过一些中间的节点后,到它们最后的目的地。作成硬件的话,则称为路由器。路由通常根据路由表——一个存储到各个目的地的最佳路径的表——来引导分组转送。因此为了有效率的转送分组,创建存储在路由器存储器内的路由表是非常重要的。

  • 路由就是分发请求,路由器就是分发请求的东西

image.png

代码:

index.js

const number = window.location.hash.substr(1)
//获取用户想去哪里
const div = document.querySelector(`#div${number}`)
const app = document.querySelector("#app")
//获取界面
div.style.display = "block"
//渲染界面
app.appendChild(div)
//展示界面

如何切换页面(hash)

  • 页面跳转的过程中,检测到路由的变化,并将相应的内容渲染到页面中

  • 如何检测路由的变化?

window.location.hash

通过window.location.hash 可以获得路由地址,获得的路由地址会自带一个“#”

window.addEventListener("hashchange", () => {
  console.log("hash 变了")
})

然后我们只需要监听 hashchange 事件,就可以检测路由的变化

默认路由

  • 如果#1,#2,#3,#4都不写,就会报错,就给默认值:number = number || 1(如果number存在,number就等于number,如果不存在,number就等于1)

404路由/保底路由

  • 如果什么输入的什么都不是,我就返回404的页面;或者不写,我就返回我默认的页面

index.html:

<body>
  <div id="app"></div>
  <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="div1" style="display:none">1</div>
  <div id="div2" style="display:none">2</div>
  <div id="div3" style="display:none">3</div>
  <div id="div4" style="display:none">4</div>
  <div id="div404" style="display:none">
    你要找的网页不存在
  </div>
  <script src="src/index.js"></script>
</body>

index.js:

function route(){
  let number = window.location.hash.substr(1)
  let app = document.querySelector('#app')
  number = number || 1
  let div = document.querySelector(`#div${number}`)
  if(!div) {
    div = document.querySelector("#div404")
}
  div.style.display = "block"
  if(app.children.length > 0){
    app.children[0].style.display = "none"
    document.body.appendChild(app.children[0])
}
    app.appendChild(div)
}
route()
window.addEventListener("hashchange", ()=>{
  console.log('hash 变了')
  route()
})

嵌套路由

image.png

image.png

路由表

获得 window.location.hash 的值以后,怎么将他跳转到相应页面呢,那就需要一个 hash 页面的映射表了,也就是路由表 (表驱动编程的体现)

const app = document.querySelector('#app')
const div1 = document.createElement("div")
div1.innerHTML = "1"
const div2 = document.createElement("div")
div1.innerHTML = "2"
const div3 = document.createElement("div")
div1.innerHTML = "3"
const div4 = document.createElement("div")
div1.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", ()=>{
  console.log('hash 变了')
  route(app)
})
  • 这就是一个的简易 hash 前端路由了

hash模式?history模式?memory模式?

hash: 任何情况下都能做前端的路由,但是会SEO不友好(服务器收不到hash)

history:后端将所有前端路由都渲染到统一页面,除404页面,(IE8以下不支持)

memory:既不用hash,又不用history,用一个对象来存储

  • 获取number的三种方式:

hash:window.location.hash.substr(1)

history: window.location.pathname

memory: window.localStorage.getItem("xxx")

history模式

history:后端将所有前端路由都渲染到统一页面,除404页面,(IE8以下不支持)

  • hash模式请求路径时,会自带一个#,而history模式则会自带一个/

代码:

index.html

<body>
  <a class = "link" href="/1">go to 1</a>
  <a class = "link" href="/2">go to 2</a>
  <a class = "link" href="/3">go to 3</a>
  <a class = "link" href="/4">go to 4</a>
  <div id="app"></div>
  <div id="div404" style="display: none;">你要找的内容被狗吃了</div>
  <script src="src/index.js"></script>
</body>

index.js

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(href)
  })
}
route(app);
function onStateChange() {
console.log("state 变了");
route(app);
}
  • 通过 history 模式进行页面切换,会有一个问题,就是跳转的时候,同时会刷新页面,由于页面的跳转需要通过 a 标签实现,而 a 标签默认是会跳转的,我们首先需要阻止 a 标签的默认事件,并得到 href,然后使用 window.history.pushState()进行跳转。

  • history.pushState()  方法向当前浏览器会话的历史堆栈中添加一个状态(state)

  • 注意:pushState() 不会造成 hashchange (en-US) 事件调用,即使新的 URL 和之前的 URL 只是锚的数据不同。

memory模式

memory:既不用hash,又不用history,用一个对象来存储

index.js

function route(container) {
  let number = window.localStorage.getItem("xxx");
  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.getItem("xxx")
    //通知
    onStateChange(href)
  })
}
route(app);
function onStateChange() {
  console.log("state 变了");
  route(app);
}
  • 把路径存在用户看不见的地方,这种模式适合非浏览器。

总结: hash模式和history模式是通过URL来存储路径的,而memory模式是非URL,前端一般放在LocalStorage里面,移动端放在本地的数据库里面。memory模式的缺点是没有URL,分享给其他人之后,进入都会是初始状态,所以只对单机有效。