你好同学,我是沐爸,欢迎点赞、收藏、评论和关注。
在 Vue3 中,app.config.errorHandler 是一个错误处理器,用于为应用内抛出的未捕获错误指定一个全局处理函数,它接收三个参数:错误对象、触发该错误的组件实例和一个指出错误来源类型信息的字符串。
app.config.errorHandler = (err, instance, info) => {
// 处理错误,例如:报告给一个服务
}
一、错误来源
它可以从下面这些来源中捕获错误:
- 组件渲染器
- 事件处理器
- 生命周期钩子
setup()
函数- 侦听器
- 自定义指令钩子
- 过渡 (Transition) 钩子
二、错误捕获
1.启用 errorHandler,接收返回的参数,调用接口。
main.js
import { createApp } from 'vue'
import App from './App.vue'
import axios from 'axios'
const app = createApp(App)
app.config.errorHandler = (error, instance, info) => {
axios.post('/api/error', {
message: error.message,
type: info
}).then(() => {
console.log('success')
})
}
app.mount('#app')
第一个参数 error 是一个错误对象,包含错误的详细信息,error.message 则是一个简短的错误描述,向后台提交错误信息时 error.message 更适合。instance 组件实例,一般用不到。
2.安装 axios,并设置接口代理。
vite.config.js
import { fileURLToPath, URL } from 'node:url'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
// https://vitejs.dev/config/
export default defineConfig({
plugins: [
vue(),
],
resolve: {
alias: {
'@': fileURLToPath(new URL('./src', import.meta.url))
}
},
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true
}
}
}
})
3.启动一个后台服务,以 Node.js 的 Express 为例
npm init -y
npm install express
创建 index.js,并输入以下内容:
const express = require('express')
const fs = require('fs')
const app = express()
app.use(express.json())
app.post('/api/error', (req, res) => {
const { message, type } = req.body
fs.appendFile('./error.txt', `${message} - ${type} \n`, (error, data) => {
if (error) {
console.log(error)
return
}
res.send({
code: 8200,
success: true
})
})
})
app.listen(3000, () => {
console.log('Server started on port 3000')
})
启动服务,控制台执行 node index.js
,这样后台服务就启动好了。
4.当页面有报错并调用后台接口会,就会往后台的 error.txt 文件中插入一条数据。
count.value.split is not a function - transition hook
count.value.split is not a function - transition hook
fn is not defined - watcher callback
fn is not defined - watcher callback
abc is not defined - setup function
abc is not defined - setup function
abc is not defined - setup function
abc is not defined - setup function
count.value.split is not a function - mounted hook
count.value.split is not a function - setup function
abc is not defined - setup function
count.value.split is not a function - setup function
count.value.split is not a function - mounted hook
count.value.split is not a function - mounted hook
count.value.split is not a function - setup function
count.value.split is not a function - mounted hook
count.value.split is not a function - setup function
三、示例
1.组件渲染器错误
<script setup>
import { ref } from 'vue'
const count = ref(100)
</script>
<template>
<div>
<!-- render function -->
<p>{{ count.split('') }}</p>
</div>
</template>
因为 count 是数字,并不具有 split 方法,所有组件渲染会报错,app.config.errorHandler 中的 info 参数的返回结果为 render function
。
2.事件处理器错误
指在组件的事件处理方法中产生的错误,app.config.errorHandler 中的 info 参数的返回结果为 native event handler
。
<script setup>
import { ref } from 'vue'
const count = ref(100)
function handleClick() {
// count.value.split('') // 不存在的属性
// fn() // 不存在的方法
// console.log(abc) // 不存在的变量
// throw new Error('报错了') // 主动抛出的错误
}
</script>
<template>
<div>
<p>{{ count }}</p>
<button @click="handleClick">点击</button>
</div>
</template>
3.生命周期钩子
指在生命周期钩子中产生的错误,app.config.errorHandler 中的 info 参数的返回结果为钩子名+ hook,如 mounted hook
。
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(100)
onMounted(() => {
// count.value.split('') // 不存在的属性
// fn() // 不存在的方法
// console.log(abc) // 不存在的变量
// throw new Error('报错了') // 主动抛出的错误
})
</script>
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
4.setup 函数
指在 <script setup>
或setup()
中产生的错误,app.config.errorHandler 中的 info 参数的返回结果为 setup function
。
<script setup>
<script setup>
import { ref, onMounted } from 'vue'
const count = ref(100)
// count.value.split('') // 不存在的属性
// fn() // 不存在的方法
// console.log(abc) // 不存在的变量
// throw new Error('报错了') // 主动抛出的错误
// 监听不存在的 abc,这里也是 setup 错误
watch(abc, (newValue) => {
console.log(newValue)
})
</script>
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
setup()
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
<script>
import { ref } from 'vue'
export default {
setup() {
const count = ref(100)
count.value.split('')
// fn()
// console.log(abc)
// throw new Error('报错了')
}
}
</script>
5.侦听器错误
指在 watch
和 watchEffect
回调中产生的错误,初始化监听产生的错误属于 setup function
错误,app.config.errorHandler 中的 info 参数的返回结果为 watcher callback
。
watch
<script setup>
import { ref, watch } from 'vue'
const count = ref(100)
watch(count, (newValue) => {
// count.value.split('')
// fn()
// console.log(abc)
// throw new Error('报错了')
})
</script>
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
watchEffect
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(100)
watchEffect(() => {
// count.value.split('')
// fn()
// console.log(abc)
// throw new Error('报错了')
})
</script>
<template>
<div>
<p>{{ count }}</p>
</div>
</template>
6.自定义指令钩子错误
指在自定义指令钩子中产生的错误,app.config.errorHandler 中的 info 参数的返回结果为 directive hook
。
<script setup>
// 添加不存在的属性,不会导致报错
const vColor = {
abc: 'abc', // 不会报错
mounted: el => {
el.style.color = 'red'
// count.value.split('')
// fn()
// console.log(abc)
// throw new Error('报错了')
}
}
</script>
<template>
<div>
<p v-color>hello</p>
</div>
</template>
7.过渡 (Transition) 钩子错误
在使用 Transition 的钩子时产生的错误,app.config.errorHandler 中的 info 参数的返回结果为 transition hook
。
以 onEnter 为例:
<script setup>
import { ref } from 'vue'
const count = ref(100)
const show = ref(false)
function handleClick() {
show.value = !show.value
}
function onEnter(el, done) {
el.style.color = 'blue'
done()
// count.value.split('')
// fn()
// console.log(abc)
// throw new Error('报错了')
}
</script>
<template>
<div>
<button @click="handleClick">点击</button>
<Transition @enter="onEnter">
<p v-if="show">内容</p>
</Transition>
</div>
</template>
四、errorCaptured / onErrorCaptured()
两者作用相同,errorCaptured用于选项式API,onErrorCaptured() 用于组合式API,都为组件中的一个钩子,作用是在捕获了后代组件传递的错误时调用。
错误传递规则
- 如果组件的继承链或组件链上存在多个
errorCaptured
钩子,对于同一个错误,这些钩子会被按从底至上的顺序一一调用。这个过程被称为“向上传递”,类似于原生 DOM 事件的冒泡机制。 - 如果
errorCaptured
钩子本身抛出了一个错误,那么这个错误和原来捕获到的错误都将被发送到app.config.errorHandler
。 errorCaptured
钩子可以通过返回false
来阻止错误继续向上传递。即表示“这个错误已经被处理了,应当被忽略”,它将阻止其他的errorCaptured
钩子或app.config.errorHandler
因这个错误而被调用。
1.向上传递
main.js 中 app.config.errorHandler 方法不变。
Parent.vue
<script setup>
import { onErrorCaptured } from 'vue'
import Child from './Child.vue'
onErrorCaptured((error, instance, info) => {
console.log('from Child', error, instance, info)
})
</script>
<template>
<Child></Child>
</template>
Child.vue
<script setup>
import { onErrorCaptured } from 'vue'
import GrandChild from './GrandChild.vue'
onErrorCaptured((error, instance, info) => {
console.log('from GrandChild', error, instance, info)
})
</script>
<template>
<GrandChild></GrandChild>
</template>
GrandChild.vue
<script setup>
console.log(abc)
// 这里不会起作用,因为 onErrorCaptured 监听来自后代组件的错误
onErrorCaptured((error, instance, info) => {
console.log('from GrandChild', error, instance, info)
})
</script>
<template>
<p>hello world</p>
</template>
以上代码执行后,会以此触发 Child.vue 和 Parent.vue 中的 onErrorCaptured
钩子,最后触发 app.config.errorHandler 中的回调。
2.阻止传递
如果在 Child.vue 的 onErrorCaptured 中添加 return false
,那么Parent.vue 和 app.config.errorHandler 中将不会收到错误,因为 Child.vue 组织了错误的传递。
3.抛出错误
如果 Child.vue 组件的onErrorCaptured
钩子中抛出了错误,那么来自 GrandChild.vue 和 Child.vue 自身的错误都会向上传递,这意味着 Parent.vue 和 app.config.errorHandler 都会收到这两个错误。
Child.vue
<script setup>
import { onErrorCaptured } from 'vue'
import GrandChild from './GrandChild.vue'
onErrorCaptured((error, instance, info) => {
console.log(def)
// console.log('from GrandChild', error, instance, info)
// return false
})
</script>
<template>
<GrandChild></GrandChild>
</template>
好了,分享结束,谢谢点赞,下期再见。