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

Vue2路由怎么学?从基础到实战常见问题全解答

terry 1天前 阅读数 18 #Vue
文章标签 Vue2 路由

想学Vue2路由却一头雾水?路由配置总出错、传参老丢数据、导航守卫不知咋用…这些问题是不是让你抓耳挠腮?别慌!这篇文章把Vue2路由从基础到实战的常见问题拆碎了讲,不管是刚入门的小白,还是想解决实战难题的开发者,看完都能通透不少~

Vue2路由基础:先搞懂“路由是干啥的”

啥是Vue2路由?和单页应用有啥关系?
Vue路由(vue-router)是Vue官方的路由管理器,专门帮单页应用(SPA)实现“无刷新页面跳转”,单页应用只有一个HTML页面,靠切换不同组件模拟多页效果,路由就是负责管理“哪个路径该显示哪个组件、怎么跳转”的工具,比如做个博客,点击“首页”“文章列表”“关于我”时,页面不用整页刷新,只是切换对应的组件,这就是路由在背后“牵线搭桥”~

路由模式有hash和history,区别在哪?
Vue2路由默认是hash模式,URL里带(比如http://xxx/#/home),后面的路径变化不会让浏览器真的请求服务器;history模式则是用HTML5的History API,URL更干净(像http://xxx/home),但部署时得配置服务器(否则直接访问子路径会404),要是做内部系统,hash模式够稳;要是对外的官网,想要好看的URL,就用history模式,不过得配合后端配置重定向~

路由里的“路由映射”“路由组件”是啥意思?
路由映射就是“URL路径和组件的绑定规则”,比如配置{ path: '/about', component: About },意思是用户访问/about时,渲染About组件,这里的About就是路由组件,它会被渲染到<router-view>这个“占位容器”里,简单说,路由映射是“规则”,路由组件是“内容”,<router-view>是“展示容器”~

路由配置入门:从安装到写第一个路由

咋在Vue2项目里装vue-router
如果用Vue CLI创建项目,选“Manually select features”时把Router勾上,CLI会自动装依赖并生成路由模板,要是手动装,先通过包管理工具安装:npm install vue-router@3(因为Vue2对应vue-router 3.x,Vue3是x),然后在src里新建router文件夹,写配置文件~

基本的路由配置文件长啥样?
一般在src/router/index.js里写核心配置:

import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/components/Home'   // 引入路由组件
import About from '@/components/About'
Vue.use(Router)  // 全局注册路由插件
export default new Router({
  routes: [  // 路由规则数组
    {
      path: '/',         // 匹配根路径
      name: 'Home',      // 路由名称(可选,方便编程式导航)
      component: Home    // 对应组件
    },
    {
      path: '/about',
      name: 'About',
      component: About
    }
  ]
})

接着在main.js里把路由实例注入Vue根实例:

import Vue from 'vue'
import App from './App.vue'
import router from './router'  // 引入路由配置
new Vue({
  router,  // 注入路由,让所有组件能访问$router/$route
  render: h => h(App)
}).$mount('#app')

最后在App.vue里加<router-view></router-view>当“组件容器”,用<router-link to="/">首页</router-link>做跳转链接~

路由组件必须放components文件夹吗?
不是必须!这只是“约定俗成的规范”,方便团队协作和代码管理,你可以按功能分文件夹(比如pages放路由组件、components放通用组件),只要在配置路由时,正确引入组件路径就行,灵活调整~

路由传参:三种方式优缺点+避坑

路由传参有哪几种方式?分别咋用?
主要三种方式,各有适用场景:

  • query传参:像GET请求带参数,URL里显示?id=1&name=xxx,跳转时用 <router-link :to="{ path: '/detail', query: { id: 1 }}">,或编程式导航 this.$router.push({ path: '/detail', query: { id: 1 }});接收参数用 this.$route.query.id
  • params传参:参数“藏”在路由路径里(配合name跳转时,URL不显示参数),需先在路由配置定义动态路由(比如{ path: '/detail/:id', name: 'Detail', component: Detail }),跳转用 <router-link :to="{ name: 'Detail', params: { id: 1 }}">this.$router.push({ name: 'Detail', params: { id: 1 }}),接收用 this.$route.params.id
  • 动态路由参数:本质是params的一种,路径里写/:id,参数“必须传”(否则匹配不到路由),适合商品详情页这类“必须有ID”的场景~

queryparams传参区别是啥?
最直观的是URL表现query参数在URL里可见,刷新页面参数还在;params如果用name跳转,参数不在URL里,刷新会丢数据(除非把参数写进路由路径),用path跳转时,params会被忽略,所以params传参一定要用name

传参后页面刷新数据没了咋整?
如果是params传参没配置到路由路径里,刷新肯定丢数据,这时候要么把参数放到query里,要么在路由配置里把参数写进path(比如/detail/:id/:name),还可以在组件的activated钩子(路由组件被缓存时,created不触发)里重新取参数,或者用Vuex、localStorage临时存数据~

导航守卫:控制路由跳转的“门卫”

导航守卫是干啥的?为啥需要它?
导航守卫能在“路由跳转前、跳转中、跳转后”插入逻辑,比如判断用户是否登录、加载进度条、设置页面标题,举个例子:用户要进个人中心,得先验证登录状态,没登录就跳登录页——这就得靠导航守卫“拦一下”~

全局守卫、路由独享守卫、组件内守卫有啥区别?

  • 全局守卫:作用于所有路由,比如router.beforeEach((to, from, next) => { ... }),每次路由跳转前都触发。
  • 路由独享守卫:只作用于某个路由,在路由配置里写beforeEnter: (to, from, next) => { ... },比如某个敏感页面,单独做权限校验。
  • 组件内守卫:在路由组件里写,比如beforeRouteEnter(组件创建前触发)、beforeRouteUpdate(路由参数变了但组件复用触发)、beforeRouteLeave(离开组件前触发),适合组件内自己的逻辑(比如离开编辑页提示“是否保存”)~

咋用导航守卫实现登录拦截?
全局前置守卫router.beforeEach里判断登录状态:

router.beforeEach((to, from, next) => {
  const isLogin = localStorage.getItem('token')  // 假设用token判断登录
  if (to.meta.requiresAuth) {  // 路由元信息标记“是否需要登录”
    if (isLogin) {
      next()  // 登录了,放行
    } else {
      next('/login')  // 没登录,跳登录页
    }
  } else {
    next()  // 不需要登录的页面,直接放行
  }
})

然后在路由配置里,给需要登录的页面加meta标记:

{
  path: '/profile',
  name: 'Profile',
  component: Profile,
  meta: { requiresAuth: true }  // 标记该页面需要登录
}

这样用户访问/profile时,就会被守卫拦截验证~

动态路由与嵌套路由:应对复杂页面结构

动态路由咋配置?适合啥场景?
动态路由就是“路径里带变量”,比如商品详情页/goods/:id,用户点击不同商品,id变化但组件复用,配置时在routes数组里写:

{ 
  path: '/goods/:id', 
  component: GoodsDetail 
}

组件里用this.$route.params.id拿参数,适合详情页、订单页这类“根据不同ID展示内容”的场景~

动态路由匹配不到咋处理?
如果用户输入了不存在的id(比如/goods/999但系统没这个商品),可以配置通配符路由“兜底”:

{
  path: '*',  // 匹配所有未定义的路径
  component: NotFound  // 404组件
}

注意要把通配符路由放在所有路由规则的最后(因为路由匹配是“从上到下”的,通配符匹配所有,放前面会挡住其他路由)~

嵌套路由是啥?啥时候用?
嵌套路由是“路由里套路由”,比如后台管理系统的layout(侧边栏+主内容区)区要根据不同路由切换组件,配置时用children数组:

{
  path: '/admin',
  component: AdminLayout,  // 父组件,包含<router-view>
  children: [
    { path: 'dashboard', component: Dashboard },  // 子路由,路径是/admin/dashboard
    { path: 'settings', component: Settings }
  ]
}

父组件AdminLayout里得有<router-view>来渲染子组件,适合有“公共布局(比如头部、侧边栏)”的页面结构~

路由懒加载:让项目加载更快的秘密

路由懒加载是啥?有啥好处?
路由懒加载是把“路由对应的组件”打包成单独的JS文件,用户访问时再加载,而不是一开始全加载,好处是首屏加载更快(减少初始bundle体积)、按需加载(用户没访问的页面,暂时不加载代码),比如用户只访问首页,就不用加载“关于页、详情页”的代码~

咋实现路由懒加载?
用ES6的import()语法,把原来的“静态引入”改成“动态引入”:

// 原来的静态引入(全量打包)
import About from '@/components/About'
// 改成懒加载(单独打包,访问时加载)
const About = () => import('@/components/About')
// 路由配置里直接用
{
  path: '/about',
  component: About
}

这样About组件会被单独打包,只有访问/about时才加载~

能和UI库的按需加载结合吗?
比如Element UI的按需加载,配合路由懒加载,能进一步减小体积,在babel.config.js里配置按需加载插件后,在路由组件里只引入需要的UI组件:

import { Button, Table } from 'element-ui'  // 只加载Button和Table,不加载整个UI库

这样每个路由组件只加载“用到的UI组件”,性能更优~

路由错误处理:解决跳转崩溃、404等问题

路由跳转时出错咋捕获?
this.$router.push跳转时,若路径错误或有其他异常,会抛出错误,可以用try...catch包裹:

try {
  await this.$router.push('/wrong-path')
} catch (error) {
  console.log('路由跳转出错:', error)
  this.$router.push('/404')  // 跳转到404页面
}

或者在全局的router.onError里捕获“路由加载时的错误”(比如懒加载组件加载失败):

router.onError((error) => {
  console.log('路由加载错误:', error)
  // 处理错误(比如跳转到错误页面)
})

404页面咋配置?
在路由规则最后加通配符路由

{
  path: '*',
  name: 'NotFound',
  component: () => import('@/components/NotFound')  // 懒加载404组件
}

注意要放在所有路由规则的最后,否则会“拦截”其他合法路由~

重复点击路由链接报错咋解决?
Vue2路由在“重复点击同一路由”时,会抛出NavigationDuplicated错误,可以在router/index.js里全局捕获:

const originalPush = Router.prototype.push
Router.prototype.push = function push(location) {
  return originalPush.call(this, location).catch(err => err)
}

这样重复点击时错误会被捕获,不影响页面~

实战避坑:那些教程没讲的“阴间”问题

路由跳转后页面不更新是咋回事?
因为路由参数变了,但组件复用了(比如从/goods/1跳到/goods/2),这时候组件的createdmounted不会重新执行,解决方法:

  • watch监听$route变化:
    watch: {
      '$route'(to, from) {
        this.fetchData(to.params.id)  // 路由变化后,重新获取数据
      }
    }
  • 用组件内守卫beforeRouteUpdate
    beforeRouteUpdate(to, from, next) {
      this.id = to.params.id
      this.fetchData(this.id)
      next()
    }

路由守卫里this指向不对咋办?
比如在全局守卫router.beforeEach里,this默认不是Vue实例(因为守卫是挂载在路由实例上,不是组件内),如果要访问Vuex或全局方法,得用router.app(路由实例的app属性是Vue根实例):

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

移动端路由切换动画咋实现?
用Vue的过渡动画配合路由,在<router-view>外面包<transition>,并设置name

<transition name="slide-left">
  <router-view></router-view>
</transition>

然后写CSS动画:

.slide-left-enter-active,
.slide-left-leave-active {
  transition: all 0.3s ease;
}
.slide-left-enter,
.slide-left-leave-to {
  transform: translateX(100%);
  opacity: 0;
}

这样路由切换时就有“滑动动画”啦~

多路由出口咋处理?
页面需要多个<router-view>时(比如侧边栏和内容区分别渲染不同组件),用命名视图

{
  path: '/',
  components: {
    default: Home,        // 对应未命名的<router-view>
    sidebar: Sidebar     // 对应<router-view name="sidebar">
  }
}

模板里这样写:

<router-view></router-view>           <!-- 渲染Home -->
<router-view name="sidebar"></router-view>  <!-- 渲染Sidebar -->

学Vue2路由,核心是理解“路径→组件→导航”的联动逻辑,再结合实战踩坑、优化性能,才能真正用顺,现在再回头看开头的问题,是不是觉得清晰多了?把这些知识点拆成小任务(比如先配个简单路由,再玩传参,接着搞守卫),逐步推进,你会发现路由其实没那么难~要是练手的话,不妨做个带“登录、详情页、嵌套布局”的小项目,把这些技巧全用上,绝对能吃透!

版权声明

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

发表评论:

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

热门