Rumah  >  Artikel  >  hujung hadapan web  >  Artikel yang menerangkan secara terperinci cara Vue3 mengendalikan pengecualian global

Artikel yang menerangkan secara terperinci cara Vue3 mengendalikan pengecualian global

青灯夜游
青灯夜游ke hadapan
2022-03-07 19:54:223680semak imbas

Bagaimanakah Vue3 melaksanakan pengendalian pengecualian global? Artikel berikut akan memperkenalkan kepada anda kaedah pengendalian pengecualian global dalam Vue3. Saya harap ia akan membantu anda!

Artikel yang menerangkan secara terperinci cara Vue3 mengendalikan pengecualian global

Apabila membangunkan perpustakaan komponen atau pemalam, selalunya perlu untuk mengendalikan pengecualian global untuk mencapai:

  • Pengendalian pengecualian bersepadu secara global;
  • Mesej ralat segera untuk pembangun;
  • Pemprosesan penurunan taraf penyelesaian, dsb.

Jadi bagaimana untuk melaksanakan fungsi di atas? Artikel ini mula-mula melaksanakan secara ringkas kaedah pengendalian pengecualian, kemudian memperkenalkannya secara terperinci berdasarkan pelaksanaan dalam kod sumber Vue3, dan akhirnya meringkaskan beberapa pelaksanaan teras pengendalian pengecualian. [Cadangan berkaitan: tutorial video vuejs]

Vue3 versi artikel ini ialah 3.0.11

Pengecualian bahagian hadapan biasa

Untuk bahagian hadapan, terdapat banyak pengecualian biasa, seperti:

  • Pengecualian sintaks JS;
  • Pengecualian permintaan Ajax; memuatkan pengecualian;
  • Janjikan pengecualian;
  • iframe pengecualian; 1. window.onerror
  • Menurut
  • dokumen
  • , kita tahu bahawa apabila ralat berlaku semasa JS berjalan (termasuk ralat sintaks),
dicetuskan:

Parameter fungsi:

window.onerrormesej: mesej ralat (rentetan). Boleh digunakan untuk pengendali dalam HTML . window.onerror()

sumber: URL skrip tempat ralat berlaku (rentetan)
window.onerror = function(message, source, lineno, colno, error) {
  console.log('捕获到异常:',{message, source, lineno, colno, error});
}

lineno: Nombor baris tempat ralat berlaku (nombor)

    kolon: Nombor lajur tempat ralat berlaku (nombor)
  • onerror=""ralat: eventObjek ralat
  • (objek)
  • Jika fungsi ini kembali benar, ia menghalang pelaksanaan fungsi pengendalian acara lalai.
  • 2. cuba...tangkap pengendalian pengecualian
  • Selain itu, kami sering menggunakan pernyataan untuk mengendalikan pengecualian:

Untuk lebih banyak kaedah pemprosesan, anda boleh membaca artikel yang disyorkan di atas.

3. Berfikir

Anda boleh memikirkannya, adakah anda sering menghadapi kesilapan ini semasa proses pembangunan perniagaan? Jadi adakah perpustakaan kompleks seperti Vue3 menggunakan try...catch untuk mengendalikan pengecualian di mana-mana sahaja? Mari kita lihat seterusnya. 2. Laksanakan pengendalian pengecualian global yang ringkas

try {
  // do something
} catch (error) {
  console.error(error);
}
Apabila membangunkan pemalam atau perpustakaan, kami boleh merangkum kaedah pengendalian pengecualian global melalui

dan menyampaikan kaedah yang perlu dilaksanakan sebagai parameter. Pemanggil hanya mengambil berat tentang hasil panggilan dan tidak perlu mengetahui logik dalaman kaedah pengendalian pengecualian global. Penggunaan umum adalah seperti berikut:

Uji:

try...catch

Seperti yang anda lihat, apabila anda perlu mengendalikan pengecualian untuk sesuatu kaedah, anda hanya perlu lulus dalam kaedah sebagai parameter. Walau bagaimanapun, contoh di atas agak berbeza daripada logik pembangunan perniagaan sebenar Dalam perniagaan sebenar, kita sering menghadapi panggilan kaedah bersarang, jadi mari cuba:

Ini tiada masalah. Kemudian langkah seterusnya ialah melaksanakan pengendalian pengecualian yang sepadan dalam cabang try...catch kaedah

. Seterusnya, mari lihat bagaimana ia dikendalikan dalam kod sumber Vue3?
const errorHandling = (fn, args) => {
  let result;
  try{
    result = args ? fn(...args) : fn();
  } catch (error){
    console.error(error)
  }
  return result;
}

3. Bagaimana untuk melaksanakan pengendalian pengecualian dalam Vue3

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
*/
Selepas memahami contoh di atas, mari kita lihat cara melaksanakan pengendalian pengecualian dalam kod sumber Vue3. Ia juga sangat mudah untuk dilaksanakan .

1. Laksanakan kaedah pengendalian pengecualian
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)
*/

mentakrifkan dua kaedah pengendalian pengecualian global, errorHandling dan catch, dalam fail

. Seperti namanya, kedua-dua kaedah ini mengendalikan masing-masing:

: mengendalikan pengecualian dalam kaedah segerak;

digunakan seperti berikut: errorHandling.tscallWithErrorHandlingcallWithAsyncErrorHandlingPelaksanaan kod adalah kira-kira seperti berikut:

  • callWithErrorHandlingLogik pemprosesan kaedah secara relatifnya mudah, melalui ringkas
  • Buat lapisan pembungkusan. Kaedah
  • lebih bijak Ia melepasi kaedah yang perlu dilaksanakan ke dalam kaedah callWithAsyncErrorHandling untuk pemprosesan, dan hasilnya diproses melalui kaedah
  • .

2. Mengendalikan pengecualian

callWithAsyncErrorHandling(
  handler,
  instance,
  ErrorCodes.COMPONENT_EVENT_HANDLER,
  args
)
Dalam kod di atas, jika ralat dilaporkan, pengecualian akan dikendalikan melalui

. Pelaksanaannya kira-kira seperti berikut:

// 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)
    })
  }
  // 省略其他代码
}

Selepas mengekalkan logik pemprosesan teras, anda dapat melihat bahawa pemprosesan di sini juga agak mudah, dan kandungan ralat dikeluarkan terus melalui callWithErrorHandling. try...catch

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前端

Atas ialah kandungan terperinci Artikel yang menerangkan secara terperinci cara Vue3 mengendalikan pengecualian global. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam