欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Vue實(shí)現(xiàn)全局異常處理的幾種方案

 更新時(shí)間:2022年05月09日 15:56:07   作者:陸榮濤  
本文主要介紹了使用pyscript在網(wǎng)頁(yè)中撰寫(xiě)Python程式的方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧

在開(kāi)發(fā)組件庫(kù)或者插件,經(jīng)常會(huì)需要進(jìn)行全局異常處理,從而實(shí)現(xiàn):\

  • 全局統(tǒng)一處理異常;
  • 為開(kāi)發(fā)者提示錯(cuò)誤信息;
  • 方案降級(jí)處理等等。

那么如何實(shí)現(xiàn)上面功能呢?本文先簡(jiǎn)單實(shí)現(xiàn)一個(gè)異常處理方法,然后結(jié)合 Vue3 源碼中的實(shí)現(xiàn)詳細(xì)介紹,最后總結(jié)實(shí)現(xiàn)異常處理的幾個(gè)核心。

本文 Vue3 版本為 3.0.11

一、前端常見(jiàn)異常

對(duì)于前端來(lái)說(shuō),常見(jiàn)的異常比較多,比如:

  • JS 語(yǔ)法異常;
  • Ajax 請(qǐng)求異常;
  • 靜態(tài)資源加載異常;
  • Promise 異常;
  • iframe 異常;

等等

最常用的比如:\

  • window.onerror

通過(guò) window.onerror文檔[2]可知,當(dāng) JS 運(yùn)行時(shí)發(fā)生錯(cuò)誤(包括語(yǔ)法錯(cuò)誤),觸發(fā) window.onerror():

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

函數(shù)參數(shù):

  • message:錯(cuò)誤信息(字符串)。可用于HTML onerror=""處理程序中的 event。
  • source:發(fā)生錯(cuò)誤的腳本URL(字符串)
  • lineno:發(fā)生錯(cuò)誤的行號(hào)(數(shù)字)
  • colno:發(fā)生錯(cuò)誤的列號(hào)(數(shù)字)
  • error:Error對(duì)象(對(duì)象)

若該函數(shù)返回true,則阻止執(zhí)行默認(rèn)事件處理函數(shù)。

  • try...catch 異常處理

另外,我們也經(jīng)常會(huì)使用 try...catch 語(yǔ)句處理異常:

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

更多處理方式,可以閱讀前面推薦的文章。

  • 思考

大家可以思考下,自己在業(yè)務(wù)開(kāi)發(fā)過(guò)程中,是否也是經(jīng)常要處理這些錯(cuò)誤情況?那么像 Vue3 這樣復(fù)雜的庫(kù),是否也是到處通過(guò) try...catch來(lái)處理異常呢?接下來(lái)一起看看。

二、實(shí)現(xiàn)簡(jiǎn)單的全局異常處理

在開(kāi)發(fā)插件或庫(kù)時(shí),我們可以通過(guò) try...catch封裝一個(gè)全局異常處理方法,將需要執(zhí)行的方法作為參數(shù)傳入,調(diào)用方只要關(guān)心調(diào)用結(jié)果,而無(wú)需知道該全局異常處理方法內(nèi)部邏輯。大致使用方法如下:

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

測(cè)試一下:

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
*/

可以看到,當(dāng)需要為方法做異常處理時(shí),只要將該方法作為參數(shù)傳入即可。但是上面示例跟實(shí)際業(yè)務(wù)開(kāi)發(fā)的邏輯差得有點(diǎn)多,實(shí)際業(yè)務(wù)中,我們經(jīng)常會(huì)遇到方法的嵌套調(diào)用,那么我們?cè)囈幌拢?/p>

const f1 = () => {
    console.log('[f1]')
    f2();
}

const f2 = () => {
    console.log('[f2]')
    f3();
}

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

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)
*/

這樣也是沒(méi)問(wèn)題的。那么接下來(lái)就是在 errorHandling方法的 catch分支實(shí)現(xiàn)對(duì)應(yīng)異常處理即可。接下來(lái)看看 Vue3 源碼中是如何處理的?

三、Vue3 如何實(shí)現(xiàn)異常處理

理解完上面示例,接下來(lái)看看在 Vue3 源碼中是如何實(shí)現(xiàn)異常處理的,其實(shí)現(xiàn)起來(lái)也是很簡(jiǎn)單。

  • 實(shí)現(xiàn)異常處理方法

在 errorHandling.ts 文件中定義了 callWithErrorHandling和 callWithAsyncErrorHandling兩個(gè)處理全局異常的方法。顧名思義,這兩個(gè)方法分別處理:

  • callWithErrorHandling:處理同步方法的異常;
  • callWithAsyncErrorHandling:處理異步方法的異常。

使用方式如下:

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

代碼實(shí)現(xiàn)大致如下:

// 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(); // 調(diào)用原方法
  } 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方法處理的邏輯比較簡(jiǎn)單,通過(guò)簡(jiǎn)單的 try...catch 做一層封裝。而 callWithAsyncErrorHandling 方法就比較巧妙,通過(guò)將需要執(zhí)行的方法傳入 callWithErrorHandling方法處理,并將其結(jié)果通過(guò) .catch方法進(jìn)行處理。

  • 處理異常

在上面代碼中,遇到報(bào)錯(cuò)的情況,都會(huì)通過(guò) handleError()處理異常。其實(shí)現(xiàn)大致如下:

// 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)
}

保留核心處理邏輯之后,可以看到這邊處理也是相當(dāng)簡(jiǎn)單,直接通過(guò)console.error(err)輸出錯(cuò)誤內(nèi)容。

  • 配置 errorHandler 自定義異常處理函數(shù)

在使用 Vue3 時(shí),也支持「指定自定義異常處理函數(shù)」,來(lái)處理「組件渲染函數(shù)」和「?jìng)陕?tīng)器執(zhí)行期間」拋出的未捕獲錯(cuò)誤。這個(gè)處理函數(shù)被調(diào)用時(shí),可獲取錯(cuò)誤信息和相應(yīng)的應(yīng)用實(shí)例。文檔參考:《errorHandler》 使用方法如下,在項(xiàng)目 main.js文件中配置:

// src/main.js

app.config.errorHandler = (err, vm, info) => {
  // 處理錯(cuò)誤
  // `info` 是 Vue 特定的錯(cuò)誤信息,比如錯(cuò)誤所在的生命周期鉤子
}

那么 errorHandler()是何時(shí)執(zhí)行的呢?我們繼續(xù)看看源碼中 handleError() 的內(nèi)容,可以發(fā)現(xiàn):

// 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 配置項(xiàng)
    const appErrorHandler = instance.appContext.config.errorHandler
    if (appErrorHandler) {
      callWithErrorHandling(
        appErrorHandler,
        null,
        ErrorCodes.APP_ERROR_HANDLER,
        [err, exposedInstance, errorInfo]
      )
      return
    }
  }
  logError(err, type, contextVNode, throwInDev)
}

通過(guò) instance.appContext.config.errorHandler取到全局配置的自定義錯(cuò)誤處理函數(shù),存在時(shí)則執(zhí)行,當(dāng)然,這邊也是通過(guò)前面定義的 callWithErrorHandling來(lái)調(diào)用。

  • 調(diào)用 errorCaptured 生命周期鉤子

在使用 Vue3 的時(shí)候,也可以通過(guò) errorCaptured生命周期鉤子來(lái)「捕獲來(lái)自后代組件的錯(cuò)誤」。文檔參考:《errorCaptured》入?yún)⑷缦拢?/p>

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

此鉤子會(huì)收到三個(gè)參數(shù):錯(cuò)誤對(duì)象、發(fā)生錯(cuò)誤的組件實(shí)例以及一個(gè)包含錯(cuò)誤來(lái)源信息的字符串。此鉤子可以返回 false以「阻止該錯(cuò)誤繼續(xù)向上傳播。」有興趣的同學(xué)可以通過(guò)文檔,「查看具體的錯(cuò)誤傳播規(guī)則」。使用方法如下,父組件監(jiān)聽(tīng) onErrorCaptured生命周期(示例代碼使用 Vue3 setup 語(yǔ)法):

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

子組件如下:

<template>
  <button @click="sendMessage">發(fā)送消息</button>
</template>

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

當(dāng)點(diǎn)擊「發(fā)送消息」按鈕,控制臺(tái)便輸出錯(cuò)誤:

[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生命周期鉤子正常執(zhí)行,并輸出子組件 Message.vue內(nèi)的異常。

那么這個(gè)又是如何實(shí)現(xiàn)呢?還是看 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) {
        // ②循環(huán)執(zhí)行 errorCaptured 中的每個(gè) Hook
        for (let i = 0; i < errorCapturedHooks.length; i++) {
          if (
            errorCapturedHooks[i](err, exposedInstance, errorInfo "i") === false
          ) {
            return
          }
        }
      }
      cur = cur.parent
    }
    // 省略其他代碼
  }
  logError(err, type, contextVNode, throwInDev)
}

這邊會(huì)先獲取 instance.parent作為當(dāng)前處理的組件實(shí)例進(jìn)行遞歸,每次將取出組件配置的 errorCaptured 生命周期方法的數(shù)組并循環(huán)調(diào)用其每一個(gè)鉤子,然后再取出當(dāng)前組件的父組件作為參數(shù),最后繼續(xù)遞歸調(diào)用下去。

  • 實(shí)現(xiàn)錯(cuò)誤碼和錯(cuò)誤消息

Vue3 還為異常定義了錯(cuò)誤碼和錯(cuò)誤信息,在不同的錯(cuò)誤情況有不同的錯(cuò)誤碼和錯(cuò)誤信息,讓我們能很方便定位到發(fā)生異常的地方。錯(cuò)誤碼和錯(cuò)誤信息如下:

// 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]: 'renderTracked hook',
  [LifecycleHooks.RENDER_TRIGGERED]: 'renderTriggered hook',
  [ErrorCodes.SETUP_FUNCTION]: 'setup function',
  [ErrorCodes.RENDER_FUNCTION]: 'render function',
  // 省略其他
  [ErrorCodes.SCHEDULER]:
    'scheduler flush. This is likely a Vue internals bug. ' +
    'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/vue-next'
}

當(dāng)不同錯(cuò)誤情況,根據(jù)錯(cuò)誤碼 ErrorCodes來(lái)獲取 ErrorTypeStrings錯(cuò)誤信息進(jìn)行提示:

// 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)
  }
}
  • 實(shí)現(xiàn) Tree Shaking

關(guān)于 Vue3 實(shí)現(xiàn) Tree Shaking 的介紹,可以看我之前寫(xiě)的高效實(shí)現(xiàn)框架和 JS 庫(kù)瘦身[7]。其中,logError 方法中就使用到了:

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

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

當(dāng)編譯成 production 環(huán)境后,__DEV__分支的代碼不會(huì)被打包進(jìn)去,從而優(yōu)化包的體積。

四、總結(jié)

到上面一部分,我們就差不多搞清楚 Vue3 中全局異常處理的核心邏輯了。我們?cè)陂_(kāi)發(fā)自己的錯(cuò)誤處理方法時(shí),也可以考慮這幾個(gè)核心點(diǎn):

  • 支持同步和異步的異常處理;
  • 設(shè)置業(yè)務(wù)錯(cuò)誤碼、業(yè)務(wù)錯(cuò)誤信息;
  • 支持自定義錯(cuò)誤處理方法;
  • 支持開(kāi)發(fā)環(huán)境錯(cuò)誤提示;
  • 支持 Tree Shaking。

這幾點(diǎn)在你設(shè)計(jì)插件的時(shí)候,都可以考慮進(jìn)去的~

到此這篇關(guān)于Vue實(shí)現(xiàn)全局異常處理的幾種方案的文章就介紹到這了,更多相關(guān)Vue 全局異常內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • vue表格顯示字符串過(guò)長(zhǎng)的問(wèn)題及解決

    vue表格顯示字符串過(guò)長(zhǎng)的問(wèn)題及解決

    這篇文章主要介紹了vue表格顯示字符串過(guò)長(zhǎng)的問(wèn)題及解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-10-10
  • Vue.js實(shí)現(xiàn)可編輯的表格

    Vue.js實(shí)現(xiàn)可編輯的表格

    這篇文章主要為大家詳細(xì)介紹了Vue.js實(shí)現(xiàn)可編輯的表格,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-12-12
  • axios實(shí)現(xiàn)文件上傳并獲取進(jìn)度

    axios實(shí)現(xiàn)文件上傳并獲取進(jìn)度

    這篇文章主要為大家詳細(xì)介紹了axios實(shí)現(xiàn)文件上傳并獲取進(jìn)度,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2019-09-09
  • Vue中使用better-scroll實(shí)現(xiàn)輪播圖組件

    Vue中使用better-scroll實(shí)現(xiàn)輪播圖組件

    better-scroll 是一款重點(diǎn)解決移動(dòng)端(已支持 PC)各種滾動(dòng)場(chǎng)景需求的插件。這篇文章主要介紹了Vue中使用better-scroll實(shí)現(xiàn)輪播圖組件的實(shí)例代碼,需要的朋友可以參考下
    2020-03-03
  • Vue實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車(chē)功能

    Vue實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車(chē)功能

    這篇文章主要為大家詳細(xì)介紹了Vue實(shí)現(xiàn)簡(jiǎn)單購(gòu)物車(chē)功能,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2020-12-12
  • Vue3-新特性defineOptions和defineModel示例詳解

    Vue3-新特性defineOptions和defineModel示例詳解

    在Vue3.3中新引入了defineOptions宏主要是用來(lái)定義Option API的選項(xiàng),可以用defineOptions定義任意的選項(xiàng),props、emits、expose、slots除外,本文給大家介紹Vue3-新特性defineOptions和defineModel,感興趣的朋友一起看看吧
    2023-11-11
  • vue項(xiàng)目中頁(yè)面底部出現(xiàn)白邊及空白區(qū)域錯(cuò)誤的問(wèn)題

    vue項(xiàng)目中頁(yè)面底部出現(xiàn)白邊及空白區(qū)域錯(cuò)誤的問(wèn)題

    這篇文章主要介紹了vue項(xiàng)目中頁(yè)面底部出現(xiàn)白邊及空白區(qū)域錯(cuò)誤的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-08-08
  • vue遞歸實(shí)現(xiàn)樹(shù)形組件

    vue遞歸實(shí)現(xiàn)樹(shù)形組件

    這篇文章主要為大家詳細(xì)介紹了vue遞歸實(shí)現(xiàn)樹(shù)形組件,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-07-07
  • Vue+Echarts繪制餅圖的示例詳解

    Vue+Echarts繪制餅圖的示例詳解

    這篇文章主要為大家詳細(xì)介紹了如何利用Vue和Echarts實(shí)現(xiàn)繪制餅圖,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-03-03
  • Object.assign觸發(fā)watch原理示例解析

    Object.assign觸發(fā)watch原理示例解析

    這篇文章主要為大家介紹了Object.assign觸發(fā)watch原理示例解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-11-11

最新評(píng)論