Vue基础教程(164)使用Vue Router开发单页面应用之使用Vue Router:给Vue组件装上GPS:Vue Router让单页面应用不再迷路

当你点开某个App的菜单时,有没有发现页面虽然切换了,但浏览器居然没!刷!新!这背后就是Vue Router在悄悄当“隐形交通指挥官”。

一、为什么需要路由?先搞懂这个再写代码

想象一下,你去商场购物,如果每进一家店都要先回到大门口重新找方向,是不是很抓狂?传统多页面网站就是这样——每次跳转都要重新加载整个页面,用户体验堪比“逛街先出大门”。

而单页面应用(SPA)就像个大型商场,Vue Router就是那个室内导航APP:你从优衣库走到星巴克,实际一直在商场内部,只是换了不同区域而已。

三个必知概念:

  • 路由:哪条路能到目的地(URL与组件的映射关系)
  • 路由表:商场导航地图(所有路由的集合)
  • 路由器:带路的保安(管理所有路由规则的对象)

二、安装配置:给你的项目请个“导航员”

1. 安装(别说你没装)
npm install vue-router@4
2. 创建路由器(绘制导航地图)

src/router/index.js中:

import { createRouter, createWebHistory } from 'vue-router'
import Home from '@/views/Home.vue'
import About from '@/views/About.vue'

// 定义路由表:什么路径显示什么组件
const routes = [
  {
    path: '/',           // 网址路径
    name: 'Home',        // 路由名字(可选但推荐)
    component: Home      // 对应的组件
  },
  {
    path: '/about',
    name: 'About', 
    component: About
  }
]

// 创建路由器实例
const router = createRouter({
  history: createWebHistory(),  // 使用HTML5历史模式
  routes // 简写,等同于 routes: routes
})

export default router
3. 挂载到Vue实例(给商场配保安)

main.js中:

import { createApp } from 'vue'
import App from './App.vue'
import router from './router'

const app = createApp(App)
app.use(router)  // 关键!让整个App都能用路由
app.mount('#app')

三、核心玩法:路由的两种导航方式

方式1:声明式导航(贴路标)

在模板中使用<router-link>,相当于在商场里贴“由此前往洗手间”:

<template>
  <div>
    <!-- 相当于设置了导航的<a>标签 -->
    <router-link to="/">回家</router-link>
    <router-link to="/about">关于我们</router-link>
    
    <!-- 这里是组件显示区域,相当于商场的店铺位置 -->
    <router-view></router-view>
  </div>
</template>
方式2:编程式导航(手机导航APP)

在JavaScript代码中控制跳转:

// 在组件方法中
methods: {
  goHome() {
    this.$router.push('/')  // 跳转到首页
  },
  goAbout() {
    this.$router.push({ name: 'About' })  // 通过路由名跳转
  },
  goBack() {
    this.$router.back()  // 返回上一页
  }
}

实用技巧$router.push()相当于点击链接,会在历史记录留痕,浏览器后退键有效;而$router.replace()则是替换当前记录,不会产生新历史记录。

四、动态路由:给URL装上变量

当你要显示用户详情页时,不可能为每个用户写个路由吧?这时候就需要动态路由:

// router/index.js
{
  path: '/user/:id',  // 冒号开头表示参数
  name: 'User',
  component: UserDetail
}

在组件中获取参数:

<template>
  <div>
    <h2>用户详情页</h2>
    <p>当前用户ID:{{ $route.params.id }}</p>
  </div>
</template>
<script>
export default {
  mounted() {
    // 通过this.$route.params获取参数
    const userId = this.$route.params.id
    this.loadUserData(userId)
  },
  methods: {
    loadUserData(id) {
      // 根据ID请求用户数据
      console.log('加载用户数据:', id)
    }
  }
}
</script>

使用方式:

// 跳转到用户123的详情页
this.$router.push('/user/123')
// 或者
this.$router.push({ name: 'User', params: { id: 123 } })

五、路由守卫:给页面访问设权限

路由守卫就是你的“保安系统”,控制谁能进哪个区域:

1. 全局前置守卫(大门保安)
// router/index.js
router.beforeEach((to, from, next) => {
  // to: 要去的路由
  // from: 来自的路由  
  // next: 放行函数
  
  const isLoggedIn = checkLoginStatus() // 检查登录状态
  
  if (to.name === 'UserProfile' && !isLoggedIn) {
    // 如果要去个人页面但未登录,跳转到登录页
    next({ name: 'Login' })
  } else {
    next() // 正常放行
  }
})
2. 路由独享守卫(店铺保安)
{
  path: '/admin',
  component: Admin,
  beforeEnter: (to, from, next) => {
    // 只有管理员能进入
    if (user.role === 'admin') {
      next()
    } else {
      next('/403') // 跳转到无权限页面
    }
  }
}
3. 组件内守卫(店内工作人员)
export default {
  beforeRouteEnter(to, from, next) {
    // 在渲染该组件前调用
    next()
  },
  beforeRouteLeave(to, from, next) {
    // 在离开该组件时调用
    const answer = confirm('确定要离开吗?未保存的数据会丢失!')
    if (answer) {
      next()
    } else {
      next(false) // 取消导航
    }
  }
}

六、懒加载:让应用启动更快

如果所有页面都在一开始加载,应用会变得很慢。懒加载让你“需要时才加载”:

// 原来的写法:一开始就加载所有组件
// import Home from '@/views/Home.vue'

// 懒加载写法:访问时才加载
const Home = () => import('@/views/Home.vue')
const About = () => import('@/views/About.vue')

const routes = [
  {
    path: '/',
    name: 'Home', 
    component: Home  // 实际访问/时才加载Home组件
  }
]

七、完整示例:用户管理系统

下面是一个包含用户列表和详情的完整示例:

目录结构
src/
  ├── router/
  │   └── index.js
  ├── views/
  │   ├── Home.vue
  │   ├── UserList.vue
  │   └── UserDetail.vue
  └── App.vue
1. 路由配置 (router/index.js)
import { createRouter, createWebHistory } from 'vue-router'

// 懒加载组件
const Home = () => import('@/views/Home.vue')
const UserList = () => import('@/views/UserList.vue') 
const UserDetail = () => import('@/views/UserDetail.vue')

const routes = [
  { path: '/', name: 'Home', component: Home },
  { 
    path: '/users', 
    name: 'UserList', 
    component: UserList 
  },
  { 
    path: '/users/:id', 
    name: 'UserDetail', 
    component: UserDetail,
    props: true  // 将路由参数作为props传递
  }
]

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

// 简单的登录检查
router.beforeEach((to, from, next) => {
  console.log(`从 ${from.path} 跳转到 ${to.path}`)
  next()
})

export default router
2. 根组件 (App.vue)
<template>
  <div id="app">
    <nav class="navbar">
      <router-link to="/" class="nav-link">首页</router-link>
      <router-link to="/users" class="nav-link">用户列表</router-link>
    </nav>
    
    <main class="main-content">
      <router-view></router-view>
    </main>
  </div>
</template>
<style>
.navbar {
  background: #2c3e50;
  padding: 1rem;
}

.nav-link {
  color: white;
  text-decoration: none;
  margin-right: 1rem;
  padding: 0.5rem 1rem;
  border-radius: 4px;
}

.nav-link:hover {
  background: #34495e;
}

.router-link-active {
  background: #42b883;
}

.main-content {
  padding: 2rem;
}
</style>
3. 用户列表页面 (views/UserList.vue)
<template>
  <div>
    <h1>用户列表</h1>
    <ul class="user-list">
      <li 
        v-for="user in users" 
        :key="user.id"
        class="user-item"
        @click="viewUserDetail(user.id)"
      >
        {{ user.name }} - {{ user.email }}
      </li>
    </ul>
  </div>
</template>
<script>
export default {
  data() {
    return {
      users: [
        { id: 1, name: '张三', email: 'zhangsan@email.com' },
        { id: 2, name: '李四', email: 'lisi@email.com' },
        { id: 3, name: '王五', email: 'wangwu@email.com' }
      ]
    }
  },
  methods: {
    viewUserDetail(userId) {
      // 编程式导航到用户详情页
      this.$router.push({
        name: 'UserDetail',
        params: { id: userId }
      })
    }
  }
}
</script>
<style scoped>
.user-list {
  list-style: none;
  padding: 0;
}

.user-item {
  padding: 1rem;
  border: 1px solid #ddd;
  margin-bottom: 0.5rem;
  cursor: pointer;
  border-radius: 4px;
}

.user-item:hover {
  background: #f5f5f5;
}
</style>
4. 用户详情页 (views/UserDetail.vue)
<template>
  <div>
    <button @click="goBack" class="back-btn">← 返回</button>
    
    <div v-if="user" class="user-detail">
      <h1>用户详情</h1>
      <p><strong>ID:</strong>{{ user.id }}</p>
      <p><strong>姓名:</strong>{{ user.name }}</p>
      <p><strong>邮箱:</strong>{{ user.email }}</p>
    </div>
    
    <div v-else>
      <p>用户不存在</p>
    </div>
  </div>
</template>
<script>
export default {
  props: ['id'], // 通过props接收路由参数
  data() {
    return {
      user: null
    }
  },
  mounted() {
    this.loadUserData()
  },
  methods: {
    loadUserData() {
      // 模拟用户数据
      const users = {
        1: { id: 1, name: '张三', email: 'zhangsan@email.com' },
        2: { id: 2, name: '李四', email: 'lisi@email.com' },
        3: { id: 3, name: '王五', email: 'wangwu@email.com' }
      }
      
      this.user = users[this.id] || null
    },
    goBack() {
      this.$router.back()
    }
  }
}
</script>
<style scoped>
.back-btn {
  background: #6c757d;
  color: white;
  border: none;
  padding: 0.5rem 1rem;
  border-radius: 4px;
  cursor: pointer;
  margin-bottom: 1rem;
}

.back-btn:hover {
  background: #5a6268;
}

.user-detail {
  background: #f8f9fa;
  padding: 2rem;
  border-radius: 8px;
}
</style>

八、常见坑位提醒

  1. 路由不生效? 检查main.js里有没有app.use(router)
  2. 参数获取不到? 确认动态路由的冒号没写错,比如/user/:id不是/user/: id
  3. 编程导航无效? 确保用的是this.$router不是this.$route
  4. 组件不更新? 动态路由参数变化时,组件不会重新创建,需要在beforeRouteUpdate钩子中处理

结语

恭喜!现在你已经掌握了Vue Router这个“室内导航系统”。记住,路由不是目的,提升用户体验才是关键。多练习、多踩坑,很快你就能开发出丝滑流畅的单页面应用了。

路由就像恋爱——重要的不是终点,而是沿途的风景和那个不会刷新的页面。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

值引力

持续创作,多谢支持!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值