3.2Vue Router路由导航

<router-link> 会根据当前路由($route)与 to 属性的匹配情况,自动添加以下 CSS 类:

  • 触发条件 :当链接对应的路由 被包含在当前路由中 时添加。
  • 用途 :适用于 嵌套路由 的场景。
  • 注意 :即使当前路径是 /users/123/users 的链接也会被激活。
  • 触发条件 :当链接对应的路由 与当前路由完全精确匹配 时添加。
  • 用途 :适用于需要 精确匹配 的场景,避免父级路由在子路由激活时也被高亮。

router-link-exact-activerouter-link-active 的子集。也就是说,一个被精确匹配的链接,同时拥有两个类

3. 修改默认样式类名

  • 全局配置 Vue Router 实例

    复制代码
    import Vue from 'vue';
    import Router from 'vue-router';
    
    Vue.use(Router);
    
    const router = new Router({
      mode: 'history',
      base: process.env.BASE_URL,
      routes: [
        // ... 路由配置
      ],
      linkActiveClass: 'my-active-class',       // 修改默认的 active class
      linkExactActiveClass: 'my-exact-active-class' // 修改默认的 exact active class
    });
    
    export default router;
  • 在单个 <router-link> 上指定自定义类名

    复制代码
    <router-link 
      to="/about" 
      active-class="my-active-class" 
      exact-active-class="my-exact-active-class"
    >
      About Us
    </router-link>

    路由导航传参:

声明式导航:<router-link>

<router-link> 是 Vue Router 提供的一个组件,用于在模板中创建可点击的链接,从而触发路由跳转。

基本用法

最简单的形式是直接指定一个 to 属性来定义目标路径:

复制代码
<router-link to="/">Go to Home</router-link>

这会生成一个指向根路径 / 的链接,当用户点击时会导航到对应的视图。

主要属性
  • to (必需): 指定目标路由的位置。可以是一个字符串或一个描述目标位置的对象。

    复制代码
    <!-- 字符串 -->
    <router-link to="home">Home</router-link>
    
    <!-- 使用对象 -->
    <router-link :to="{ name: 'user', params: { userId: 123 }}">User</router-link>
    <!-- 使用字符串路径(原对象形式使用 name 和 params)-->
    <router-link to="/user/123">User</router-link>
    
    
    <!-- 带查询参数 -->
    <router-link :to="{ path: 'register', query: { plan: 'private' }}">Register</router-link>
    <!-- 带查询参数的字符串形式(原对象形式使用 path 和 query)-->
    <router-link to="/register?plan=private">Register</router-link>
  • replace : 如果设置为 true,则导航不会留下历史记录,也就是说点击后退按钮不会回到这个页面。

    复制代码
    <router-link :to="{ path: '/abc'}" replace>Go to ABC</router-link>
  • append : 设置为 true 时,导航时会在当前路径前追加路径,而不是替换整个路径。

    复制代码
    <router-link :to="{ path: 'relative/path'}" append>Relative Path</router-link>
  • tag : 自定义渲染的标签名,默认为 <a> 标签。

    复制代码
    <router-link to="/foo" tag="li">Foo</router-link>
    <!-- 渲染为 <li>Foo</li> -->

编程式导航详解

1. $router.push()

push() 方法用于导航到新的路由,会向浏览器历史记录栈添加一条新记录。

使用字符串路径

复制代码
// 字符串路径
this.$router.push('/home')

// 带查询参数,结果为 /register?plan=private
this.$router.push('/register?plan=private')
// 或者使用对象形式
this.$router.push({ path: '/register', query: { plan: 'private' }})

查询参数

query)是以键值对的形式附加在 URL 的末尾,前面有一个问号 ? 分隔符。如果有多个查询参数,则用 & 符号连接。

复制代码
http://example.com/user?id=123&name=john

在这个例子中,id=123name=john 就是两个查询参数。

params 通常与 RESTful 风格的路由设计紧密相关.

复制代码
/users          # 表示所有用户这个资源集合
/users/123      # 表示 ID 为 123 的单个用户资源
/users/123/posts # 表示 ID 为 123 的用户的博客文章集合
使用路径 path
复制代码
// 使用 path
this.$router.push({ path: '/user' })

// path + params 不起作用,params 会被忽略
// ❌ 错误:/user 不会包含 params
this.$router.push({ path: '/user', params: { userId: 123 }})

// ✅ 正确:使用 query
this.$router.push({ path: '/user', query: { userId: 123 }})
// 结果:/user?userId=123
使用路由名称 name
复制代码
// 使用 name 和 params
this.$router.push({ name: 'user', params: { userId: 123 }})
// 结果:/user/123 (假设路由配置为 /user/:userId)

// 使用 name 和 query
this.$router.push({ name: 'user', query: { id: 123 }})
// 结果:/user?id=123

2. $router.replace()

replace() 方法与 push() 类似,但不会向 history 添加新记录,而是替换当前记录。

复制代码
// 替换当前路由
this.$router.replace('/home')
this.$router.replace({ path: '/home' })
this.$router.replace({ name: 'user', params: { userId: 123 }})

3. $router.go()

在 history 记录中前进或后退指定步数。

复制代码
// 后退一步
this.$router.go(-1)

// 前进一步
this.$router.go(1)

// 前进三步
this.$router.go(3)

// 如果没有足够的记录,会失败且不报错
this.$router.go(-100)
this.$router.go(100)

路由传参详解

1. params 传参

路由配置
复制代码
const routes = [
  {
    path: '/user/:userId',
    name: 'user',
    component: User
  },
  {
    path: '/user/:userId/post/:postId',
    name: 'user-post',
    component: UserPost
  }
]
传递 params
复制代码
// 使用 name + params
this.$router.push({ 
  name: 'user', 
  params: { userId: 123 } 
})

// 传递多个参数
this.$router.push({ 
  name: 'user-post', 
  params: { userId: 123, postId: 456 } 
})

// 声明式导航
<router-link :to="{ name: 'user', params: { userId: 123 }}">用户</router-link>
接收 params
复制代码
// 在目标组件中
export default {
  created() {
    console.log(this.$route.params.userId) // 123
    console.log(this.$route.params.postId) // 456
  }
}

2. query 传参

传递 query
复制代码
// 使用 path + query
this.$router.push({ 
  path: '/user', 
  query: { userId: 123, name: 'john' } 
})

// 使用 name + query
this.$router.push({ 
  name: 'user', 
  query: { userId: 123, name: 'john' } 
})

// 声明式导航
<router-link :to="{ path: '/user', query: { userId: 123 }}">用户</router-link>
<router-link :to="'/user?userId=123'">用户</router-link>
接收 query
复制代码
// 在目标组件中
export default {
  created() {
    console.log(this.$route.query.userId) // 123
    console.log(this.$route.query.name)   // john
  }
}

完整案例

1. 路由配置

复制代码
// router/index.js
import Vue from 'vue'
import Router from 'vue-router'
import Home from '@/views/Home.vue'
import User from '@/views/User.vue'
import UserProfile from '@/views/UserProfile.vue'
import UserPosts from '@/views/UserPosts.vue'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'home',
      component: Home
    },
    {
      path: '/user',
      component: User,
      children: [
        {
          path: ':id',
          name: 'user-profile',
          component: UserProfile,
          props: true // 将路由参数作为组件属性传递
        },
        {
          path: ':id/posts',
          name: 'user-posts',
          component: UserPosts
        }
      ]
    }
  ]
})

2. 导航示例

复制代码
// Home.vue
export default {
  methods: {
    // 使用 path 导航
    goToUserByPath() {
      this.$router.push('/user/123')
    },

    // 使用 path + query
    goToUserWithQuery() {
      this.$router.push({
        path: '/user/123',
        query: { tab: 'profile', lang: 'zh' }
      })
    },

    // 使用 name + params
    goToUserByName() {
      this.$router.push({
        name: 'user-profile',
        params: { id: 123 }
      })
    },

    // 使用 name + params + query
    goToUserWithParamsAndQuery() {
      this.$router.push({
        name: 'user-posts',
        params: { id: 123 },
        query: { page: 1, sort: 'date' }
      })
    },

    // 替换导航
    replaceToUser() {
      this.$router.replace({
        name: 'user-profile',
        params: { id: 123 }
      })
    }
  }
}

3. 接收参数

复制代码
// UserProfile.vue
export default {
  // 使用 props 接收参数
  props: ['id'],
  
  created() {
    // 通过 $route 接收 params
    console.log('User ID:', this.$route.params.id)
    
    // 通过 $route 接收 query
    console.log('Tab:', this.$route.query.tab)
    console.log('Language:', this.$route.query.lang)
    
    // 使用 props 接收(推荐)
    console.log('Props ID:', this.id)
  },
  
  watch: {
    // 监听路由变化(适用于同一个组件内不同参数的切换)
    '$route'(to, from) {
      if (to.params.id !== from.params.id) {
        this.fetchUserData(to.params.id)
      }
    }
  },
  methods: {
    fetchUserData(id) {
      // 根据新的用户ID获取数据
    }
  }
}

重要注意事项

  1. path 与 params 的限制

    • 当使用 path 时,params 会被忽略
    • 需要使用 query 来传递参数
  2. name 与 params 的优势

    • 更安全,不受路由路径变化影响
    • 可以正确传递参数
  3. 响应路由参数变化

    • 组件被复用时,created 钩子不会被调用
    • 需要使用 watch 监听 $route 变化
  4. 编程式导航的返回值

    • push()replace() 返回一个 Promise
    • 可以处理导航成功或失败的情况

    this.$router.push('/home').then(() => {
    // 导航成功
    }).catch(err => {
    // 导航被阻止或出错
    if (err.name !== 'NavigationDuplicated') {
    console.log(err)
    }
    })

通过掌握这些路由导航和传参的方法,你可以构建出功能丰富、用户体验良好的单页应用。

$route

$route 监听不需要设置 deep: true

  1. $route 对象的特性

    • $route 是响应式的,每当 URL 发生变化(例如通过点击链接或调用 $router.push()),Vue Router 都会更新 $route 对象,并触发相关的侦听器。
    • $route 包含了诸如 path, name, params, query 等属性,这些属性代表了当前路由的状态。
  2. 监听 $route 变化的目的

    • 通常监听 $route 的目的是为了根据新的路由信息(如参数、查询等)执行某些操作,比如加载新数据或改变页面内容。
    • 因为 Vue Router 在路由发生变化时会直接替换整个 $route 对象,而不是修改其内部属性,所以你只需要监听 $route 本身的变化即可,而无需深入检查 $route 内部属性的变化。
  3. 为什么不需要 deep: true

    • 当路由发生跳转时,Vue Router 实际上是在替换整个 $route 对象,而不是修改它的某个属性。这意味着,如果你监听了 $route,那么每次路由变化都会触发监听器,无论这个变化是发生在 params, query 还是其他属性上。
    • 设置 deep: true 主要是为了监听对象内部深层次的属性变化。然而,在 $route 的情况下,由于它是作为一个整体被替换的,所以不需要这样做。简单地监听 $route 就足够捕捉到所有的路由变化了。