怎么理解前端路由? 当然是自己实现一个啦!

本文解析了SPA(单页面应用)中前端路由的工作原理及其与后端路由的区别,并通过实例展示了如何使用JavaScript和HTML5的History API实现前端路由。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

spa流行的今天不少同学会把前端路由跟后端路由弄混, 莫名其妙的怎么页面404啦之类'奇怪'的问题, 其实这就是没弄清楚前端路由和后端路由的原因(当然你用hash当我没说).

本文所有前端路由都是spa的情况下, 不存在后端渲染好变量的情况

原理

首先我们看看前后端路由在浏览器中是怎么工作的, 上图:

后端控制的路由:

我们可以知道后端其实返回的是html字符串, 也就是dom节点不出意外的话是确认的. 不管你请求多少次, 结果都是确定的(get 幂等). 所以也就不存在404的情况

前端控制的路由:

如果是spa的话, 我们可以知道不管你请求那个页面, 在后端处理好的情况下后端都会返回一个html文件(所谓单页的由来), 静态资源当然也是类似的. 那么我们可能有点疑问, 比如一个个人主页, 如果只返回一个html文件的话, 怎么得到不同的用户资料呢, 答案就是前端路由(大部分情况, 不排除本地存储?), js根据不同的路由再向服务器请求相关资料, 也就是说其实第一次服务端渲染我们的页面是空的, 后期ajax请求. 所以我们看到很多单页页面打开了首先要loading一会. 就是在向服务器请求渲染页面.

实现

后端路由我们暂且不去管它, 我们看看是怎么实现的:

在非hash的情况下, 前端路由的实现基础是window.history, 当然我们不用去管它的兼容性了, 反正现在大部分浏览器能用就是了:

history有个重要的方法就是pushState, 其它的方法暂时用不到不提, 它的作用呢就是改变浏览器地址栏里的地址, 以及在历史纪录里加上一条, 除此以外没啥别的副作用了, 比如:

var stateObj = { foo: "bar" };
history.pushState(stateObj, "page 2", "bar.html");
复制代码

上面的代码只会跳到一个 bar.html的地址, 但是页面本身并未跳转, 我们不是来讲history对象本身的, 有兴趣可以自行翻看mdn.

其实参考后端对路由的控制, 我们大略可以想像一个前端路由所具有的功能:

  1. 对路由做出响应
  2. 渲染
  3. 一些事件, 比如beforeChange之类的

当然我们现在一切从简, 上面那些说清楚了起实现无非就是苦力了, 先给大家看看效果吧:

还是有点意思的吧.

下面是html代码:

<!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>Document</title>
  <link href="https://cdn.bootcss.com/bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
  <nav class="navbar navbar-default nav-static-top">
    <div class="container-fluid">
      <!-- Brand and toggle get grouped for better mobile display -->
      <div class="navbar-header">
        <a class="navbar-brand" href="#">LOGO</a>
      </div>

      <!-- Collect the nav links, forms, and other content for toggling -->
      <div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
        <ul class="nav navbar-nav">
          <li><a href="/1" data-role="custom-history">地址1 <span class="sr-only">(current)</span></a></li>
          <li><a href="/2" data-role="custom-history">地址2</a></li>
        </ul>
      </div><!-- /.navbar-collapse -->
    </div><!-- /.container-fluid -->
  </nav>
  <div id="app" class="container">
    <div class="panel panel-default">
      <div class="panel-heading">Panel heading without title</div>
      <div class="panel-body">
        Panel content
      </div>
    </div>

  </div>

  <script src="https://cdn.bootcss.com/jquery/3.2.1/jquery.min.js"></script>
  <script src="./route.js"></script>
  <script src="./index.js"></script>
</body>
</html>
复制代码

index.js

const $routeController = $('a[data-role=custom-history]')

const app = new Route()

app.set('/1', function () {
  $('#app').html('1')
})

app.set('/2', function () {
  $('#app').html(2)
})
复制代码

route.js

class Route {
  constructor () {
    this.urls = []
    this.handles = {}
    window.addEventListener('popstate', (e) => {
      const state = e.state || {}
      const url = state.url || null

      if (url) {
        this.refresh(url)
      }
    })

    const $routeController = $('a[data-role=custom-history]')

    $routeController.on('click', e => {
      e.preventDefault()
      const link = $(e.target).attr('href')
      history.pushState({ url: link }, '', link)
      this.refresh(link)
    })
  }

  set (route, handle) {
    if (this.urls.indexOf(route) === -1) {
      this.urls.push(route)
      this.handles[route] = handle
    }
  }

  refresh (route) {
    if (this.urls.indexOf(route) === -1) throw new Error('请不要这样调用, 路由表中不存在!')
    this.handles[route]()
  }
}
复制代码

按我的本意是不想在一篇文章里贴这么多代码的, 但是因为也不可以直接嵌入jsbin之类的, 方便大家试试看效果, 就放进来把, 因为代码比较简单, 而且深度绑定到了dom上, 就不要嘲笑啦!

<think>好的,用户问的是“一个项目可以多个路由吗”。首先,我需要确认用户的问题具体指什么。在Vue Router的上下文中,“多个路由”可能有不同的含义。可能是指多个路由配置实例,或者是在同一个应用中定义多个独立的路由系统,或者只是在一个应用中定义很多路由路径。 首先,我得回忆Vue Router的基本结构。通常,一个Vue项目会创建一个路由实例,通过createRouter方法,并传入路由配置数组。这个数组里可以定义很多不同的路径,每个路径对应不同的组件。所以,常规情况下,一个项目确实可以有多个路由配置,即多个路由路径,但都属于同一个路由实例。这种情况下,用户的问题应该回答为“可以”,但需要说明是在同一个路由实例下管理多个路由路径。 但用户可能混淆了“路由”和“路由实例”的概念。如果用户指的是多个独立的路由实例,比如不同的路由系统同时存在,这种情况是否可行呢?例如,是否有办法在一个Vue应用中创建多个router实例,分别管理不同的部分。这时候可能需要考虑Vue应用的结构,通常一个应用只能有一个活动的router实例,通过app.use(router)来挂载。不过,如果使用Vue 3的组合式API或某些高级技巧,可能可以实现多个路由实例,但这种情况非常罕见,且可能带来复杂性问题,所以通常不建议这样做。 接下来,用户可能的实际需求是什么?比如,他们是否在开发一个大型应用,需要模块化路由配置,或者需要将路由分成多个文件管理?比如,在大型项目中,可能会将路由拆分成多个模块,然后通过合并的方式导入到主路由配置中,这样虽然路由配置分散在多个地方,但最终仍属于同一个路由实例。这种情况下,用户的问题可能更关注如何组织多个路由配置,而不是字面意义上的多个路由实例。 此外,用户可能遇到需要多个路由实例的情况,比如微前端架构中,不同子应用有自己的路由,但这种情况通常是由框架(如qiankun)处理,每个子应用独立运行,各自有自己的router实例。但这种情况下的“多个路由”属于不同Vue应用实例,而非同一个项目中的多个路由实例。 所以,综合考虑,用户的真实问题可能是在问是否可以在一个Vue项目中定义多个路由路径,即多个不同的页面路径,这当然是支持的。而如果是多个路由实例,通常不支持,也不推荐。因此,在回答时需要明确这两个层面,避免用户误解。 另外,需要检查之前的对话历史,用户之前询问了Vue路由的基本介绍,现在可能是在进一步了解路由的配置方式,比如如何组织大量路由,或者是否有办法分割路由配置。因此,在回答时除了直接回答问题,还可以提供一些最佳实践,比如路由模块化、动态导入等,帮助用户更好地管理多个路由配置。 总结回答的结构:首先明确回答可以,然后解释在同一个路由实例下管理多个路由配置,接着说明不支持多个路由实例,但可以模块化组织路由,最后给出示例和最佳实践。这样既解决了用户的问题,又提供了进一步的信息,帮助用户更好地应用。</think>--- ### Vue 项目中能否使用多个路由? **可以**。在 Vue 项目中,通常通过 **单个路由实例** 管理 **多个路由配置**,即所有页面路径(`path`)均定义在同一个路由配置文件中。但需要注意以下两种常见场景: --- #### 一、常规用法:单路由实例,多路由配置 1. **路由集中管理** 所有路由路径(`path`)统一注册到 **一个路由实例** 中,通过 `routes` 数组定义多个路由规则: ```javascript // router.js const routes = [ { path: '/', component: Home }, // 路由1 { path: '/about', component: About }, // 路由2 { path: '/user/:id', component: User }, // 路由3 // ...可继续添加更多路由 ]; ``` 2. **路由模块化拆分** 大型项目中可将路由按功能模块拆分,再合并到主路由: ```javascript // userRoutes.js export const userRoutes = [ { path: 'profile', component: Profile }, { path: 'settings', component: Settings } ]; // mainRoutes.js import { userRoutes } from './userRoutes'; const routes = [ { path: '/', component: Home }, { path: '/user', component: UserLayout, children: userRoutes // 合并子路由 } ]; ``` --- #### 二、特殊场景:多路由实例(不推荐) 若强行在一个 Vue 应用中创建多个路由实例(如 `router1` 和 `router2`),会导致冲突,因为 Vue 仅支持 **单路由实例挂载**。但以下场景可能间接实现路由逻辑: 1. **微前端架构** 主应用与子应用各自独立运行,**每个子应用拥有自己的路由实例**(如通过 `qiankun` 框架实现)。 2. **动态路由重置** 通过 `router.removeRoute()` 和 `router.addRoute()` 动态替换路由配置,模拟“多路由”效果: ```javascript // 切换路由配置 function switchRoutes(newRoutes) { router.getRoutes().forEach(route => { router.removeRoute(route.name); }); newRoutes.forEach(route => { router.addRoute(route); }); } ``` --- #### 三、最佳实践 1. **统一管理路由** 推荐将所有路由集中定义或模块化拆分后合并,保持可维护性。 2. **嵌套路由优化层级** 使用 `children` 配置组织复杂页面的层级关系: ```javascript { path: '/dashboard', component: DashboardLayout, children: [ { path: 'overview', component: Overview }, // 实际路径:/dashboard/overview { path: 'analytics', component: Analytics } ] } ``` 3. **动态路由按需加载** 结合懒加载提升性能: ```javascript const UserDetails = () => import('@/views/UserDetails.vue'); ``` --- #### 总结 - ✅ **支持**:一个 Vue 项目可以通过单路由实例管理 **成百上千个路由路径**。 - ❌ **不支持**:同一 Vue 应用内同时运行多个路由实例(需通过微前端等特殊方案间接实现)。 - 💡 **建议**:通过模块化拆分和嵌套路由,高效组织复杂项目的路由结构。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值