Code前端首页关于Code前端联系我们

一、基础入门,vue-router4 咋搭起基本路由?

terry 3周前 (09-06) 阅读数 41 #Vue
文章标签 router4;基本路由

不少刚开始用Vue3做项目的同学,一碰到vue-router4就犯懵:路由配置咋和以前不一样了?动态路由、嵌套路由咋整?路由守卫写法变在哪?这篇文章用问答形式,把vue-router4从基础到进阶的关键问题拆明白,帮你少走弯路。

问题1:vue-router4 和 v3 最核心的区别是啥?

最大的变化是API 设计和 Vue3 生态的适配,以前 v3 是通过 new VueRouter({...}) 创建路由实例,v4 要用 createRouter 这种函数式的写法;路由模式也变了,v3 里的 mode: 'history' 现在得换成 createWebHistory()(对应 history 模式)或者 createWebHashHistory()(对应 hash 模式),Vue3 主推组合式 API,v4 也新增了 useRouteruseRoute 这些组合式 API 钩子,在 setup 里用路由更顺手。

举个最直观的配置对比:
v3 写法:

import VueRouter from 'vue-router'
Vue.use(VueRouter)
const router = new VueRouter({
  mode: 'history',
  routes: [...]
})

v4 写法:

import { createRouter, createWebHistory } from 'vue-router'
const router = createRouter({
  history: createWebHistory(), // 替代原来的mode
  routes: [...]
})
// 不需要Vue.use了,直接在createApp里用app.use(router)

问题2:vue-router4 最基础的路由配置步骤是啥?

分四步就能跑通基础路由:

  1. 安装:命令行执行 npm i vue-router@4(注意版本,v4 才适配 Vue3)。
  2. 写路由规则:新建 router/index.js,用 createRouter + createWebHistory + routes 数组定义页面映射。
    import { createRouter, createWebHistory } from 'vue-router'
    import Home from '../views/Home.vue'
    import About from '../views/About.vue'

const routes = [ { path: '/', component: Home }, { path: '/about', component: About } ]

const router = createRouter({ history: createWebHistory(), routes })

export default router

**注入路由到Vue应用**:在 `main.js` 里把路由实例传给 `app.use()`:  
```js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
createApp(App).use(router).mount('#app')
  1. 用路由组件:在 App.vue 里放 <router-view>(显示匹配的页面)和 <router-link>(导航链接):
    <template>
    <div>
     <router-link to="/">首页</router-link>
     <router-link to="/about">lt;/router-link>
     <router-view></router-view>
    </div>
    </template>

这样一配,点击导航就能切换页面啦~

问题3:<router-link> 用法和之前有啥不同?

v4 里 <router-link> 做了不少简化和增强:

  • 以前的 tag 属性没了,想自定义标签得用 custom 属性 + 插槽,比如想把 router-link 改成按钮:

    <router-link to="/" custom v-slot="{ navigate }">
    <button @click="navigate">首页</button>
    </router-link>
  • to 属性更灵活,支持对象形式,比如跳转到带查询参数的页面:

    <router-link :to="{ path: '/user', query: { id: 123 }}">用户页</router-link>

    这样 URL 会变成 /user?id=123

  • 激活样式的类名,默认还是 router-link-active,但现在可以通过 active-class 或者全局配置 linkActiveClass 自定义,比以前更灵活。

核心功能:动态路由、嵌套路由咋玩?

问题1:动态路由(比如用户ID页面)咋配置?

动态路由就是 URL 里带可变参数(比如用户ID),配置时在路由规则里用 标记参数,组件里用 useRoute 取参数。

举个用户详情页的例子:

  1. 配置路由规则:在 routes 数组里加一条:

    {
    path: '/user/:id', // :id 是动态参数
    component: () => import('../views/User.vue') // 这里用了懒加载,后面讲
    }
  2. 组件内取参数:在 User.vue 的 setup 里用 useRoute

    import { useRoute } from 'vue-router'

export default { setup() { const route = useRoute() // route.params.id URL 里的动态参数 console.log('当前用户ID:', route.params.id) return {} } }


3. **跳转动态路由**:用 `router.push` 时,可以传对象形式:  
```js
import { useRouter } from 'vue-router'
setup() {
  const router = useRouter()
  // 跳转到用户ID为100的页面
  const goToUser = () => {
    router.push({ path: '/user/100' })
    // 或者用命名路由(如果配了name的话):router.push({ name: 'user', params: { id: 100 } })
  }
  return { goToUser }
}

这样不管是从导航链接点进来,还是编程式跳转,都能拿到动态参数~

问题2:嵌套路由(子路由)该怎么设置?

嵌套路由常见于布局型页面,比如后台管理系统的“布局页”里嵌套“仪表盘”“设置”等子页面,关键是父路由里配 children 数组,并且父组件里要有 <router-view> 来显示子路由

举个后台布局的例子:

  1. 配置路由规则:父路由 /admin 下有两个子路由 dashboardsettings

    const routes = [
    {
     path: '/admin',
     component: AdminLayout, // 父布局组件
     children: [
       { path: '', redirect: 'dashboard' }, // 默认跳转到dashboard
       { path: 'dashboard', component: Dashboard },
       { path: 'settings', component: Settings }
     ]
    }
    ]

    注意子路由的 path 不加 ,会自动拼接父路由的 path,所以访问 /admin/dashboard 就能匹配到 Dashboard 组件。

  2. 父组件里放 <router-view>:在 AdminLayout.vue 里,除了侧边栏、顶栏,还要用 <router-view> 显示子页面:

    <template>
    <div class="admin-layout">
     <aside>侧边栏导航</aside>
     <main>
       <router-view></router-view> <!-- 子路由组件显示在这 -->
     </main>
    </div>
    </template>
  3. 子路由的导航:可以在父组件侧边栏用 <router-link> 跳转子路由:

    <aside>
    <router-link to="dashboard">仪表盘</router-link>
    <router-link to="settings">设置</router-link>
    </aside>

    这里 to 用相对路径(不加 ),会自动基于父路由 /admin 拼接。

这样嵌套结构就清晰啦,父布局负责框架,子路由负责具体页面内容~

问题3:路由懒加载在 vue-router4 里咋实现?

路由懒加载能让页面按需加载,减少首屏体积,v4 里实现特简单,把组件导入改成 () => import('组件路径') 这种动态导入形式,Webpack 之类的打包工具会自动拆分代码块。

对比一下普通导入和懒加载:

普通导入(首屏就加载所有组件):

import Home from '../views/Home.vue'
import About from '../views/About.vue'
const routes = [
  { path: '/', component: Home },
  { path: '/about', component: About }
]

懒加载(访问对应路由时才加载组件):

const routes = [
  { path: '/', component: () => import('../views/Home.vue') },
  { path: '/about', component: () => import('../views/About.vue') }
]

甚至还能给懒加载的组件取别名,方便调试:

{ 
  path: '/about', 
  component: () => import(/* webpackChunkName: "about" */ '../views/About.vue') 
}

这样打包后,About 组件会单独生成一个 about.[hash].js 的文件,访问 /about 时才会加载,首屏加载速度就上去啦~

路由守卫:导航控制咋搞?

问题1:全局守卫、组件内守卫在 v4 里咋用?

路由守卫用来控制导航流程(比如登录验证、权限判断),v4 里分全局守卫组件内守卫,写法和 v3 有变化,尤其是组件内守卫在组合式 API 里的用法。

全局守卫 直接在路由实例上挂钩子:

  • router.beforeEach:导航触发时最先执行,常用作登录拦截。

    router.beforeEach((to, from, next) => {
    // to: 要去的路由;from: 来自的路由;next: 放行函数(v4 里也支持 Promise 风格,next 可以省略,return 布尔值或路由对象)
    if (to.meta.requiresAuth && !isLogin()) {
      next('/login') // 没登录且需要权限,跳登录页
    } else {
      next() // 放行
    }
    })
  • router.beforeResolve:和 beforeEach 类似,但在组件内守卫之后、导航确认之前执行,适合做全局解析逻辑。

  • router.afterEach:导航完成后执行,常用作修改页面标题、埋点统计等。

    router.afterEach((to, from) => {
    document.title = to.meta.title || '默认标题'
    })

组件内守卫 在 v4 里分两种场景:

  • 选项式 API 里,还是用 beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave,用法和 v3 差不多。

  • 组合式 API(setup 里),得用 onBeforeRouteEnteronBeforeRouteUpdateonBeforeRouteLeave 这些钩子(从 vue-router 导入),比如在 setup 里处理离开守卫:

    import { onBeforeRouteLeave } from 'vue-router'

setup() { onBeforeRouteLeave((to, from) => { // 离开当前路由时的逻辑,比如提示“是否保存” return window.confirm('确定离开吗?未保存内容会丢失~') }) }

注意 `onBeforeRouteEnter` 执行时,组件实例还没创建,所以不能访问 `this`,如果要拿组件数据,得用回调里的 `next(vm => { ... })` 形式(但组合式 API 里更推荐用 `onMounted` 配合 `useRoute`)。  
#### 问题2:导航被取消(比如重复点同一路由)咋处理?  
v4 里导航是 **Promise 风格** 的,重复点击同一路由会触发 `AbortError`,比如用 `router.push` 时,如果导航被中断(比如守卫里阻止了),会返回一个被拒绝的 Promise。  
处理方式有两种:  
1. **try...catch 捕获错误**:在编程式导航时用 try...catch:  
```js
import { useRouter } from 'vue-router'
import { isNavigationFailure, NavigationFailureType } from 'vue-router'
setup() {
  const router = useRouter()
  const goToPage = async () => {
    try {
      await router.push('/same-path')
    } catch (error) {
      if (isNavigationFailure(error, NavigationFailureType.aborted)) {
        // 导航被取消,这里可以提示用户
        console.log('导航被取消啦~')
      }
    }
  }
  return { goToPage }
}
  1. 全局处理错误:用 router.onError 捕获所有路由错误:
    router.onError((error) => {
    console.log('路由错误:', error)
    // 可以统一跳转到错误页
    router.push('/error')
    })

这样就能优雅处理重复导航、守卫拦截等导致的导航失败啦~

问题3:路由元信息(meta)咋用?比如设置页面标题、权限?

路由元信息(meta)是给路由附加自定义信息的字段,比如页面标题、是否需要登录,配置和使用都很简单:

  1. 配置 meta:在路由规则里加 meta 对象:

    const routes = [
    {
     path: '/admin',
     component: Admin,
     meta: { 
       requiresAuth: true, // 需要登录
       title: '管理后台' // 页面标题
     }
    },
    {
     path: '/public',
     component: Public,
     meta: { 
       requiresAuth: false,
       title: '公共页面'
     }
    }
    ]
  2. 读取 meta:在全局守卫或组件内用 to.meta(全局守卫里)或 useRoute().meta(组件内),比如全局守卫里判断权限:

    router.beforeEach((to, from) => {
    if (to.meta.requiresAuth && !isLogin()) {
     return '/login' // 没登录且需要权限,跳登录页
    }
    })

    再比如组件内设置页面标题:

    import { useRoute, onMounted } from 'vue-router'

setup() { const route = useRoute() onMounted(() => { document.title = route.meta.title || '默认标题' }) }


meta 就像路由的“小背包”,想塞啥自定义信息都能塞,灵活得很~  
### 四、进阶与踩坑:生产部署、状态管理咋配合?
#### 问题1:vue-router4 怎么和 Pinia(或 Vuex)配合做权限控制?  
权限控制核心思路是:**在全局守卫里,用状态管理工具(Pinia/Vuex)里的用户信息,判断是否能进入目标路由**,以 Pinia 为例:  
1. **在 Pinia 里存用户权限**:比如有个 `userStore`,里面有 `userInfo.role` 表示用户角色。  
2. **全局守卫里做判断**:在 `router.beforeEach` 里导入 store,检查目标路由 meta 里的权限要求:  
```js
import { createRouter, createWebHistory } from 'vue-router'
import { useUserStore } from '../stores/user'
const router = createRouter({...})
router.beforeEach((to, from) => {
  const userStore = useUserStore()
  // 假设路由meta里有 role 字段,代表需要的角色
  if (to.meta.role && userStore.userInfo.role !== to.meta.role) {
    // 角色不匹配,跳403页面
    return '/403'
  }
  // 其他逻辑...
})
  1. 路由规则里配 meta.role:比如管理员页面需要 admin 角色:
    {
    path: '/admin',
    component: Admin,
    meta: { role: 'admin' }
    }

这样结合状态管理和路由 meta,就能实现细粒度的权限控制~如果用 Vuex,思路一样,只是取状态的方式换成 store.state.user.role 就行。

问题2:路由跳转时传参(query和params)有啥区别?

路由传参分 query(查询参数)和 params(路由参数),核心区别是是否在 URL 显式体现、刷新后是否保留

  • query

    • 会拼在 URL 后面,格式是 ?key=value/user?name=张三)。
    • 刷新页面后参数还在,因为存在 URL 里。
    • 配置路由时不需要在 path 里写,任何路由都能传。
    • 取值用 useRoute().query.name
  • params

    • 有两种情况:动态路由参数(在 path 里用 :id 定义)和 非动态路由的 params(路由规则里没定义,靠命名路由传)。
    • 动态路由参数会在 URL 里(/user/123),刷新保留;非动态路由的 params 不在 URL 里,刷新就丢了。
    • 动态路由参数取值用 useRoute().params.id;非动态的得用命名路由传参(router.push({ name: 'user', params: { id: 123 } })),但这种方式不推荐,

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门