Skip to content

Latest commit

 

History

History
185 lines (138 loc) · 5.14 KB

Vue 错误处理 — onErrorCaptured 钩子.md

File metadata and controls

185 lines (138 loc) · 5.14 KB

Vue 错误处理 — onErrorCaptured 钩子

Vue 实例有一个 onErrorCaptured 钩子,每当事件处理程序或生命周期钩子抛出错误时,Vue 会调用该钩子。

例如,下面的代码将增加一个计数器,每次单击按钮时,组件 test 都会抛出一个错误。

<template>
  <span id="count">{{ count }}</span>
  <test></test>
</template>

<script setup>
  import { onErrorCaptured, ref } from 'vue'
  import test from './test.vue'

  const count = ref(0)

  onErrorCaptured((err) => {
    console.log('Caught error', err.message)
    ++count.value
    return false
  })
</script>

test.vue 组件内容如下:

<!-- 这里我们没有提供 notAMethod 方法供按钮调用 -->
<template>
  <button @click="notAMethod()">Throw</button>
</template>

按照上面的步骤走,您将在控制台看到 onErrorCaptured 捕获的错误:

Caught error _ctx.notAMethod is not a function

示例地址

onErrorCaptured 只捕获嵌套组件中的错误

一个常见的问题是,当错误发生在注册 onErrorCaptured 钩子的同一组件中时,Vue 不会调用 onErrorCaptured

例如,如果从上述示例中删除 test 组件,并将按钮内联到顶级 Vue 实例中,Vue 将不会调用 onErrorCaptured

<template>
  <span id="count">{{ count }}</span>
  <button @click="notAMethod">Throw</button>
</template>

<script setup>
  import { onErrorCaptured, ref } from 'vue'
  import test from './test.vue'

  const count = ref(0)

  // Vue 不会调这个钩子,因为错误发生在这个 Vue 实例中,而不是子组件。
  onErrorCaptured((err) => {
    console.log('Caught error', err.message)
    ++count.value
    return false
  })
</script>

异步错误

好的一面是,当异步函数抛出错误时,Vue 会调用 onErrorCapture

例如,如果子组件异步抛出错误,Vue 仍然会将错误冒泡给父组件。

<template>
  <span id="count">{{ count }}</span>
  <test />
</template>

<script setup>
  import { onErrorCaptured, ref } from 'vue'
  import test from './test.vue'

  const count = ref(0)

  onErrorCaptured((err) => {
    console.log('Caught error', err.message)
    ++count.value
    return false
  })
</script>

test.vue

<template>
  <button @click="test">Throw</button>
</template>

<script setup>
  // Vue 会将异步错误冒泡到父级的 onErrorCaptured(),因此每次单击该按钮时,
  // Vue 都会调用带有 err 的 onErrorCaptured() 钩子。
  // err.message = 'Oops'
  const test = async () => {
    await new Promise((resolve) => setTimeout(resolve, 50))
    throw new Error('Oops!')
  }
</script>

控制台将打印:

Caught error Oops!

错误传播

在前面的示例中,您可能已经注意到 return false。如果 onErrorCaptured() 函数没有 return false,则 Vue 会将错误冒泡到父组件的 onErrorCaptured()

<template>
  <span id="count">{{ count }}</span>
  <test1 />
</template>

<script setup>
  import { onErrorCaptured, ref } from 'vue'
  import test1 from './test1.vue'

  // 由于 test1 组件的 onErrorCaptured() 没有 return false,Vue 将冒泡显示错误。
  const count = ref(0)

  onErrorCaptured((err) => {
    console.log('Caught top-test error', err.message)
    ++count.value
    return false
  })
</script>

test1.vue

<template>
  <test2 />
</template>

<script setup>
  import { onErrorCaptured, ref } from 'vue'
  import test2 from './test2.vue'

  onErrorCaptured((err) => {
    console.log('test 1 error', err.message)
  })
</script>

test2.vue

<template>
  <button @click="notAMethod()">Throw</button>
</template>

错误传播

另一方面,如果 onErrorCaptured() 方法使用 return false,Vue 将停止该错误的传播:

// test2.vue
onErrorCaptured((err) => {
  console.log('test 1 error', err.message)
  return false
})

最后

您可以继续阅读 Handling Errors in Vue with Error Boundaries 这篇文章,它利用 onErrorCaptured 钩子封装了一个类似 React ErrorBoundary 的组件。组件地址