41.VueJS学习-VueRouter

一、基本使用

1.路由模式

在 Vue 中,有两种主要的路由模式:哈希模式(hash)和历史模式(history)。

1.哈希模式(hash)
        1.工作原理        
  • 在哈希模式下,URL 中的哈希(#)部分会被浏览器用来模拟页面的不同状态,而不会向服务器发送请求。
  • 例如,当访问 http://example.com/#/home 时,浏览器实际上是在加载 http://example.com/,但 Vue Router 会根据哈希值 #/home 来确定要显示的组件。
  • 当用户在页面上导航时,哈希值会发生变化,但浏览器不会重新加载整个页面,而是由 Vue Router 根据新的哈希值来切换组件。
        2.优缺点:
  • 兼容性好:哈希模式在所有浏览器中都能正常工作,包括不支持 HTML5 History API 的旧版本浏览器。
  • 无需服务器配置:由于哈希值的变化不会触发服务器请求,所以在使用哈希模式时,不需要对服务器进行特殊配置来处理不同的路由。
  • 缺点: URL 不太美观:哈希值会出现在 URL 中,使得 URL 看起来不太干净和美观。
2.历史模式(history)
        1.工作原理
  • 历史模式利用 HTML5 的 History API 来管理 URL 和页面状态。
  • 当使用历史模式时,URL 看起来就像普通的 URL,没有哈希值。例如,http://example.com/home
  • 当用户在页面上导航时,Vue Router 会通过 History API 来修改浏览器的历史记录,而不是使用哈希值。这样可以实现更美观的 URL 和更自然的用户体验。
        2.优缺点
  • URL 美观:没有哈希值,看起来更像传统的网站 URL。
  • 更好的用户体验:用户可以像在传统网站上一样使用浏览器的前进、后退按钮,并且可以将页面添加到书签,而不会出现哈希值相关的问题。
  • 缺点:服务器配置要求:由于历史模式下的 URL 变化会被浏览器视为真正的 URL 变化,所以需要服务器进行相应的配置,以确保当用户直接访问某个特定的 URL 时,服务器能够正确地返回对应的页面内容。否则,用户在刷新页面或者直接访问某个路由时,可能会出现 404 错误。
2.VueRouter-动态参数路由

在 Vue Router 中,动态参数路由允许你在 URL 中定义参数,这些参数可以在组件中被访问和使用。以下是关于 Vue Router 动态参数路由的详细介绍:

        1.定义动态参数路由

                在路由配置中,可以使用冒号(:)来定义动态参数。例如:

import { createRouter, createWebHistory } from 'vue-router'

const routes = [
    {
        path: '/user/:id'
        component: UserComponent
    }
]

const router = createRouter({
    history: createWebHistory(),
    routes
})
                在上面的例子中,:id是一个动态参数,它可以匹配任何值。当用户访问 /user/123 时,id 参数的值将是 123。        
        2.在组件中访问动态参数

                在组件中,可以通过 $route.params 对象来访问动态参数。

<template>
    <div>
        <h2>User id: {{ $route.params.id }}</h2>
    </div>
</template>

<script>
    export default {
        name: 'UserComponent'
    }
</script>
                在上面的组件中,$route.params.id 将显示当前路由中的 id 参数的值。        
        3.动态参数的类型
                默认情况下,动态参数被视为字符串。但是,你可以通过在路由配置中添加正则表达式来限制参数的类型。例如:
const routes = [
    {
        path: '/user/:id(\\d+)',
        component: UserComponet
    }
]
                在上面的例子中,:id 参数只能是一个或多个数字。如果用户访问 /user/abc,将会出现 404 错误,因为参数不匹配规定的类型。
        4.导航到动态参数路由

                可以使用 router.push 方法来导航到动态参数路由,并传递参数值。例如:

// 在某一个组件的方法中

this.$router.push({path: `/user/${userId}`})
               在上面的例子中,userId 是一个变量,它的值将被作为 id 参数传递给 /user/:id 路由。
         5.监听路由参数变化

                当路由参数发生变化时,可以使用 watch 来监听 $route.params 的变化。例如:

<template>
  <div>
    <h2>User ID: {{ $route.params.id }}</h2>
  </div>
</template>

<script>
export default {
  name: 'UserComponent',
  watch: {
    '$route.params.id': {
      immediate: true,
      handler(newId, oldId) {
        // 处理参数变化的逻辑
      },
    },
  },
};
</script>
        在上面的例子中,当 id 参数发生变化时,handler 函数将被调用。
3.VueRouter-路由的匹配语法

        在 Vue Router 中,路由的匹配语法用于定义路由的路径规则,以确定哪些 URL 应该匹配特定的路由。以下是关于 Vue Router 路由匹配语法的详细介绍:

        1.基本路径匹配

                1.简单路径

                路由路径可以是一个简单的字符串,表示一个固定的 URL 路径。例如:

     const routes = [
       {
         path: '/home',
         component: HomeComponent,
       },
     ];

                        当用户访问 /home 时,这个路由将被匹配,并且 HomeComponent 将被渲染。

                   2.动态参数

                        使用冒号(:)来定义动态参数。动态参数可以在 URL 中匹配任意值,并在组件中通过 $route.params 访问。例如:

     const routes = [
       {
         path: '/user/:id',
         component: UserComponent,
       },
     ];

                当用户访问 /user/123 时,id 参数的值将是 123,可以在 UserComponent 中通过 $route.params.id 访问。

        2.可选参数和通配符

                1.可选参数:使用括号和问号(?:)来定义可选参数。可选参数在 URL 中可以出现也可以不出现。例如:

     const routes = [
       {
         path: '/profile/:username?',
         component: ProfileComponent,
       },
     ];
  • 用户可以访问 /profile 或 /profile/john,在 ProfileComponent 中可以通过 $route.params.username 访问用户名参数,如果没有提供用户名,则该参数为 undefined

                2.通配符:使用星号(*)来定义通配符路由。通配符路由可以匹配任何未被其他路由匹配的 URL。例如:

     const routes = [
       {
         path: '/404',
         component: NotFoundComponent,
       },
       {
         path: '*',
         component: NotFoundComponent,
       },
     ];
  • 如果用户访问的 URL 没有被其他路由匹配,将显示 NotFoundComponent

        3.正则表达式匹配

                使用正则表达式来限制动态参数的类型或定义更复杂的路径规则。例如:

   const routes = [
     {
       path: '/user/:id(\\d+)',
       component: UserComponent,
     },
   ];

                在这个例子中,:id 参数只能是一个或多个数字。如果用户访问 /user/abc,将会出现 404 错误,因为参数不匹配规定的类型。

         4.命名路由和路由别名

                1.命名路由:可以为路由命名,以便在导航时更容易引用。例如:

     const routes = [
       {
         path: '/home',
         name: 'home',
         component: HomeComponent,
       },
     ];

               可以使用 router.push({ name: 'home' }) 来导航到这个路由。

                  2.路由别名:可以为路由定义别名,以便在不同的 URL 下访问同一个路由。例如:

     const routes = [
       {
         path: '/home',
         component: HomeComponent,
         alias: '/dashboard',
       },
     ];
  • 用户可以访问 /home 或 /dashboard 来访问 HomeComponent
4.VueRouter-嵌套路由

在 Vue Router 中,嵌套路由允许你在一个父路由下定义子路由,从而构建出具有层次结构的页面布局。以下是关于 Vue Router 嵌套路由的详细介绍:

一、定义嵌套路由

  1. 首先,在父路由的组件中使用 <router-view> 标签来渲染子路由的内容。例如:
    <template>
      <div>
        <h1>Parent Component</h1>
        <router-view></router-view>
      </div>
    </template>
    
    <script>
    export default {
      name: 'ParentComponent',
    };
    </script>
  2. 然后,在路由配置中定义父路由和子路由。例如:
    import { createRouter, createWebHistory } from 'vue-router';
    import ParentComponent from './ParentComponent.vue';
    import ChildComponent from './ChildComponent.vue';
    
    const routes = [
      {
        path: '/parent',
        component: ParentComponent,
        children: [
          {
            path: 'child',
            component: ChildComponent,
          },
        ],
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(),
      routes,
    });

    在上面的例子中,当用户访问 /parent/child 时,ParentComponent 将被渲染,并且在 ParentComponent 的 <router-view> 中会渲染 ChildComponent

二、嵌套路由的路径
  1. 子路由的路径是相对于父路由的路径。例如,如果父路由的路径是 /parent,子路由的路径是 child,那么完整的路径就是 /parent/child

  2. 可以在子路由的路径中使用动态参数和其他路由匹配语法,就像在顶级路由中一样。例如:

    const routes = [
      {
        path: '/parent',
        component: ParentComponent,
        children: [
          {
            path: 'child/:id',
            component: ChildComponent,
          },
        ],
      },
    ];

    在上面的例子中,当用户访问 /parent/child/123 时,id 参数的值将是 123,可以在 ChildComponent 中通过 $route.params.id 访问。

三、多层嵌套路由

可以在子路由中继续定义孙路由,从而实现多层嵌套。例如

const routes = [
  {
    path: '/parent',
    component: ParentComponent,
    children: [
      {
        path: 'child',
        component: ChildComponent,
        children: [
          {
            path: 'grandchild',
            component: GrandchildComponent,
          },
        ],
      },
    ],
  },
];

在上面的例子中,当用户访问 /parent/child/grandchild 时,ParentComponentChildComponent 和 GrandchildComponent 将依次被渲染,每个组件中的 <router-view-view> 用于渲染下一级的子路由内容。

5.VueRouter-编程式导航

在 Vue Router 中,前端编程式导航允许你通过代码来触发路由的切换,而不是通过传统的链接点击。以下是关于 Vue Router 前端编程式导航的详细介绍:

一、使用router.push进行导航
  1. router.push方法接受一个路由地址或一个描述路由的对象作为参数,并将导航到对应的路由。例如:
    // 导航到一个字符串路径
    this.$router.push('/home');
    
    // 导航到一个命名路由
    this.$router.push({ name: 'user', params: { id: 123 } });
    
    // 带查询参数,结果是 /register?plan=private
    this.$router.push({ path: '/register', query: { plan: 'private' } });
  2. 如果导航失败(例如,目标路由不存在),会抛出一个错误。你可以通过捕获这个错误来处理导航失败的情况。例如:
    try {
      this.$router.push('/non-existent-route');
    } catch (error) {
      console.log('Navigation failed:', error);
    }
二、使用router.replace进行替换导航

  1. router.replace方法与router.push类似,但它不会在历史记录中添加新的记录,而是替换当前的历史记录项。例如:
    this.$router.replace('/home');
  2. 这在某些情况下很有用,例如,当你希望在不影响用户后退按钮行为的情况下导航到另一个页面。
三、使用router.go进行历史导航

  1. router.go方法接受一个整数作为参数,表示在历史记录中前进或后退的步数。例如:
    // 向前一步,类似于浏览器的前进按钮
    this.$router.go(1);
    
    // 向后一步,类似于浏览器的后退按钮
    this.$router.go(-1);
四、在组件中使用编程式导航
  1. 在 Vue 组件中,可以通过this.$router访问路由实例,并使用上述方法进行编程式导航。例如:
    <template>
      <div>
        <button @click="goToHome">Go to Home</button>
      </div>
    </template>
    
    <script>
    export default {
      methods: {
        goToHome() {
          this.$router.push('/home');
        },
      },
    };
    </script>

  2. 还可以在生命周期钩子、方法或计算属性中使用编程式导航,根据应用的逻辑来触发路由的切换。
  3. 前端编程式导航提供了更大的灵活性,可以根据应用的具体需求动态地控制路由的切换,实现更加复杂的用户交互和页面导航逻辑。
6.VueRouter-命名路由

在 Vue Router 中,命名路由可以为路由赋予一个名称,通过这个名称可以更方便地进行路由导航,而不需要记住具体的路径字符串。以下是关于 Vue Router 命名路由的详细介绍:

一、定义命名路由
  1. 在路由配置中,可以使用 name 属性为路由指定一个名称。例如:
    import { createRouter, createWebHistory } from 'vue-router';
    import HomeComponent from './HomeComponent.vue';
    import AboutComponent from './AboutComponent.vue';
    
    const routes = [
      {
        path: '/',
        name: 'home',
        component: HomeComponent,
      },
      {
        path: '/about',
        name: 'about',
        component: AboutComponent,
      },
    ];
    
    const router = createRouter({
      history: createWebHistory(),
      routes,
    });
在上面的例子中,为两个路由分别赋予了名称 home 和 about
二、使用命名路由进行导航

  1. 可以使用 router.pushrouter.replace 等方法,并传入一个包含 name 属性的对象来进行命名路由的导航。例如:
    // 使用命名路由进行导航
    this.$router.push({ name: 'about' });
  2. 也可以传递参数给命名路由。例如:
    // 带有参数的命名路由导航
    this.$router.push({ name: 'user', params: { id: 123 } });

    在目标路由的组件中,可以通过 $route.params 获取传递的参数。

三、命名路由的优势
  1. 提高可读性:使用命名路由可以使代码更具可读性,特别是在复杂的应用中,通过名称可以清楚地知道要导航到哪个页面,而不需要记住具体的路径字符串。

  2. 易于维护:如果路由的路径发生变化,只需要在路由配置中修改路径,而不需要在所有使用该路由的地方进行修改。使用命名路由可以保持代码的稳定性。

  3. 动态生成路由链接:在某些情况下,可能需要动态生成路由链接。使用命名路由可以更容易地实现这一点,而不需要手动拼接路径字符串。例如:

    <template>
      <div>
        <router-link :to="{ name: 'user', params: { id: userId } }">User Profile</router-link>
      </div>
    </template>
    
    <script>
    export default {
      data() {
        return {
          userId: 123,
        };
      },
    };
    </script>

    在上面的例子中,根据 userId 动态生成了一个指向用户 profile 页面的路由链接。

7.VueRouter-重定向和别名

在 Vue Router 中,重定向(redirect)和别名(alias)是两个非常有用的功能,可以用来实现灵活的路由配置和用户体验优化。

一、重定向(redirect)
  1. 定义重定向:    
  • 在路由配置中,可以使用 redirect 属性来定义重定向。例如:
         const routes = [
           {
             path: '/old-path',
             redirect: '/new-path',
           },
           {
             path: '/new-path',
             component: NewComponent,
           },
         ];
  • 在上面的例子中,当用户访问 /old-path 时,会被自动重定向到 /new-path,并渲染 NewComponent
  1. 重定向到命名路由
    1. 可以将重定向目标设置为命名路由。例如:
           const routes = [
             {
               path: '/old-path',
               redirect: { name: 'newRoute' },
             },
             {
               path: '/new-path',
               name: 'newRoute',
               component: NewComponent,
             },
           ];
  2. 函数式重定向
    1. redirect 属性也可以接受一个函数,该函数接收当前路由对象作为参数,并返回重定向的目标。例如:
           const routes = [
             {
               path: '/login',
               redirect: to => {
                 // 如果用户已登录,重定向到首页
                 if (isLoggedIn()) {
                   return '/home';
                 }
                 // 否则,保持在登录页面
                 return '/login';
               },
             },
           ];
二、别名(alias)
1.定义别名
  • 别名允许一个路由匹配多个 URL。在路由配置中,可以使用 alias 属性来定义别名。例如:
         const routes = [
           {
             path: '/home',
             component: HomeComponent,
             alias: '/dashboard',
           },
         ];
  • 在上面的例子中,用户访问 /home 或 /dashboard 都会渲染 HomeComponent
2.多个别名
  • 可以为一个路由设置多个别名。例如:
         const routes = [
           {
             path: '/home',
             component: HomeComponent,
             alias: ['/dashboard', '/start'],
           },
         ];
3.别名与重定向的区别
  • 重定向会实际改变浏览器的 URL,而别名只是让一个路由匹配多个 URL,但浏览器地址栏中的 URL 不会改变。
  • 重定向和别名在 Vue Router 中可以帮助你实现灵活的路由配置,提高用户体验,并且可以根据应用的需求进行动态的路由调整。
8.手写VueRouter
class VueRouter {
    constructor (options) {
        this.routes = options.routes
        this.mode = options.mode || 'hash'
        this.history = []
        this.currentRoute = null

        // 根据模式初始化路由监听
        if (this.mode === 'hash') {
            window.addEventListener('hashchange', this.onHashChange.bind(this))
        } else if (this.mode === 'history') {
            window.addEventListener('popstate', this.onHistoryChange.bind(this))
        }

        this.init()
    }

    init () {
        if (this.mode === 'hash') {
            const hash = window.location.hash.slice(1) || '/'
            this.currentRoute = this.matchRoute(hash)
        } else if (this.mode === 'history') {
            const path = window.location.pathname
            this.currentRoute = this.matchRoute(path)
        }
    }

    matchRoute (path) {
        for (const route of this.routes) {
            if (route.path === path || route.path === '*') {
                return route
            }
        }
        return null
    }

    onHashChange () {
        const hash = window.location.hash.slice(1)
        this.currentRoute = this.matchRoute(hash)
        this.emit('routeChange', this.currentRoute)
    }

    onHistoryChange () {
        const path = window.location.pathname
        this.currentRoute = this.matchRoute(path)
        this.emit('routeChange', this.currentRoute)
    }

    push(path) {
        if (this.mode === 'hash') {
            window.location.hash = `#${hash}`
        } else if (this.mode === 'history') {
            window.history.pushState(null,null,path)
            this.currentRoute = this.matchRoute(path)
            this.emit('routeChange', this.currentRoute)
        }
    }

    emit (event, ...args) {
        if (this.listeners && this.listeners[event]) {
            this.listeners[event].forEach(listener => listener(...args));
        }
    }

    on (event, listener) {
        if (!this.listeners) {
            this.listeners = {}
        }
        if (!this.listeners[event]) {
            this.listeners[event] = []
        }
        this.listeners[event].push(listener)
    }
}

使用示例:

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
</head>

<body>
  <div id="app"></div>

  <script>
    // 定义两个组件
    const HomeComponent = { template: '<div>Home</div>' };
    const AboutComponent = { template: '<div>About</div>' };

    // 使用 VueRouter
    const router = new VueRouter({
      mode: 'hash',
      routes: [
        { path: '/', component: HomeComponent },
        { path: '/about', component: AboutComponent },
      ],
    });

    // 创建 Vue 实例并挂载
    const app = {
      template: `
        <div>
          <nav>
            <ul>
              <li><a href="#/" @click.prevent="goHome">Home</a></li>
              <li><a href="#/about" @click.prevent="goAbout">About</a></li>
            </ul>
          </nav>
          <router-view></router-view>
        </div>
      `,
      methods: {
        goHome() {
          router.push('/');
        },
        goAbout() {
          router.push('/about');
        },
      },
    };

    const vm = new Vue(app).$mount('#app');

    router.on('routeChange', route => {
      console.log('Route changed to:', route);
    });
  </script>
</body>

</html>

这个手写的 Vue Router 实现了基本的路由功能,包括根据不同模式监听 URL 变化、匹配路由、导航等。但这只是一个简单的示例,实际的 Vue Router 有更多的功能和优化。

二、进阶使用

1.导航守卫在
Vue Router 中,导航守卫是一种用于控制路由导航的机制。它允许你在路由切换的不同阶段执行特定的逻辑,例如验证用户身份、权限控制等。以下是关于 Vue Router 导航守卫的详细介绍:

1、全局前置守卫(beforeEach)

定义全局前置守卫:

  • 使用 router.beforeEach 方法来定义一个全局前置守卫。这个守卫会在每次路由切换之前被调用。例如:
         const router = new VueRouter({
           //...路由配置
         });
    
         router.beforeEach((to, from, next) => {
           // 在这里执行逻辑
           // to 是目标路由对象
           // from 是源路由对象
           // next 是一个函数,用于控制导航流程
           if (to.meta.requiresAuth &&!isLoggedIn()) {
             // 如果目标路由需要认证且用户未登录,重定向到登录页面
             next('/login');
           } else {
             // 否则继续导航
             next();
           }
         });

    守卫函数参数:

  • to:目标路由对象,包含目标路由的信息,如路径、参数、查询参数等。
  • from:源路由对象,包含源路由的信息。
  • next:一个函数,用于控制导航流程。必须调用 next() 才能继续导航。如果不调用 next(),导航将被阻止。

2、全局后置守卫(afterEach)

定义全局后置守卫:

  • 使用 router.afterEach 方法来定义一个全局后置守卫。这个守卫会在每次路由切换之后被调用。例如:
         router.afterEach((to, from) => {
           // 在这里执行逻辑,例如记录页面访问日志
           console.log(`从 ${from.path} 导航到 ${to.path}`);
         });

    守卫函数参数:

  • to:目标路由对象。
  • from:源路由对象。

3、路由独享守卫(beforeEnter)

定义路由独享守卫:

  • 在路由配置中,可以使用 beforeEnter 属性来定义路由独享守卫。例如:
         const routes = [
           {
             path: '/admin',
             component: AdminComponent,
             meta: { requiresAuth: true },
             beforeEnter: (to, from, next) => {
               // 在这里执行逻辑,例如检查用户是否具有管理员权限
               if (isAdmin()) {
                 next();
               } else {
                 next('/');
               }
             },
           },
         ];
  • 守卫函数参数与全局前置守卫相同。

4、组件内守卫

beforeRouteEnter

  • 在组件内,可以使用 beforeRouteEnter 导航守卫在路由进入该组件之前被调用。例如:
         <template>
           <div>Protected Component</div>
         </template>
    
         <script>
         export default {
           beforeRouteEnter(to, from, next) {
             // 在这里执行逻辑,例如检查用户是否有权限访问该组件
             if (hasPermission()) {
               next();
             } else {
               next('/');
             }
           },
         };
         </script>

beforeRouteUpdate

  • 当路由参数变化时,这个守卫会被调用。例如:
         <template>
           <div>User Profile</div>
         </template>
    
         <script>
         export default {
           name: 'UserProfile',
           data() {
             return {
               user: null,
             };
           },
           beforeRouteUpdate(to, from, next) {
             // 当路由参数变化时,重新获取用户数据
             fetchUser(to.params.id).then(user => {
               this.user = user;
               next();
             });
           },
         };
         </script>

beforeRouteLeave:

  • 在离开当前路由之前被调用。例如:
         <template>
           <div>Edit Form</div>
         </template>
    
         <script>
         export default {
           data() {
             return {
               formData: {},
             };
           },
           beforeRouteLeave(to, from, next) {
             // 在这里可以提示用户保存未保存的更改
             if (hasUnsavedChanges()) {
               const confirmLeave = window.confirm('你有未保存的更改,确定要离开吗?');
               if (confirmLeave) {
                 next();
               } else {
                 next(false);
               }
             } else {
               next();
             }
           },
         };
         </script>

    导航守卫在 Vue Router 中提供了强大的控制能力,可以根据不同的应用需求实现各种路由导航的控制逻辑。

  • 10
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值