onBeforeRouteUpdate 函数 | Vue Router API
概述
onBeforeRouteUpdate 是 Vue Router 提供的组合式 API 函数,用于在组件内添加路由更新守卫。当当前路由的参数或查询参数发生变化,但组件被复用时,这个守卫会被触发。
语法
typescript
function onBeforeRouteUpdate(guard: NavigationGuard): void参数
guard
类型: NavigationGuard必需: 是 说明: 导航守卫函数
typescript
type NavigationGuard = (
to: RouteLocationNormalized,
from: RouteLocationNormalized,
next: NavigationGuardNext
) => any基本用法
基础路由更新守卫
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
const userData = ref(null)
onBeforeRouteUpdate(async (to, from, next) => {
// 当用户 ID 发生变化时重新加载数据
if (to.params.id !== from.params.id) {
userData.value = await fetchUserData(to.params.id)
}
next()
})
</script>查询参数变化处理
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref, watch } from 'vue'
const searchResults = ref([])
const currentQuery = ref('')
onBeforeRouteUpdate(async (to, from, next) => {
// 当搜索查询发生变化时重新搜索
if (to.query.q !== from.query.q) {
currentQuery.value = to.query.q || ''
searchResults.value = await performSearch(currentQuery.value)
}
next()
})
</script>实际应用场景
场景 1:用户资料页面
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref, onMounted } from 'vue'
const user = ref(null)
const loading = ref(false)
// 加载用户数据
async function loadUserData(userId) {
loading.value = true
try {
user.value = await api.getUser(userId)
} catch (error) {
console.error('加载用户数据失败:', error)
user.value = null
} finally {
loading.value = false
}
}
// 组件挂载时加载数据
onMounted(() => {
loadUserData($route.params.id)
})
// 路由参数变化时重新加载数据
onBeforeRouteUpdate(async (to, from, next) => {
if (to.params.id !== from.params.id) {
await loadUserData(to.params.id)
}
next()
})
</script>
<template>
<div v-if="loading">加载中...</div>
<div v-else-if="user">
<h1>{{ user.name }} 的资料</h1>
<!-- 用户信息展示 -->
</div>
<div v-else>用户不存在</div>
</template>场景 2:产品列表和筛选
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref, computed } from 'vue'
const products = ref([])
const filters = ref({})
// 从路由查询参数解析筛选条件
function parseFiltersFromRoute(route) {
return {
category: route.query.category || '',
priceMin: parseInt(route.query.price_min) || 0,
priceMax: parseInt(route.query.price_max) || 1000,
sort: route.query.sort || 'name'
}
}
// 加载产品数据
async function loadProducts(filters) {
products.value = await api.getProducts(filters)
}
// 初始加载
onMounted(() => {
filters.value = parseFiltersFromRoute($route)
loadProducts(filters.value)
})
// 路由查询参数变化时更新筛选和重新加载
onBeforeRouteUpdate(async (to, from, next) => {
const newFilters = parseFiltersFromRoute(to)
// 检查筛选条件是否发生变化
if (JSON.stringify(newFilters) !== JSON.stringify(filters.value)) {
filters.value = newFilters
await loadProducts(filters.value)
}
next()
})
// 计算属性:筛选后的产品
const filteredProducts = computed(() => {
return products.value.filter(product =>
product.price >= filters.value.priceMin &&
product.price <= filters.value.priceMax
)
})
</script>场景 3:分页导航
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref, computed } from 'vue'
const items = ref([])
const totalItems = ref(0)
const itemsPerPage = 10
// 计算当前页码
const currentPage = computed(() => {
return parseInt($route.query.page) || 1
})
// 计算总页数
const totalPages = computed(() => {
return Math.ceil(totalItems.value / itemsPerPage)
})
// 加载分页数据
async function loadPageData(page) {
const offset = (page - 1) * itemsPerPage
const result = await api.getItems({ offset, limit: itemsPerPage })
items.value = result.items
totalItems.value = result.total
}
// 初始加载
onMounted(() => {
loadPageData(currentPage.value)
})
// 页码变化时重新加载数据
onBeforeRouteUpdate(async (to, from, next) => {
const toPage = parseInt(to.query.page) || 1
const fromPage = parseInt(from.query.page) || 1
if (toPage !== fromPage) {
await loadPageData(toPage)
}
next()
})
</script>
<template>
<div class="pagination">
<router-link
v-for="page in totalPages"
:key="page"
:to="{ query: { page } }"
:class="{ active: page === currentPage }"
>
{{ page }}
</router-link>
</div>
<div class="items">
<div v-for="item in items" :key="item.id">
{{ item.name }}
</div>
</div>
</template>高级用法
防抖搜索
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref, watch } from 'vue'
const searchResults = ref([])
const searchTerm = ref('')
let searchTimeout = null
// 防抖搜索函数
function debouncedSearch(term) {
return new Promise((resolve) => {
if (searchTimeout) {
clearTimeout(searchTimeout)
}
searchTimeout = setTimeout(async () => {
const results = await api.search(term)
resolve(results)
}, 300)
})
}
onBeforeRouteUpdate(async (to, from, next) => {
const newTerm = to.query.q || ''
if (newTerm !== searchTerm.value) {
searchTerm.value = newTerm
searchResults.value = await debouncedSearch(newTerm)
}
next()
})
// 清理定时器
onUnmounted(() => {
if (searchTimeout) {
clearTimeout(searchTimeout)
}
})
</script>数据预加载和缓存
vue
<script setup>
import { onBeforeRouteUpdate } from 'vue-router'
import { ref } from 'vue'
const dataCache = new Map()
async function fetchDataWithCache(key, fetcher) {
if (dataCache.has(key)) {
return dataCache.get(key)
}
const data = await fetcher()
dataCache.set(key, data)
return data
}
onBeforeRouteUpdate(async (to, from, next) => {
const cacheKey = to.fullPath
if (to.params.id !== from.params.id) {
const data = await fetchDataWithCache(cacheKey, () =>
api.getDetail(to.params.id)
)
// 使用缓存的数据
}
next()
})
</script>最佳实践
1. 性能优化
javascript
// 避免不必要的重渲染
onBeforeRouteUpdate(async (to, from, next) => {
// 只在相关参数变化时执行操作
const relevantParams = ['id', 'slug', 'category']
const hasRelevantChange = relevantParams.some(param =>
to.params[param] !== from.params[param]
)
if (hasRelevantChange) {
await reloadData(to)
}
next()
})2. 错误处理
javascript
onBeforeRouteUpdate(async (to, from, next) => {
try {
if (shouldReloadData(to, from)) {
await reloadData(to)
}
next()
} catch (error) {
console.error('路由更新失败:', error)
// 可以选择显示错误页面或重试
showErrorNotification('数据加载失败')
next(false) // 或 next('/error')
}
})3. 条件性更新
javascript
const updateConditions = {
userProfile: (to, from) => to.params.userId !== from.params.userId,
searchResults: (to, from) => to.query.q !== from.query.q,
pagination: (to, from) => to.query.page !== from.query.page
}
onBeforeRouteUpdate(async (to, from, next) => {
const conditions = Object.values(updateConditions)
const shouldUpdate = conditions.some(condition => condition(to, from))
if (shouldUpdate) {
await handleRouteUpdate(to, from)
}
next()
})注意事项
- 组件复用 - 确保组件能够正确处理参数变化
- 性能考虑 - 避免在每次更新时都执行昂贵操作
- 数据一致性 - 确保数据与当前路由参数匹配
- 用户体验 - 提供加载状态和错误处理
兼容性
- Vue 3.0+
- 组合式 API
- TypeScript 支持
🔄 提示:
onBeforeRouteUpdate是处理动态路由参数变化的强大工具,特别适合需要根据路由变化更新数据的场景。