Vue-Router 前端路由实现的思路

简介: Vue-Router 前端路由实现的思路

Vue-Router 前端路由实现的思路


一.前端路由是什么?

1.  路由

只要满足一对多的关系就叫分发, 分别发送到各个地方。路由就是分发请求,通过网络把信息从原地址传输到目的地址,路由器就是分发请求的东西。

前端路由:

在一个HTML页面中实现与用户交互时不刷新和跳转页面的同时,为SPA中的每个视图展示匹配一个特殊的url,改变这个url且不会让浏览器像服务器发送请求,并且可以监听到url的变化。为实现这一目标,我们需要做到以下二点:

  1. 改变 url 且不让浏览器像服务器发送请求。
  2. 可以监听到 url 的变化

什么是 SPA

SPA 是 single page web application 的简称,译为单页Web应用。

简单的说 SPA 就是一个WEB项目只有一个 HTML 页面,一旦页面加载完成,SPA 不会因为用户的操作而进行页面的重新加载或跳转。 取而代之的是利用 JS 动态的变换 HTML 的内容,从而来模拟多个视图间跳转。

2.  分发

3.  路由表

自定义hash与div的关系

const div1 = document.createElement("div"); //JS创建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() {
  let number = window.location.hash.substr(1);
  let app = document.querySelector("#app");
  number = number || 1;
  let div = routeTable[number.toString()]; //得到'1'
  if (!div) {
    div = document.querySelector("#div404");
  }
  div.style.display = "block";
  app.innerHTML = ""; //置空
  app.appendChild(div);
}
route();
window.addEventListener("hashchange", () => {
  console.log("hash变了");
  route();
});

4.  默认路由

number = number || 1 //如果number不存在就给个保底址1

5.  404 路由 / 保底路由

if(div){
  div.style.display="block"
}else{
  div=document.querySelector("#div404")
  div.style.display="block"
}
```J
### 6.  嵌套路由
# 二.hash模式? history模式? memory模式?
## 1.hash
### Js实现
[代码片段](https://code.juejin.cn/pen/7144916568755929118)
**任何情况下你都可以用hash做前端路由,但是SEO不友好。**
**缺点:SEO不友好。**  服务器收不到hash,因为浏览器是不会把#之后的内容发送给服务器。所以不管你是访问`https://www.baidu.com/#1`还是`https://www.baidu.com/#frank`对百度来说都是同一个内容,永远只展示默认路由`https://www.baidu.com`。根本没法收录你的内容,这就叫SEO不友好。 但也不是绝对的,google的**hashbang**可以让google能识别#,需要你的服务器做些配置:加`!`,这样google就会认为是不同的页面。例如`https://www.baidu.com/#!1`
```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.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);
});

2.history

Js实现

<nav class="hi">
  <a class="link" href="/">home</a>
  <a class="link" href="/a">a</a>
  <a class="link" href="/c">c</a>
</nav>
<section id="home">
  <h1>home</h1>
  <p>This is home page</p>
</section>
<section id="a">
  <h1>a</h1>
  <p>This is a page</p>
</section>
<section id="default">
  <h1>404</h1>
  <p>404</p>
</section>
<script>
</script>
.hi{
  display: flex;
  justify-content: center;
}
a{
  width: 100px;
  border: 1px solid black;
  padding: 5px;
  left:50%;
}
 class Router {
    constructor({ mode, routes }) {
      this.routes = routes
      window.onpopstate = () => this.setPage()
      document.querySelectorAll('.link').forEach(node => {
        node.addEventListener('click', (e) => {
          this.setPage(node.pathname)
          e.preventDefault()
        })
      })
      this.setPage()
    }
    setPage(path = '/') {
      history.pushState({}, "", path)
      this.routes.forEach(route => route.component.style.display = "none")
      let route = this.routes.find(route => route.path === path) || this.routes[this.routes.length - 1]
      route.component.style.display = "block"
    }
  }
  new Router({
    mode: 'history',
    routes: [
      {
        path: '/',
        component: document.querySelector('#home')
      },
      {
        path: '/a',
        component: document.querySelector('#a')
      },
      {
        component: document.querySelector('#default')
      },
    ]
  })

image.png

条件1.后端将所有前端路由都渲染同一个页面(不能是404)

条件2.不需要兼容IE8以下。

只要后端能实现需求:不管你请求https://www.baidu.com/#1还是https://www.baidu.com/#frank都得到同一个页面https://www.baidu.com,而且不需要兼容IE8以下,那你就可以使用history模式。

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;
    console.log("number: " + number);
    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);
    }

细节

1.每次点都要重新渲染,体验感很差,于是浏览器出了一个新的APIhistory

2.如果是通过document.querySelectorAll得到的allA是不能直接map的,可以用这种方法for(let a of allA){}遍历。

3.在不刷新页面的情况下,改URL

history mdn

用法
var stateObj = { foo: "bar" };
history.pushState(stateObj, "", "bar.html");
项目实例
const href = a.getAttribute("href");//每次点击时获取href值
window.history.pushState(null, `page ${href}`, href);//改URL

4.监听state变化

点击链接时跳转,怎么监听变化了呢?

在push时进行通知:就是调用一个函数onStateChange(href) 知道它变了后就可以render了。

5.history模式为什么需要所有的页面指向同一个页面呢?

因为用户喜欢刷新。比如说当前页面是/4,点击刷新,如果4没有定位到该页面就会到404页面,那这可不行。

memory模式(一般不用)

memory模式既不用hash也不用history,用一个对象来存储你的东西。

hash、history模式都是把数据存在URL,URL一变,页面就要变。memory是存到localStorage里。

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("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.setItem("xxx", href);
        // 通知
        onStateChange(href);
    });
    }
    route(app);
    function onStateChange() {
    console.log("state 变了");
    route(app);
    }

把路径存在用户看不见的地方,没有路径

网络异常,图片无法展示
|

\

memory优点:  这种模式适合非浏览器,比如你想在APP里想做路由,APP是没有路径的,不是网页就没有路径。

memory适用于:react nativeweex(Vue手机端的方案),有市场但前端一般不用。

hash、history、memory三者区别

hash、history都是把路径存在url上,只不过一个是URL的哈希,一个是URL的路径。memory不用URL,前端一般放在localStorage,移动端APP放在本地数据库里面。

memory缺点是没有URL,只对单机有效,是单机版的路由,分享无效。hash、history可以记录你的信息,所以你可以分享你的URL。你的页面显示的是几,用户看到的就是几,所以是可分享的路由。

三.Vue-Router 源码

VueRouter源码

相关文章
|
21天前
|
移动开发 缓存 前端开发
深入理解前端路由:原理、实现与应用
本书《深入理解前端路由:原理、实现与应用》全面解析了前端路由的核心概念、工作原理及其实现方法,结合实际案例探讨了其在现代Web应用中的广泛应用,适合前端开发者和相关技术人员阅读。
|
28天前
|
缓存 前端开发 JavaScript
JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式
本文深入解析了JavaScript前端路由的实现原理及其在单页应用中的重要性,涵盖前端路由概念、基本原理、常见实现方式(Hash路由和History路由)、优点及挑战,并通过实际案例分析,帮助开发者更好地理解和应用这一关键技术,提升用户体验。
69 1
|
1月前
|
前端开发 JavaScript 开发者
React与Vue:前端框架的巅峰对决与选择策略
【10月更文挑战第23天】React与Vue:前端框架的巅峰对决与选择策略
|
1月前
|
前端开发 JavaScript 数据管理
React与Vue:两大前端框架的较量与选择策略
【10月更文挑战第23天】React与Vue:两大前端框架的较量与选择策略
|
2月前
|
资源调度 JavaScript 前端开发
路由管理:Vue Router的使用和配置技巧
【10月更文挑战第21天】路由管理:Vue Router的使用和配置技巧
49 3
|
1月前
|
JavaScript 前端开发 搜索推荐
Vue的数据驱动视图与其他前端框架的数据驱动方式有何不同?
总的来说,Vue 的数据驱动视图在诸多方面展现出独特的优势,其与其他前端框架的数据驱动方式的不同之处主要体现在绑定方式、性能表现、触发机制、组件化结合、灵活性、语法表达以及与后端数据交互等方面。这些差异使得 Vue 在前端开发领域具有独特的地位和价值。
|
2月前
|
JavaScript 前端开发 算法
前端优化之超大数组更新:深入分析Vue/React/Svelte的更新渲染策略
本文对比了 Vue、React 和 Svelte 在数组渲染方面的实现方式和优缺点,探讨了它们与直接操作 DOM 的差异及 Web Components 的实现方式。Vue 通过响应式系统自动管理数据变化,React 利用虚拟 DOM 和 `diffing` 算法优化更新,Svelte 通过编译时优化提升性能。文章还介绍了数组更新的优化策略,如使用 `key`、分片渲染、虚拟滚动等,帮助开发者在处理大型数组时提升性能。总结指出,选择合适的框架应根据项目复杂度和性能需求来决定。
|
2月前
|
前端开发 JavaScript 安全
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
在vue前端开发中基于refreshToken和axios拦截器实现token的无感刷新
156 4
|
2月前
|
JavaScript API
vue 批量自动引入并注册组件或路由等等
【10月更文挑战第12天】 vue 批量自动引入并注册组件或路由等等
|
2月前
|
JavaScript 前端开发 API
vue3中常用插件的使用方法:按需引入自定义组件,自动导入依赖包,自动生成路由,自动生成模拟数据
vue3中常用插件的使用方法:按需引入自定义组件,自动导入依赖包,自动生成路由,自动生成模拟数据
871 0