Skip to content

路由组件传参

布尔模式

在你的组件中使用 $routeuseRoute() 会与路由紧密耦合,这限制了组件的灵活性,因为它只能用于特定的 URL。虽然这不一定是件坏事,但我们可以通过 props 配置来解除这种行为:

vue
<template>
  <div>
    User {{ $route.params.id }}
  </div>
</template>
ts
import User from './User.vue'

// 传入 `createRouter`
const routes = [
  { path: '/users/:id', component: User },
]

我们可以通过声明 prop 来在 User.vue 中删除对 $route 的直接依赖:

vue
<!-- User.vue -->
<script setup>
defineProps({
  id: String
})
</script>

<template>
  <div>
    User {{ id }}
  </div>
</template>

然后我们可以通过设置 props: true 来配置路由将 id 参数作为 prop 传递给组件:

ts
const routes = [
  { path: '/user/:id', component: User, props: true }
]

props 设置为 true 时,route.params 将被设置为组件的 props。

对象模式

props 是一个对象时,它将原样设置为组件 props。当 props 是静态的时候很有用。

ts
const routes = [
  {
    path: '/promotion/from-newsletter',
    component: Promotion,
    props: { newsletterPopup: false }
  }
]

函数模式

props支持编写函数:

ts
const routes = [
  {
    path: '/search',
    component: SearchUser,
    props: route => ({ query: route.query.q })
  }
]

URL /search?q=vue 将传递 {query: 'vue'} 作为 props 传给 SearchUser 组件。

函数要“无状态”(stateless):函数只依赖当前路由信息计算 props,不依赖组件的响应式状态,否则当状态变化时,props 不会自动更新。如果必须依赖状态,请用“包装组件”来响应式地传递。

假设我们有一个搜索页面:

ts
const routes = [
  {
    path: '/search',
    component: SearchUser,
    props: route => ({ query: route.query.q })
  }
]

props 函数只在路由变化时调用,如果依赖某个“可变状态”,就不会自动响应。

比如我们希望 props 不仅取决于 route.query.q,还要依赖一个“全局搜索模式”状态(来自 Pinia 或父组件)。 这时候 props: route => (...) 就不行了,因为它不会随着“搜索模式”变化而重新执行。

我们可以写一个包装组件来负责把动态状态传给子组件:

ts
<!-- SearchWrapper.vue -->
<script setup>
import { computed } from 'vue'
import { useRoute } from 'vue-router'
import { useSearchStore } from '@/stores/searchStore'
import SearchUser from './SearchUser.vue'

const route = useRoute()
const searchStore = useSearchStore()

// 在这里动态计算 props
const props = computed(() => ({
  query: route.query.q,
  mode: searchStore.mode
}))
</script>

<template>
  <SearchUser v-bind="props" />
</template>
ts
{
  path: '/search',
  component: () => import('@/components/SearchWrapper.vue')
}

通过 RouterView

你还可以通过 插槽 传递任意参数:

vue
<RouterView v-slot="{ Component }">
  <component
    :is="Component"
    view-prop="value"
   />
</RouterView>

在这种情况下,所有视图组件都会接收到 view-prop。通常这并不是一个好主意,因为这意味着所有的视图组件都声明了一个 view-prop prop,但这未必需要。所以请尽可能使用上述的其他选项。