>웹 프론트엔드 >View.js >Vue3가 전역 예외를 처리하는 방법을 자세히 설명하는 기사

Vue3가 전역 예외를 처리하는 방법을 자세히 설명하는 기사

青灯夜游
青灯夜游앞으로
2022-03-07 19:54:223766검색

Vue3는 전역 예외 처리를 어떻게 구현하나요? 다음 글에서는 Vue3의 전역 예외 처리 방법을 소개하겠습니다. 도움이 되셨으면 좋겠습니다!

Vue3가 전역 예외를 처리하는 방법을 자세히 설명하는 기사

구성 요소 라이브러리 또는 플러그인을 개발할 때 다음을 달성하기 위해 전역 예외 처리가 필요한 경우가 많습니다.

  • 전역적으로 통합된 예외 처리
  • 개발자를 위한 프롬프트 오류 메시지
  • 프로젝트 성능 저하 처리 등

그럼 위 기능을 어떻게 구현할까요? 이 기사에서는 먼저 예외 처리 방법을 간략하게 구현한 다음 Vue3 소스 코드의 구현을 기반으로 자세히 소개하고 마지막으로 예외 처리의 몇 가지 핵심 구현을 요약합니다. [관련 권장 사항: vuejs 비디오 튜토리얼]

이 문서의 Vue3 버전은 3.0.11입니다

1. 일반적인 프런트엔드 예외

프론트엔드에는 다음과 같은 많은 일반적인 예외가 있습니다.

  • JS 구문 예외;
  • 정적 리소스 로딩 예외;
  • iframe 예외;
  • etc.
  • 창. onerror
  • window.onerror를 통해Document
JS 런타임 중에 오류가 발생하면(구문 오류 포함) window.onerror()가 트리거되는 것을 볼 수 있습니다.

window.onerror = function(message, source, lineno, colno, error) {
  console.log('捕获到异常:',{message, source, lineno, colno, error});
}

Function 매개 변수:

message: error 메시지(문자열). HTML onerror="" 핸들러의 event에 사용할 수 있습니다. window.onerror文档可知,当 JS 运行时发生错误(包括语法错误),触发 window.onerror()

try {
  // do something
} catch (error) {
  console.error(error);
}

函数参数:

  • message:错误信息(字符串)。可用于HTML onerror=""处理程序中的 event
  • source:发生错误的脚本URL(字符串)
  • lineno:发生错误的行号(数字)
  • colno:发生错误的列号(数字)
  • error:Error对象(对象)

若该函数返回true,则阻止执行默认事件处理函数。

2.  try...catch 异常处理

另外,我们也经常会使用 try...catchsource: 오류가 발생한 스크립트 URL(문자열)

lineno: 오류가 발생한 줄 번호(번호)

colno: 오류가 발생한 열 번호(번호)

error: 오류 개체

(개체)try...catch

이 경우 함수가 true를 반환하면 기본 이벤트 핸들러의 실행을 방지합니다.

2. try...catch 예외 처리

try...catch또한 예외를 처리하기 위해

statements

를 자주 사용합니다.

const errorHandling = (fn, args) => {
  let result;
  try{
    result = args ? fn(...args) : fn();
  } catch (error){
    console.error(error)
  }
  return result;
}

자세한 처리 방법은 다음을 참조하세요. 이전 추천 기사.

errorHandling方法的 catch3. Thinking

생각해보면 사업개발 과정에서 이런 오류를 자주 처리해야 하는 경우가 있나요? 그렇다면 Vue3과 같은 복잡한 라이브러리는

를 사용하여 모든 곳에서 예외를 처리합니까? 다음을 살펴보겠습니다.

2. 간단한 전역 예외 처리 구현

플러그인이나 라이브러리를 개발할 때

를 통해 전역 예외 처리 방법을 캡슐화하고 호출자만 신경 쓰면 실행해야 하는 메서드를 전달할 수 있습니다. 이 전역 예외 처리 방법의 내부 논리를 모르고 호출 결과. 일반적인 사용법은 다음과 같습니다. errorHandling.ts 文件中定义了 callWithErrorHandlingcallWithAsyncErrorHandling

const f1 = () => {
    console.log('[f1 running]')
    throw new Error('[f1 error!]')
}

errorHandling(f1);
/*
 输出:
 [f1 running]
Error: [f1 error!]
    at f1 (/Users/wangpingan/leo/www/node/www/a.js:14:11)
    at errorHandling (/Users/wangpingan/leo/www/node/www/a.js:4:39)
    at Object.<anonymous> (/Users/wangpingan/leo/www/node/www/a.js:17:1)
    at Module._compile (node:internal/modules/cjs/loader:1095:14)
    at Object.Module._extensions..js (node:internal/modules/cjs/loader:1147:10)
    at Module.load (node:internal/modules/cjs/loader:975:32)
    at Function.Module._load (node:internal/modules/cjs/loader:822:12)
    at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    at node:internal/main/run_main_module:17:47
*/

테스트해 보세요:
    const f1 = () => {
        console.log(&#39;[f1]&#39;)
        f2();
    }
    
    const f2 = () => {
        console.log(&#39;[f2]&#39;)
        f3();
    }
    
    const f3 = () => {
        console.log(&#39;[f3]&#39;)
        throw new Error(&#39;[f3 error!]&#39;)
    }
    
    errorHandling(f1)
    /*
      输出:
      [f1 running]
      [f2 running]
      [f3 running]
      Error: [f3 error!]
        at f3 (/Users/wangpingan/leo/www/node/www/a.js:24:11)
        at f2 (/Users/wangpingan/leo/www/node/www/a.js:19:5)
        at f1 (/Users/wangpingan/leo/www/node/www/a.js:14:5)
        at errorHandling (/Users/wangpingan/leo/www/node/www/a.js:4:39)
        at Object.<anonymous> (/Users/wangpingan/leo/www/node/www/a.js:27:1)
        at Module._compile (node:internal/modules/cjs/loader:1095:14)
        at Object.Module._extensions..js (node:internal/modules/cjs/loader:1147:10)
        at Module.load (node:internal/modules/cjs/loader:975:32)
        at Function.Module._load (node:internal/modules/cjs/loader:822:12)
        at Function.executeUserEntryPoint [as runMain] (node:internal/modules/run_main:81:12)
    */
  • 메서드에 대한 예외를 처리해야 할 때 해당 메서드를 매개변수로 전달하기만 하면 된다는 것을 알 수 있습니다. 그러나 위의 예는 실제 비즈니스 개발의 논리와 약간 다릅니다. 실제 비즈니스에서는 중첩된 메서드 호출을 자주 접하므로 시도해 보겠습니다. callWithErrorHandling:处理同步方法的异常;
  • callWithAsyncErrorHandling:处理异步方法的异常。

使用方式如下:

callWithAsyncErrorHandling(
  handler,
  instance,
  ErrorCodes.COMPONENT_EVENT_HANDLER,
  args
)

代码实现大致如下:

// packages/runtime-core/src/errorHandling.ts

// 处理同步方法的异常
export function callWithErrorHandling(
  fn: Function,
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  args?: unknown[]
) {
  let res
  try {
    res = args ? fn(...args) : fn(); // 调用原方法
  } catch (err) {
    handleError(err, instance, type)
  }
  return res
}

// 处理异步方法的异常
export function callWithAsyncErrorHandling(
  fn: Function | Function[],
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  args?: unknown[]
): any[] {
  // 省略其他代码
  const res = callWithErrorHandling(fn, instance, type, args)
  if (res && isPromise(res)) {
    res.catch(err => {
      handleError(err, instance, type)
    })
  }
  // 省略其他代码
}

callWithErrorHandling方法处理的逻辑比较简单,通过简单的 try...catch

// packages/runtime-core/src/errorHandling.ts

// 异常处理方法
export function handleError(
  err: unknown,
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  throwInDev = true
) {
  // 省略其他代码
  logError(err, type, contextVNode, throwInDev)
}

function logError(
  err: unknown,
  type: ErrorTypes,
  contextVNode: VNode | null,
  throwInDev = true
) {
  // 省略其他代码
  console.error(err)
}
callWithAsyncErrorHandling 方法就比较巧妙,通过将需要执行的方法传入 callWithErrorHandling方法处理,并将其结果通过 .catch方法进行处理。

2. 处理异常

在上面代码中,遇到报错的情况,都会通过 handleError()处理异常。其实现大致如下:

// src/main.js

app.config.errorHandler = (err, vm, info) => {
  // 处理错误
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
}

保留核心处理逻辑之后,可以看到这边处理也是相当简单,直接通过 console.error(err) 문제 없습니다. 그런 다음 다음 단계는

분기에서 해당 예외 처리를 구현하는 것입니다. 다음으로 Vue3 소스 코드에서 어떻게 처리되는지 살펴보겠습니다. 🎜🎜3. Vue3에서 예외 처리를 구현하는 방법🎜🎜위의 예를 이해한 후 Vue3 소스 코드에서 예외 처리를 구현하는 방법을 살펴보겠습니다. 구현도 매우 간단합니다. 🎜🎜1. 예외 처리 방법 구현🎜🎜전역 예외 처리에는 🎜두 가지 방법이 있습니다. 이름에서 알 수 있듯이 이 두 메서드는 각각 다음을 처리합니다. 🎜🎜🎜callWithErrorHandling: 동기 메서드의 예외를 처리합니다. 🎜🎜callWithAsyncErrorHandling: 비동기 메서드의 예외를 처리합니다. 🎜🎜🎜사용법은 다음과 같습니다. 🎜
// packages/runtime-core/src/errorHandling.ts

export function handleError(
  err: unknown,
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  throwInDev = true
) {
  const contextVNode = instance ? instance.vnode : null
  if (instance) {
    // 省略其他代码
    // 读取 errorHandler 配置项
    const appErrorHandler = instance.appContext.config.errorHandler
    if (appErrorHandler) {
      callWithErrorHandling(
        appErrorHandler,
        null,
        ErrorCodes.APP_ERROR_HANDLER,
        [err, exposedInstance, errorInfo]
      )
      return
    }
  }
  logError(err, type, contextVNode, throwInDev)
}
🎜코드 구현은 대략 다음과 같습니다. 🎜
(err: Error, instance: Component, info: string) => ?boolean
🎜callWithErrorHandling 메서드에서 처리하는 로직은 비교적 간단하며 간단한 🎜을 사용하여 레이어를 생성합니다. 캡슐화의. callWithAsyncErrorHandling 메서드는 실행해야 하는 메서드를 callWithErrorHandling 메서드에 전달하여 처리하고, 그 결과는 .catch 메소드 . 🎜<h3 data-id="heading-7">2. 예외 처리🎜🎜위 코드에서 오류가 보고되면 <code>handleError()를 통해 예외를 처리합니다. 구현은 대략 다음과 같습니다. 🎜
<template>
  <Message></Message>
</template>
<script setup>
// App.vue  
import { onErrorCaptured } from &#39;vue&#39;;
  
import Message from &#39;./components/Message.vue&#39;
  
onErrorCaptured(function(err, instance, info){
  console.log(&#39;[errorCaptured]&#39;, err, instance, info)
})
</script>
🎜핵심 처리 로직을 유지한 후에 여기에서도 처리가 매우 간단하다는 것을 알 수 있으며 오류 내용은 console.error(err)를 통해 직접 출력됩니다. . 🎜

3. 配置 errorHandler 自定义异常处理函数

在使用 Vue3 时,也支持指定自定义异常处理函数,来处理组件渲染函数侦听器执行期间抛出的未捕获错误。这个处理函数被调用时,可获取错误信息和相应的应用实例。 文档参考:《errorHandler》 使用方法如下,在项目 main.js文件中配置:

// src/main.js

app.config.errorHandler = (err, vm, info) => {
  // 处理错误
  // `info` 是 Vue 特定的错误信息,比如错误所在的生命周期钩子
}

那么 errorHandler()是何时执行的呢?我们继续看看源码中 handleError() 的内容,可以发现:

// packages/runtime-core/src/errorHandling.ts

export function handleError(
  err: unknown,
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  throwInDev = true
) {
  const contextVNode = instance ? instance.vnode : null
  if (instance) {
    // 省略其他代码
    // 读取 errorHandler 配置项
    const appErrorHandler = instance.appContext.config.errorHandler
    if (appErrorHandler) {
      callWithErrorHandling(
        appErrorHandler,
        null,
        ErrorCodes.APP_ERROR_HANDLER,
        [err, exposedInstance, errorInfo]
      )
      return
    }
  }
  logError(err, type, contextVNode, throwInDev)
}

通过 instance.appContext.config.errorHandler取到全局配置的自定义错误处理函数,存在时则执行,当然,这边也是通过前面定义的 callWithErrorHandling来调用。

4. 调用 errorCaptured 生命周期钩子

在使用 Vue3 的时候,也可以通过 errorCaptured生命周期钩子来捕获来自后代组件的错误。 文档参考:《errorCaptured》 入参如下:

(err: Error, instance: Component, info: string) => ?boolean

此钩子会收到三个参数:错误对象、发生错误的组件实例以及一个包含错误来源信息的字符串。 此钩子可以返回 false阻止该错误继续向上传播。有兴趣的同学可以通过文档,查看具体的错误传播规则。 使用方法如下,父组件监听 onErrorCaptured生命周期(示例代码使用 Vue3 setup 语法):

<template>
  <Message></Message>
</template>
<script setup>
// App.vue  
import { onErrorCaptured } from &#39;vue&#39;;
  
import Message from &#39;./components/Message.vue&#39;
  
onErrorCaptured(function(err, instance, info){
  console.log(&#39;[errorCaptured]&#39;, err, instance, info)
})
</script>

子组件如下:

<template>
  <button @click="sendMessage">发送消息</button>
</template>

<script setup>
// Message.vue
const sendMessage = () => {
  throw new Error(&#39;[test onErrorCaptured]&#39;)
}
</script>

当点击「发送消息」按钮,控制台便输出错误:

[errorCaptured] Error: [test onErrorCaptured]
    at Proxy.sendMessage (Message.vue:36:15)
    at _createElementVNode.onClick._cache.<computed>._cache.<computed> (Message.vue:3:39)
    at callWithErrorHandling (runtime-core.esm-bundler.js:6706:22)
    at callWithAsyncErrorHandling (runtime-core.esm-bundler.js:6715:21)
    at HTMLButtonElement.invoker (runtime-dom.esm-bundler.js:350:13) Proxy {sendMessage: ƒ, …} native event handler

可以看到 onErrorCaptured生命周期钩子正常执行,并输出子组件 Message.vue内的异常。

那么这个又是如何实现呢?还是看 errorHandling.ts 中的 handleError() 方法:

// packages/runtime-core/src/errorHandling.ts

export function handleError(
  err: unknown,
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  throwInDev = true
) {
  const contextVNode = instance ? instance.vnode : null
  if (instance) {
    let cur = instance.parent
    // the exposed instance is the render proxy to keep it consistent with 2.x
    const exposedInstance = instance.proxy
    // in production the hook receives only the error code
    const errorInfo = __DEV__ ? ErrorTypeStrings[type] : type
    while (cur) {
      const errorCapturedHooks = cur.ec // ①取出组件配置的 errorCaptured 生命周期方法
      if (errorCapturedHooks) {
        // ②循环执行 errorCaptured 中的每个 Hook
        for (let i = 0; i < errorCapturedHooks.length; i++) {
          if (
            errorCapturedHooks[i](err, exposedInstance, errorInfo) === false
          ) {
            return
          }
        }
      }
      cur = cur.parent
    }
    // 省略其他代码
  }
  logError(err, type, contextVNode, throwInDev)
}

这边会先获取 instance.parent作为当前处理的组件实例进行递归,每次将取出组件配置的 errorCaptured 生命周期方法的数组并循环调用其每一个钩子,然后再取出当前组件的父组件作为参数,最后继续递归调用下去。

5. 实现错误码和错误消息

Vue3 还为异常定义了错误码和错误信息,在不同的错误情况有不同的错误码和错误信息,让我们能很方便定位到发生异常的地方。 错误码和错误信息如下:

// packages/runtime-core/src/errorHandling.ts

export const enum ErrorCodes {
  SETUP_FUNCTION,
  RENDER_FUNCTION,
  WATCH_GETTER,
  WATCH_CALLBACK,
  // ... 省略其他
}

export const ErrorTypeStrings: Record<number | string, string> = {
  // 省略其他
  [LifecycleHooks.RENDER_TRACKED]: &#39;renderTracked hook&#39;,
  [LifecycleHooks.RENDER_TRIGGERED]: &#39;renderTriggered hook&#39;,
  [ErrorCodes.SETUP_FUNCTION]: &#39;setup function&#39;,
  [ErrorCodes.RENDER_FUNCTION]: &#39;render function&#39;,
  // 省略其他
  [ErrorCodes.SCHEDULER]:
    &#39;scheduler flush. This is likely a Vue internals bug. &#39; +
    &#39;Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next&#39;
}

当不同错误情况,根据错误码 ErrorCodes来获取 ErrorTypeStrings错误信息进行提示:

// packages/runtime-core/src/errorHandling.ts

function logError(
  err: unknown,
  type: ErrorTypes,
  contextVNode: VNode | null,
  throwInDev = true
) {
  if (__DEV__) {
    const info = ErrorTypeStrings[type]
    warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`)
    // 省略其他
  } else {
    console.error(err)
  }
}

6. 实现 Tree Shaking

关于 Vue3 实现 Tree Shaking 的介绍,可以看我之前写的高效实现框架和 JS 库瘦身。 其中,logError 方法中就使用到了:

// packages/runtime-core/src/errorHandling.ts

function logError(
  err: unknown,
  type: ErrorTypes,
  contextVNode: VNode | null,
  throwInDev = true
) {
  if (__DEV__) {
    // 省略其他
  } else {
    console.error(err)
  }
}

当编译成 production 环境后,__DEV__分支的代码不会被打包进去,从而优化包的体积。

四、总结

到上面一部分,我们就差不多搞清楚 Vue3 中全局异常处理的核心逻辑了。我们在开发自己的错误处理方法时,也可以考虑这几个核心点:

  • 支持同步和异步的异常处理;

  • 设置业务错误码、业务错误信息;

  • 支持自定义错误处理方法;

  • 支持开发环境错误提示;

  • 支持 Tree Shaking。

原文地址:https://juejin.cn/post/7071982812668100616

(学习视频分享:vuejs教程web前端

위 내용은 Vue3가 전역 예외를 처리하는 방법을 자세히 설명하는 기사의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
이 기사는 juejin.cn에서 복제됩니다. 침해가 있는 경우 admin@php.cn으로 문의하시기 바랍니다. 삭제