一、前端路由

什么是前端路由?简单地说,前端路由是一种根据url来渲染前端组件的技术。

前端路由应用于什么场景?多页面的场景,在多页面的场景下,相对于后端路由,前端路由由于可以实现无刷新的效果,用户体验更好。

比如,当我们的应用有两个页面,home主页,和user用户页面时候,我们希望https://my.domain.com/home访问到主页的内容,https://my.domain.com/user访问用户页面,我们就可以使用前端路由来实现。

使用前端路由,每个访问路径会对应一个组件,这个组件我们页称为页面。

二、Vue Router

注意本文中示例vue-router使用的版本是4

1. 路由配置

使用前端路由技术开发应用时候,我们希望指定url的路径与组件之间的对应关系,即当访问某个url时候,渲染哪个组件。

这是通过Vue Router提供的API:createRouter方法来实现的。具体使用为以下步骤:

  1. 提供路由配置并传入到createRouter方法中
  2. 在组件中通过指定组件渲染的位置,当路径匹配上时候,组件会替换掉标签。
  1. // main.js
  2. import { createApp } from 'vue';
  3. import {createRouter, createWebHistory} from 'vue-router';
  4. import routerConfig from './route.config';
  5. import App from './App';
  6. const router = createRouter({
  7. history: createWebHistory(),
  8. routes: routerConfig
  9. });
  10. createApp(App).use(router).mount('#app')
  1. // route.config.js
  2. import Home from './pages/home';
  3. import User from './pages/user';
  4. export default [
  5. {
  6. path: '/home',
  7. component: Home
  8. },
  9. {
  10. path: '/user',
  11. component: User
  12. }
  13. ];
  1. // App.vue
  2. <template>
  3. <div>
  4. <router-view></router-view>
  5. </div>
  6. </template>
  7. <script>
  8. export default {};
  9. </script>

上面实例代码展示了基本的路由配置,当访问localhost:8080/home时候,会渲染Home组件,当访问localhost:8080/user时候会渲染user组件。

当在全局注入了router(createApp(App).use(router))之后,在组件中可以通过this.$route来访问路由对象。

2. 动态路由匹配

有时候我们需要动态的路由,即路径中存在动态的字符,这时候可以通过Vue Router支持的动态路由语法来匹配。

路由配置:

  1. {
  2. // http://localhost:8080/user/123
  3. path: '/user/:id',
  4. component: User
  5. }

User组件:

  1. <template>
  2. <div>user: {{$route.params.id}}</div>
  3. </template>
  4. <script>
  5. export default {};
  6. </script>

当访问http://localhost:8080/user/123时候,匹配上了路由’/user/:id’,其中123对应id。因此会渲染component声明的组件:User,在User里面可以通过this.$route访问路由对象,$route.params是动态路由匹配到的参数,所以模板中的{{$route.params.id}}值为123。

3. 导航链接和编程式导航

我们已经知道了如何通过配置路由来让访问路径和组件对应。那么在应用内如何处理路由跳转呢?即我们想实现点击某个链接跳转页面,应该怎么做呢?

有两种方式,导航链接编程式导航

导航链接即使用Vue Router提供的组件:来实现导航:

  1. <router-link to="/home">进入home页面</router-link>

上面的代码会在页面展示一个“进入home页面”的超链,点击之后跳转到home页面。它的行为和原生的a标签并不一样,它不会真正刷新浏览器,而只是渲染home组件。

编程式导航是通过js代码来实现路由跳转

  1. <button @click="gotoHome">点击进入home页面</button>
  1. {
  2. methods: {
  3. gotoHome() {
  4. this.$router.push('/home');
  5. }
  6. }
  7. };

编程式导航更加灵活,比如当跳转不是点击触发的时候,就需要用编程式导航来实现。

4. 重定向和别名

重定向:如果我们希望访问一个路径时候,自动跳转到另一个路径,那么就需要Vue Router提供的重定向的能力。

  1. {
  2. path: '/',
  3. redirect: '/home'
  4. }

这样配置声明了访问根路径时候重定向到’/home’。当访问http://localhost:8080时候,就会自动跳转到[http://localhost:8080](http://localhost:8080)/home了。

别名:设置一个路由的别名,可以让不同访问的路径指向同一个组件。

  1. {
  2. path: '/home',
  3. component: Home,
  4. alias: '/main'
  5. },

上面的配置会让我们在访问http://localhost:8080/home和http://localhost:8080/main时候都会渲染Home组件。

5. 嵌套路由

一般一个web应用的路径可能是多级的,比如我们有一个用户列表页面,还有一个用户详情页。那么我们会希望https://my.domain.com/user/list访问用户列表页,https://my.domain.com/user/detail访问的是用户详情页。

对于多级路径,在路由配置时候是嵌套的形式,user是外层路由,而list和detail是里层路由,

对于嵌套路由的配置有2个步骤:

  1. 配置父路由和子路由,通过children字段声明
  2. 父路由声明子路由的组件渲染的位置,通过组件来实现。

例如我们想在上面’/user/:id’动态路由后面增加一个嵌套路由’/user/:id/list’和’/user/:id/detail’,那么首先配置路由

  1. {
  2. path: '/user/:id',
  3. component: User,
  4. children: [
  5. {
  6. path: '/user/:id/list',
  7. component: UserList
  8. },
  9. {
  10. path: '/user/:id/detail',
  11. component: UserDetail
  12. }
  13. ]
  14. }

然后在父路由User组件中增加标签:

  1. <template>
  2. <div>user: {{$route.params.id}}</div>
  3. <router-view></router-view>
  4. </template>
  5. <script>
  6. export default {};
  7. </script>

然后访问/user/123/list和/user/123/detail就可以看到能够正常展示了。

这里要注意父路由的组件是必须的,它用来在子路由匹配上之后渲染子组件。

6. 导航守卫

很多情况下我们需要在路由跳转前后做一些处理,例如在跳转到某个路由之前先判断一下用户是否有权限,如果没有权限组不跳转。

导航守卫是Vue Router提供的在路由跳转前后做处理的钩子API。

主要有3中导航守卫:全局守卫路由独享守卫组件内守卫

当我们需要在路由跳转时候做一些通用逻辑时候可以使用全局守卫。

  1. import { createApp } from 'vue';
  2. import {createRouter, createWebHistory} from 'vue-router';
  3. import routerConfig from './route.config';
  4. import App from './App';
  5. const router = createRouter({
  6. history: createWebHistory(),
  7. routes: routerConfig
  8. });
  9. // 全局守卫
  10. router.beforeEach((to, from) => {
  11. console.log(`从${from.path} 跳转到 ${to.path}`);
  12. });
  13. createApp(App).use(router).mount('#app')
  1. 对于个别路由自己的逻辑可以使用路由独享守卫
  1. {
  2. path: '/user/:id',
  3. component: User,
  4. children: [
  5. {
  6. path: 'list',
  7. component: UserList,
  8. // return false 则不跳转
  9. beforeEnter: () => {
  10. return false
  11. },
  12. },
  13. {
  14. path: 'detail',
  15. component: UserDetail
  16. },
  17. ]
  18. }
  1. 在组件中也可以声明相应地钩子来处理跳转逻辑
  1. // UserList.vue
  2. <template>
  3. <div>user-list</div>
  4. </template>
  5. <script>
  6. export default {
  7. beforeRouteEnter() {
  8. return false;
  9. }
  10. };
  11. </script>

7. 两种历史记录模式

两种历史记录模式的使用

前端路由有两种模式,HTML5和hash,默认是hash模式。这两种模式本质是不同的底层浏览器技术,但是上层Vue Router做了统一化的封装,因此在我们开发组件和配置路由时候使用这两种模式的区别并不大:

  1. import {createRouter, createWebHistory, createWebHashHistory} from 'vue-router';
  2. import routerConfig from './route.config';
  3. const router = createRouter({
  4. // createWebHashHistory
  5. history: createWebHistory(),
  6. routes: routerConfig
  7. });
  1. 可以看到在使用createRouter创建vue路由时候,可以指定使用HTML5createWebHistory)还是hash模式(createWebHashHistory)。

比如如果访问home页,hash是这样的http://localhost:8080/#/home,HTML5模式是这样的http://localhost:8080/home

这两种模式有几个主要区别

  1. HTML5模式的路由没有”#”字符,而是在域名后直接写路径,更加优雅
  2. 由于#后面的字符不会发给服务器,因此hash路由SEO比较差,且不会在服务器生成日志记录
  3. HTML5需要服务器在访问不同的路径时候都能fallback到index.html,因此相对麻烦

两种历史记录模式的原理

前端路由的原理关键有2点

  1. 可以修改url,但不会引起刷新,从而在不刷新的页面的情况下跳转路由。
  2. 监听url改变,根据url渲染对应组件。

hash模式和history模式的原理都是基于这两点。hash是通过浏览器提供的locationAPI修改url,通过onhashchange方法监听hash改变;history通过浏览器提供的history.pushState或者history.replacestate修改url,通过popState事件监听url改变。