Skip to content

onBeforeRouteLeave 函数 | Vue Router API

概述

onBeforeRouteLeave 是 Vue Router 提供的组合式 API 函数,用于在组件内添加路由离开守卫。当用户尝试离开当前路由时,这个守卫会被触发,允许你执行清理操作、数据保存或阻止导航。

语法

typescript
function onBeforeRouteLeave(guard: NavigationGuard): void

参数

guard

类型: NavigationGuard必需: 是 说明: 导航守卫函数

typescript
type NavigationGuard = (
  to: RouteLocationNormalized,
  from: RouteLocationNormalized,
  next: NavigationGuardNext
) => any

基本用法

基础离开守卫

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'

onBeforeRouteLeave((to, from, next) => {
  if (confirm('确定要离开当前页面吗?未保存的更改将会丢失。')) {
    next() // 允许离开
  } else {
    next(false) // 阻止离开
  }
})
</script>

异步离开确认

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { ref } from 'vue'

const hasUnsavedChanges = ref(false)

onBeforeRouteLeave(async (to, from, next) => {
  if (hasUnsavedChanges.value) {
    const confirmed = await showConfirmationDialog(
      '有未保存的更改',
      '确定要离开吗?未保存的更改将会丢失。'
    )
    
    if (confirmed) {
      next()
    } else {
      next(false)
    }
  } else {
    next() // 没有未保存的更改,直接允许离开
  }
})
</script>

实际应用场景

场景 1:表单数据保护

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { ref, watch } from 'vue'

const formData = ref({
  title: '',
  content: '',
  tags: []
})

const originalData = ref({})
const hasChanges = ref(false)

// 监听表单变化
watch(formData, (newVal) => {
  hasChanges.value = JSON.stringify(newVal) !== JSON.stringify(originalData.value)
}, { deep: true })

onBeforeRouteLeave((to, from, next) => {
  if (hasChanges.value) {
    const answer = confirm('表单有未保存的更改,确定要离开吗?')
    if (answer) {
      next()
    } else {
      next(false)
    }
  } else {
    next()
  }
})

// 初始化原始数据
function initializeData(data) {
  formData.value = { ...data }
  originalData.value = { ...data }
  hasChanges.value = false
}
</script>

场景 2:资源清理

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { onUnmounted } from 'vue'

const subscriptions = ref([])
const timers = ref([])

// 订阅数据
function subscribeToData() {
  const subscription = api.subscribe('data-update', handleDataUpdate)
  subscriptions.value.push(subscription)
}

// 设置定时器
function startPolling() {
  const timer = setInterval(fetchData, 5000)
  timers.value.push(timer)
}

onBeforeRouteLeave((to, from, next) => {
  // 清理订阅
  subscriptions.value.forEach(sub => sub.unsubscribe())
  subscriptions.value = []
  
  // 清理定时器
  timers.value.forEach(timer => clearInterval(timer))
  timers.value = []
  
  next() // 允许离开
})

// 组件卸载时也清理(双重保险)
onUnmounted(() => {
  subscriptions.value.forEach(sub => sub.unsubscribe())
  timers.value.forEach(timer => clearInterval(timer))
})
</script>

场景 3:工作流导航控制

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'

const currentStep = ref(1)
const completedSteps = ref([])

onBeforeRouteLeave((to, from, next) => {
  // 检查是否允许离开当前步骤
  if (to.name === 'workflow-step' && to.params.step < currentStep.value) {
    // 允许返回之前的步骤
    next()
  } else if (!completedSteps.value.includes(currentStep.value)) {
    // 当前步骤未完成,需要确认
    const confirmed = confirm('当前步骤尚未完成,确定要离开吗?')
    if (confirmed) {
      next()
    } else {
      next(false)
    }
  } else {
    // 步骤已完成,允许离开
    next()
  }
})

function markStepCompleted(step) {
  if (!completedSteps.value.includes(step)) {
    completedSteps.value.push(step)
  }
}
</script>

高级用法

条件性离开守卫

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { computed } from 'vue'

const isEditing = ref(false)
const hasUnsavedChanges = ref(false)

// 只在编辑模式下启用离开守卫
const shouldProtectNavigation = computed(() => 
  isEditing.value && hasUnsavedChanges.value
)

onBeforeRouteLeave((to, from, next) => {
  if (shouldProtectNavigation.value) {
    showSavePrompt().then((result) => {
      switch (result) {
        case 'save':
          saveChanges().then(() => next())
          break
        case 'discard':
          next()
          break
        case 'cancel':
          next(false)
          break
      }
    })
  } else {
    next()
  }
})
</script>

与状态管理集成

vue
<script setup>
import { onBeforeRouteLeave } from 'vue-router'
import { useFormStore } from '@/stores/form'

const formStore = useFormStore()

onBeforeRouteLeave((to, from, next) => {
  if (formStore.hasUnsavedChanges) {
    formStore.showLeaveConfirmation = true
    formStore.pendingNavigation = { to, from, next }
  } else {
    next()
  }
})

// 在状态管理中处理确认结果
function handleLeaveConfirmation(confirmed) {
  if (confirmed) {
    formStore.pendingNavigation.next()
  } else {
    formStore.pendingNavigation.next(false)
  }
  formStore.showLeaveConfirmation = false
  formStore.pendingNavigation = null
}
</script>

最佳实践

1. 用户友好的确认提示

javascript
async function showUserFriendlyConfirm(message) {
  // 使用自定义确认对话框而不是原生 confirm
  return new Promise((resolve) => {
    showModal({
      title: '确认离开',
      message,
      buttons: [
        { text: '保存并离开', action: 'save' },
        { text: '直接离开', action: 'leave' },
        { text: '取消', action: 'cancel' }
      ],
      onClose: (action) => {
        resolve(action)
      }
    })
  })
}

2. 自动保存策略

javascript
onBeforeRouteLeave(async (to, from, next) => {
  if (hasUnsavedChanges.value) {
    try {
      // 尝试自动保存
      await autoSave()
      next()
    } catch (error) {
      // 自动保存失败,让用户决定
      const action = await showSavePrompt()
      if (action === 'leave') {
        next()
      } else {
        next(false)
      }
    }
  } else {
    next()
  }
})

3. 性能优化

javascript
// 避免在每次导航时都创建新函数
const leaveGuard = (to, from, next) => {
  if (shouldBlockNavigation.value) {
    handleBlockedNavigation(to, from, next)
  } else {
    next()
  }
}

onBeforeRouteLeave(leaveGuard)

注意事项

  1. 组件生命周期 - 守卫只在组件激活时有效
  2. 异步操作 - 确保正确处理异步确认逻辑
  3. 内存泄漏 - 及时清理事件监听器和定时器
  4. 用户体验 - 避免过度使用离开确认,只在必要时提示

兼容性

  • Vue 3.0+
  • 组合式 API
  • TypeScript 支持

🛡️ 提示onBeforeRouteLeave 是保护用户数据的重要工具,合理使用可以防止意外数据丢失,提升应用的专业性。

🚀 Vue Router - 让前端路由变得简单而强大 | 构建现代化单页应用的最佳选择