第7章 路由管理

第21节 路由高级特性

7.21.1 路由元信息与过渡动画

路由元信息

路由元信息是Vue Router提供的一种机制,允许我们在路由配置中添加自定义数据,用于权限控制、页面标题设置、动画效果等场景。

// router/index.js
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue'),
    meta: {
      title: '首页',
      requiresAuth: false,
      layout: 'default',
      transition: 'fade'
    }
  },
  {
    path: '/admin',
    name: 'Admin',
    component: () => import('../views/Admin.vue'),
    meta: {
      title: '后台管理',
      requiresAuth: true,
      roles: ['admin'],
      layout: 'admin',
      transition: 'slide'
    }
  }
]

在导航守卫中访问路由元信息:

router.beforeEach((to, from, next) => {
  // 设置页面标题
  document.title = to.meta.title || '默认标题'
  
  // 权限验证
  const isAuthenticated = localStorage.getItem('token')
  const userRole = localStorage.getItem('role')
  
  if (to.meta.requiresAuth) {
    if (!isAuthenticated) {
      next('/login')
    } else if (to.meta.roles && !to.meta.roles.includes(userRole)) {
      next('/403')
    } else {
      next()
    }
  } else {
    next()
  }
})

路由过渡动画

Vue Router与Vue的<transition>组件结合,可以实现页面切换时的过渡动画效果。

基本过渡效果
<!-- App.vue -->
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link>
      <router-link to="/about">关于</router-link>
    </nav>
    
    <!-- 路由过渡动画 -->
    <transition name="fade" mode="out-in">
      <router-view />
    </transition>
  </div>
</template>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}
</style>
基于路由元信息的动态过渡
<!-- App.vue -->
<template>
  <div id="app">
    <nav>
      <router-link to="/">首页</router-link>
      <router-link to="/about">关于</router-link>
      <router-link to="/admin">后台</router-link>
    </nav>
    
    <!-- 基于路由元信息的动态过渡 -->
    <transition 
      :name="transitionName" 
      mode="out-in"
    >
      <router-view />
    </transition>
  </div>
</template>

<script>
export default {
  data() {
    return {
      transitionName: 'fade'
    }
  },
  watch: {
    $route(to, from) {
      // 从路由元信息中获取过渡名称
      this.transitionName = to.meta.transition || 'fade'
    }
  }
}
</script>

<style>
/* 淡入淡出效果 */
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.3s ease;
}

.fade-enter-from,
.fade-leave-to {
  opacity: 0;
}

/* 滑动效果 */
.slide-enter-active,
.slide-leave-active {
  transition: all 0.3s ease;
}

.slide-enter-from {
  transform: translateX(100%);
}

.slide-leave-to {
  transform: translateX(-100%);
}
</style>

7.21.2 路由懒加载与代码分割

路由懒加载是一种优化技术,它允许我们将不同路由对应的组件分割成不同的代码块,只有当路由被访问时才会加载对应的组件代码。

基本懒加载实现

// router/index.js
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import('../views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import('../views/About.vue')
  },
  {
    path: '/admin',
    name: 'Admin',
    component: () => import('../views/Admin.vue')
  }
]

命名代码块

我们可以为懒加载的代码块指定名称,以便在打包时更好地识别和管理:

const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import(/* webpackChunkName: "home" */ '../views/Home.vue')
  },
  {
    path: '/about',
    name: 'About',
    component: () => import(/* webpackChunkName: "about" */ '../views/About.vue')
  },
  {
    path: '/admin',
    name: 'Admin',
    component: () => import(/* webpackChunkName: "admin" */ '../views/Admin.vue')
  }
]

组件懒加载

除了路由级别的懒加载,我们还可以在组件内部实现组件级别的懒加载:

// 在组件中懒加载其他组件
const HeavyComponent = defineAsyncComponent(() => 
  import(/* webpackChunkName: "heavy" */ './HeavyComponent.vue')
)

export default {
  components: {
    HeavyComponent
  }
}

预加载策略

我们可以使用&lt;link rel=&quot;prefetch&quot;&gt;或Vue Router的预加载功能来优化用户体验:

// 预加载所有路由组件
const routes = [
  {
    path: '/',
    name: 'Home',
    component: () => import(/* webpackPrefetch: true */ '../views/Home.vue')
  }
]

7.21.3 滚动行为控制

Vue Router允许我们自定义路由切换时的滚动行为,控制页面滚动到指定位置。

基本滚动行为配置

// router/index.js
const router = createRouter({
  history: createWebHistory(),
  routes,
  // 滚动行为配置
  scrollBehavior(to, from, savedPosition) {
    // 如果有保存的位置,恢复到该位置(如浏览器后退/前进)
    if (savedPosition) {
      return savedPosition
    } else {
      // 否则滚动到顶部
      return { top: 0 }
    }
  }
})

滚动到指定元素

scrollBehavior(to, from, savedPosition) {
  // 如果路由中指定了#hash,滚动到对应元素
  if (to.hash) {
    return {
      el: to.hash,
      behavior: 'smooth' // 平滑滚动
    }
  } else if (savedPosition) {
    return savedPosition
  } else {
    return { top: 0 }
  }
}

基于路由元信息的滚动行为

scrollBehavior(to, from, savedPosition) {
  // 从路由元信息中获取滚动位置
  if (to.meta.scrollTo) {
    return to.meta.scrollTo
  } else if (savedPosition) {
    return savedPosition
  } else {
    return { top: 0 }
  }
}

// 路由配置中使用
const routes = [
  {
    path: '/long-page',
    name: 'LongPage',
    component: () => import('../views/LongPage.vue'),
    meta: {
      scrollTo: { top: 0, behavior: 'smooth' }
    }
  }
]

7.21.4 路由模式:Hash vs History

Vue Router支持两种路由模式:Hash模式和History模式。

Hash模式

Hash模式是Vue Router的默认模式,它使用URL的哈希值(#)来模拟路由,不需要服务器配置。

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

特点

  • URL中包含#符号,如:http://example.com/#/home
  • 不要求服务器配置,兼容性好
  • 刷新页面不会404
  • SEO不友好

History模式

History模式使用HTML5的History API来实现路由,URL更加美观,没有#符号。

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

特点

  • URL更加美观,如:http://example.com/home
  • SEO友好
  • 要求服务器配置,否则刷新页面会404
  • 兼容性要求较高(IE10+)

服务器配置

使用History模式时,需要服务器配置来处理所有请求,将它们重定向到index.html。

Nginx配置示例

server {
  listen 80;
  server_name example.com;
  root /path/to/your/app;
  
  location / {
    try_files $uri $uri/ /index.html;
  }
}

Apache配置示例

<IfModule mod_rewrite.c>
  RewriteEngine On
  RewriteBase /
  RewriteRule ^index\.html$ - [L]
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteRule . /index.html [L]
</IfModule>

Node.js (Express)配置示例

const express = require('express')
const path = require('path')
const app = express()

app.use(express.static(path.join(__dirname, 'dist')))

app.get('*', (req, res) => {
  res.sendFile(path.join(__dirname, 'dist', 'index.html'))
})

app.listen(3000, () => {
  console.log('Server is running on port 3000')
})

最佳实践与注意事项

  1. 路由元信息的合理使用

    • 不要在元信息中存储过多数据,只存储与路由相关的配置
    • 元信息适合存储权限、标题、布局、过渡效果等配置
  2. 路由懒加载的最佳实践

    • 对所有路由组件使用懒加载,提高初始加载速度
    • 合理划分代码块,避免过大的单块代码
    • 对不常用的路由组件考虑预加载
  3. 滚动行为的设计

    • 大多数情况下,滚动到顶部是最佳体验
    • 对长列表页面,可以考虑保持滚动位置
    • 使用平滑滚动提升用户体验
  4. 路由模式的选择

    • 开发环境:推荐使用History模式,URL更美观
    • 生产环境:
      • 如果服务器支持,优先选择History模式
      • 否则使用Hash模式
      • 考虑SEO需求,History模式更友好

小结

本节我们学习了Vue Router的高级特性,包括:

  • 路由元信息的定义和使用
  • 基于路由的过渡动画
  • 路由懒加载与代码分割
  • 滚动行为控制
  • Hash模式与History模式的对比

这些高级特性可以帮助我们构建更加优化、用户体验更好的单页应用。合理使用这些特性,可以提高应用的性能、SEO友好性和用户体验。

思考与练习

  1. 实现一个基于路由元信息的权限管理系统,不同角色可以访问不同的路由。
  2. 为不同路由配置不同的过渡动画效果。
  3. 使用路由懒加载优化应用性能,并查看打包后的代码分割情况。
  4. 配置滚动行为,实现页面切换时的平滑滚动效果。
  5. 对比Hash模式和History模式,尝试在本地服务器上配置History模式。
« 上一篇 19-programmatic-navigation-guards 下一篇 » 21-pinia-basics