亚洲激情专区-91九色丨porny丨老师-久久久久久久女国产乱让韩-国产精品午夜小视频观看

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Vue3如何進行全局異常處理

發布時間:2022-03-08 09:02:47 來源:億速云 閱讀:223 作者:iii 欄目:編程語言

本篇內容主要講解“Vue3如何進行全局異常處理”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“Vue3如何進行全局異常處理”吧!

Vue3如何進行全局異常處理

在開發組件庫或者插件,經常會需要進行全局異常處理,從而實現:

  • 全局統一處理異常;

  • 為開發者提示錯誤信息;

  • 方案降級處理等等。

那么如何實現上面功能呢? 本文先簡單實現一個異常處理方法,然后結合 Vue3 源碼中的實現詳細介紹,最后總結實現異常處理的幾個核心。

本文 Vue3 版本為 3.0.11

一、前端常見異常

對于前端來說,常見的異常比較多,比如:

  • JS 語法異常;

  • Ajax 請求異常;

  • 靜態資源加載異常;

  • Promise 異常;

  • iframe 異常;

  • 等等

最常用的比如:

1. window.onerror

通過 window.onerror文檔可知,當 JS 運行時發生錯誤(包括語法錯誤),觸發 window.onerror()

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

函數參數:

  • message:錯誤信息(字符串)。可用于HTML onerror=""處理程序中的 event

  • source:發生錯誤的腳本URL(字符串)

  • lineno:發生錯誤的行號(數字)

  • colno:發生錯誤的列號(數字)

  • error:Error對象(對象)

若該函數返回true,則阻止執行默認事件處理函數。

2.  try...catch 異常處理

另外,我們也經常會使用 try...catch 語句處理異常:

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

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

3. 思考

大家可以思考下,自己在業務開發過程中,是否也是經常要處理這些錯誤情況? 那么像 Vue3 這樣復雜的庫,是否也是到處通過 try...catch來處理異常呢? 接下來一起看看。

二、實現簡單的全局異常處理

在開發插件或庫時,我們可以通過 try...catch封裝一個全局異常處理方法,將需要執行的方法作為參數傳入,調用方只要關心調用結果,而無需知道該全局異常處理方法內部邏輯。 大致使用方法如下:

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

測試一下:

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('[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)
*/

這樣也是沒問題的。那么接下來就是在 errorHandling方法的 catch分支實現對應異常處理即可。 接下來看看 Vue3 源碼中是如何處理的?

三、Vue3 如何實現異常處理

理解完上面示例,接下來看看在 Vue3 源碼中是如何實現異常處理的,其實現起來也是很簡單。

1. 實現異常處理方法

errorHandling.ts 文件中定義了 callWithErrorHandlingcallWithAsyncErrorHandling兩個處理全局異常的方法。 顧名思義,這兩個方法分別處理:

  • 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 做一層封裝。 而 callWithAsyncErrorHandling 方法就比較巧妙,通過將需要執行的方法傳入 callWithErrorHandling方法處理,并將其結果通過 .catch方法進行處理。

2. 處理異常

在上面代碼中,遇到報錯的情況,都會通過 handleError()處理異常。其實現大致如下:

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

保留核心處理邏輯之后,可以看到這邊處理也是相當簡單,直接通過 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生命周期鉤子來捕獲來自后代組件的錯誤。 如下:

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

此鉤子會收到三個參數:錯誤對象、發生錯誤的組件實例以及一個包含錯誤來源信息的字符串。 此鉤子可以返回 false阻止該錯誤繼續向上傳播。有興趣的同學可以通過文檔,查看具體的錯誤傳播規則。 使用方法如下,父組件監聽 onErrorCaptured生命周期(示例代碼使用 Vue3 setup 語法):

<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">發送消息</button>
</template>

<script setup>
// Message.vue
const sendMessage = () => {
  throw new Error('[test onErrorCaptured]')
}
</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]: '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'
}

當不同錯誤情況,根據錯誤碼 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如何進行全局異常處理”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

vue
AI

宜城市| 登封市| 济宁市| 永康市| 三门县| 大安市| 肥西县| 宣城市| 巴林左旗| 保亭| 富裕县| 墨脱县| 台南县| 集贤县| 高雄市| 高唐县| 祁阳县| 临桂县| 梓潼县| 丰县| 扬中市| 濮阳市| 河南省| 浦东新区| 明光市| 松江区| 吉水县| 开封市| 延庆县| 南阳市| 文昌市| 霍州市| 巴里| 藁城市| 玛曲县| 连平县| 江达县| 崇明县| 桐梓县| 芜湖县| 长丰县|