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

Vue Router是什么?怎么用?前端开发高频问题全解析

terry 10小时前 阅读数 12 #Vue

做Vue项目时,路由就像“导航员”,决定用户点按钮、输网址后该展示哪个页面,但刚上手Vue Router,不少同学会懵:“这东西到底咋配置?页面跳转咋做?遇到404咋处理?”今天用问答形式,把Vue Router从基础到实战的关键点掰碎了讲,不管是新手入门还是解决疑难杂症,看完心里有数~

Vue Router到底解决啥核心问题?

先想场景:做单页应用(SPA)时,页面切换不能像传统多页那样刷新整个页面(不然白做SPA了!),Vue Router就是干这个的——让SPA里的“页面切换”既流畅(不刷新),又能通过URL直观看到当前位置,还能实现权限控制、传参这些复杂逻辑

举个栗子:用户点“我的订单”,页面从首页滑到订单页,URL从/home/order,但浏览器没刷新,这就是Vue Router通过“前端路由”实现的,它背后维护了一套“URL路径 ↔ 组件”的映射规则,还能搞路由守卫(控制谁能进哪个页)、动态路由(比如用户ID不同,页面内容不同)这些高级操作。

零基础咋快速搭好Vue Router基础配置?

分5步走,拿Vue2项目举例(Vue3思路一样,API略有调整):

  1. 装依赖:终端执行npm install vue-router@3(Vue2配v3,Vue3配v4);
  2. 建路由文件:src下新建router文件夹,里面建index.js;
  3. 写路由规则:在index.js里导入Vue、VueRouter,再定义routes数组(每个对象存path和component)。
    import Vue from 'vue'  
    import VueRouter from 'vue-router'  
    import Home from '@/views/Home.vue'  
    import About from '@/views/About.vue'  
    

    Vue.use(VueRouter) // 注册插件

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

    const router = new VueRouter({ routes })
    export default router

  4. 挂到Vue根实例:打开main.js,导入router,加到new Vue({ router, ... })里;
  5. 放路由出口:在App.vue里加<router-view></router-view>,这个标签就是“页面展示的位置”,路由匹配到哪个组件,就把组件渲染到这。

做完这步,访问时显示Home组件,访问/about显示About组件,基础路由就跑通啦~

页面跳转有哪几种方式?该咋选?

声明式编程式两种,场景不同用法不同:

声明式:用<router-link>组件

就像写``标签,但更“Vue”,比如在导航栏写:<router-link to="/about">关于我们</router-link>,点击后,URL变,页面无刷新切换,还自动给激活的链接加.router-link-active类(方便做高亮)。

适合页面上的导航按钮、菜单这些“看得见的跳转”,不用写JS逻辑。

编程式:用this.$router的方法

在方法里写逻辑,比如点击按钮后判断用户是否登录,再决定跳不跳:

methods: {  
  goOrder() {  
    if (this.isLogin) {  
      this.$router.push('/order') // 跳转到订单页,新增一条历史记录  
    } else {  
      this.$router.push('/login') // 跳登录页  
    }  
  }  
}  

常用的还有replace(替换当前历史记录,比如支付成功后不想让用户返回支付页,就用replace)、go(类似`history.go`,this.$router.go(-1)`返回上一页)。

适合需要逻辑判断、用户操作后(比如表单提交成功)的跳转

简单跳转用声明式,复杂逻辑跳转用编程式~

动态路由和参数传递咋玩明白?

比如做“用户详情页”,每个用户ID不同,URL得是/user/123/user/456这种,这时候就得用动态路由

配置动态路由

在routes里加个带参数的path:

{ path: '/user/:id', component: User }  

这里的:id动态段”,组件里能拿到这个id。

组件内拿参数

在User组件里,通过this.$route.params.id获取。

export default {  
  created() {  
    console.log(this.$route.params.id) // 打印当前用户ID  
  }  
}  

如果是查询参数(比如/user?name=张三),就用this.$route.query.name

注意“组件复用”的坑

如果从/user/123跳到/user/456,因为用的同一个User组件,Vue为了性能会复用组件,created这些钩子不会再执行!这时候数据没更新就坑了。

解决办法有俩:

  • watch监听$route变化:
    watch: {  
        '$route'(to, from) {  
          // to是目标路由,from是当前路由  
          this.fetchData(to.params.id) // 重新发请求拿新用户数据  
        }  
      }  
      
  • 用组件内守卫beforeRouteUpdate
    beforeRouteUpdate(to, from, next) {  
        this.id = to.params.id  
        this.fetchData(this.id)  
        next() // 必须调用next放行  
      }  
      

嵌套路由咋设计更合理?

比如做后台管理系统,布局是“侧边栏 + 顶部栏 + 内容区”,内容区根据不同菜单切换页面,这时候就得用嵌套路由

配置children数组

在父路由的routes里加children,

{  
  path: '/admin',  
  component: AdminLayout, // 父组件,包含侧边栏、顶部栏  
  children: [  
    { path: 'dashboard', component: Dashboard }, // 子路由,URL是/admin/dashboard  
    { path: 'settings', component: Settings } // URL是/admin/settings  
  ]  
}  

注意子路由的path不加/,会自动拼接父路由的path(比如父是/admin,子path是dashboard,完整URL就是/admin/dashboard)。

父组件里放<router-view>

AdminLayout组件的模板里得有``,用来显示子组件:

<template>  
  <div class="admin-layout">  
    <aside>侧边栏</aside>  
    <main><router-view></router-view></main> // 子组件渲染在这  
  </div>  
</template>  

这样访问/admin时,先渲染AdminLayout,再根据子路由渲染对应的Dashboard或Settings,嵌套路由特别适合做“布局复用 + 子页面切换”的场景~

路由守卫有啥用?不同场景咋选?

路由守卫就像“门卫”,在路由跳转的各个阶段(比如跳之前、跳之后、进入组件前…)拦下来做验证、埋点、权限控制这些事,分三大类:

全局守卫:控制所有路由

比如在router/index.js里写:

router.beforeEach((to, from, next) => {  
  // to:要去的路由;from:从哪来的路由;next:放行/跳转到其他路由  
  if (to.path === '/pay' && !isLogin()) {  
    next('/login') // 没登录就跳登录页  
  } else {  
    next() // 放行  
  }  
})  

beforeEach是跳转前触发,适合做全局权限验证、页面标题设置(比如to.meta.title,设置document.title = to.meta.title),还有afterEach(跳转后触发,适合埋点统计)。

路由独享守卫:只拦单个路由

在routes里的某个路由对象上写beforeEnter

{  
  path: '/order',  
  component: Order,  
  beforeEnter: (to, from, next) => {  
    if (hasPermission()) {  
      next()  
    } else {  
      next('/403')  
    }  
  }  
}  

适合单个路由的特殊权限验证,比如订单页需要VIP权限。

组件内守卫:组件自己的“门卫”

在组件里写,

export default {  
  // 进入组件前触发(此时组件实例还没创建)  
  beforeRouteEnter(to, from, next) {  
    next(vm => {   
      // vm是组件实例,能访问this  
      vm.fetchData()   
    })  
  },  
  // 路由参数变化时触发(组件复用场景)  
  beforeRouteUpdate(to, from, next) {  
    this.id = to.params.id  
    next()  
  },  
  // 离开组件前触发(比如阻止表单未保存就跳转)  
  beforeRouteLeave(to, from, next) {  
    if (this.formDirty) {  
      if (confirm('表单没保存,确定离开?')) {  
        next()  
      } else {  
        next(false) // 取消跳转  
      }  
    } else {  
      next()  
    }  
  }  
}  

组件内守卫适合组件级的权限、数据预载、离开确认这些场景。

路由模式hash和history咋选?有啥坑?

Vue Router有两种路由模式,决定URL长啥样,还有兼容性:

hash模式(默认)

URL长这样:https://xxx.com/#/home,靠URL里的后面的内容变化来实现路由,优点是兼容性好(IE8都能跑),不用后端配置;缺点是URL带,不太美观。

history模式

URL长这样:https://xxx.com/home,用HTML5的history API(pushStatereplaceState)实现,优点是URL干净美观,像多页应用;缺点是刷新页面会404(因为后端没配置的话,服务器不认识/home这个路径,会返回404)。

咋选?

  • 如果项目是内部系统、对URL美观要求不高,用hash模式,省心;
  • 如果是面向用户的官网、需要SEO友好,选history模式,但得让后端配合:所有前端路由的请求,都重定向到index.html(比如Nginx配置try_files $uri $uri/ /index.html)。

路由懒加载咋实现?能解决啥痛点?

做SPA时,所有组件打包成一个JS文件,用户打开页面得下载超大文件,首屏加载巨慢!路由懒加载就是把每个路由对应的组件,分成单独的JS文件,用户访问哪个路由,再加载对应的文件,实现“按需加载”。

实现方法:用import()语法

把原来的静态导入(import Home from '@/views/Home.vue')改成动态导入:

{  
  path: '/',  
  component: () => import('@/views/Home.vue') // 打包时会把Home.vue单独拆成一个chunk  
}  

这样打包后,首页JS体积变小,用户打开首页时先加载小文件,等点其他路由时再加载对应组件的JS,首屏速度飞起~

注意:Vue Router官网推荐这种写法,Webpack会自动做代码分割,不用额外配置~

404页面咋配置?为啥要放最后?

用户访问不存在的路由时,要显示404页面,配置方法:

{  
  path: '/:pathMatch(.*)*', // Vue3写法,匹配所有未定义的路由  
  // Vue2写法:path: '*'  
  component: Error404  
}  

必须把404路由放在routes数组的最后面!因为Vue Router的路由匹配是“从上到下”遍历的,前面的路由如果匹配到了,就不会往后走,要是把404放前面,不管访问啥都会匹配到404,就坑了。

举个错误例子:

const routes = [  
  { path: '*', component: Error404 }, // 错误!放前面了  
  { path: '/home', component: Home }  
]  

这时候访问/home,会匹配到,显示404,所以正确做法是把404放最后:

const routes = [  
  { path: '/home', component: Home },  
  { path: '/about', component: About },  
  { path: '/:pathMatch(.*)*', component: Error404 } // 正确,放最后  
]  

路由和Vuex需要联动吗?哪些场景要配合?

Vuex是状态管理,路由是页面导航,俩工具经常配合干活:

权限控制

比如用户登录状态存在Vuex的state里,全局守卫beforeEach里判断:

router.beforeEach((to, from, next) => {  
  const isLogin = store.state.user.isLogin  
  if (to.meta.requiresAuth && !isLogin) {  
    next('/login')  
  } else {  
    next()  
  }  
})  

这里to.meta.requiresAuth是路由元信息,用来标记哪些路由需要登录(比如订单页、个人中心)。

路由参数触发数据更新

路由参数变化时(比如用户从/user/1跳到/user/2),组件里通过Vuex的action更新数据:

watch: {  
  '$route'(to) {  
    this.$store.dispatch('user/fetchUser', to.params.id)  
  }  
}  

设置页面标题

路由元信息里存title,全局守卫afterEach里设置document.title

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

这样不同路由切换时,页面标题自动变,体验更像原生App~

移动端路由切换动画咋实现?

给路由切换加动画,让页面跳转更丝滑,核心是用Vue的``包裹``,配合CSS过渡。

版权声明

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

发表评论:

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

热门