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

源碼剖析Vue3中如何進(jìn)行錯(cuò)誤處理

 更新時(shí)間:2024年01月29日 10:39:49   作者:云浪  
錯(cuò)誤處理是框架設(shè)計(jì)的核心要素之一,框架的錯(cuò)誤處理好壞,直接決定用戶應(yīng)用程序的健壯性以及用戶開發(fā)應(yīng)用時(shí)處理錯(cuò)誤的心智負(fù)擔(dān),本文將從源碼入手,剖析一下Vue3中是如何進(jìn)行錯(cuò)誤處理的,需要的可以參考下

錯(cuò)誤處理

錯(cuò)誤處理是框架設(shè)計(jì)的核心要素之一??蚣艿腻e(cuò)誤處理好壞,直接決定用戶應(yīng)用程序的健壯性以及用戶開發(fā)應(yīng)用時(shí)處理錯(cuò)誤的心智負(fù)擔(dān)。同時(shí),Vue 作為一個(gè)基礎(chǔ)地前端框架,也提供了統(tǒng)一地全局錯(cuò)誤處理接口,用戶可以通過該接口(errorhandler)注冊(cè)自定義的錯(cuò)誤處理程序來全局地處理框架產(chǎn)生的所有異常。

Vue3 中提供了兩種處理異常的函數(shù),同步異常處理函數(shù)(callWithErrorHandling)和異步異常處理函數(shù)(callWithAsyncErrorHandling)。異步異常處理函數(shù)是在同步異常處理函數(shù)的基礎(chǔ)上實(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()
  } catch (err) {
    handleError(err, instance, type)
  }
  return res
}

本篇文章的源碼均摘自 Vue.js 3.2.45

從整體上來看,callWithErrorHandling 函數(shù)的實(shí)現(xiàn)是比較簡(jiǎn)單的,就是對(duì) try catch 的封裝。catch 捕獲到的異常統(tǒng)一由 handleError 函數(shù)來處理。

// 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
) {
  if (__DEV__) {
    // 省略其他代碼
  } else {
    // recover in prod to reduce the impact on end-user
    console.error(err)
  }
}

在 handleError 中會(huì)將捕獲到的異常輸出來。

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

export function callWithAsyncErrorHandling(
  fn: Function | Function[],
  instance: ComponentInternalInstance | null,
  type: ErrorTypes,
  args?: unknown[]
): any[] {
  if (isFunction(fn)) {
    const res = callWithErrorHandling(fn, instance, type, args)
    if (res && isPromise(res)) {
      res.catch(err => {
        handleError(err, instance, type)
      })
    }
    return res
  }

  const values = []
  for (let i = 0; i < fn.length; i++) {
    values.push(callWithAsyncErrorHandling(fn[i], instance, type, args))
  }
  return values
}

異步異常處理函數(shù)(callWithAsyncErrorHandling),是基于同步異常處理函數(shù)(callWithErrorHandling)實(shí)現(xiàn)的,如果傳入的參數(shù)是函數(shù)數(shù)組(Function[]),則會(huì)遍歷這個(gè)函數(shù)數(shù)組,遞歸調(diào)用異步異常處理函數(shù)(callWithAsyncErrorHandling),將返回的結(jié)果保留到數(shù)組中(values),最后返回這個(gè)數(shù)組。

在異步異常處理函數(shù)中(callWithAsyncErrorHandling) ,它通過判斷傳入函數(shù)的返回值(res)是否為對(duì)象,并且是否有 then 、catch 回調(diào)來判斷返回值(res)是否為 Promise 實(shí)例。這種判斷是否為 Promise 實(shí)例的方式在我們平時(shí)的開發(fā)中也可以借鑒一下。

// packages/shared/src/index.ts

export const isFunction = (val: unknown): val is Function =>
  typeof val === 'function'

export const isObject = (val: unknown): val is Record<any, any> =>
  val !== null && typeof val === 'object'

// 判斷是否為 Promise 實(shí)例
export const isPromise = <T = any>(val: unknown): val is Promise<T> => {
  return isObject(val) && isFunction(val.then) && isFunction(val.catch)
}

Vue3 還提供了 API (errorHandler)用于給用戶注冊(cè)捕獲錯(cuò)誤的全局處理函數(shù),使用方法如下:

app.config.errorHandler = (err, instance, info) => {
  // 處理錯(cuò)誤,例如:報(bào)告給一個(gè)服務(wù)
}

該 API (errorHandler)在 handleError 函數(shù)中實(shí)現(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) {
    // 讀取用戶配置的全局錯(cuò)誤處理函數(shù)
    const appErrorHandler = instance.appContext.config.errorHandler
    if (appErrorHandler) {
      // 如果存在用戶配置的全局錯(cuò)誤處理函數(shù)則放入 callWithErrorHandling 中執(zhí)行
      callWithErrorHandling(
        appErrorHandler,
        null,
        ErrorCodes.APP_ERROR_HANDLER,
        [err, exposedInstance, errorInfo]
      )
      return
    }
  }
  // 省略其他代碼
}

Vue3 會(huì)判斷用戶是否配置了全局錯(cuò)誤處理函數(shù),如果配置了則會(huì)丟給內(nèi)置的同步異常處理函數(shù)執(zhí)行(callWithErrorHandling)。由于用戶配置的全局錯(cuò)誤處理函數(shù)執(zhí)行是給同步異常處理函數(shù)執(zhí)行的,因此,用戶在自定義全局錯(cuò)誤處理函數(shù)時(shí),要注意兼容異步錯(cuò)誤的情況,即最好在自定義全局處理函數(shù)中,加上對(duì)異步錯(cuò)誤代碼的處理,因?yàn)?Vue3 內(nèi)部沒法捕獲到異步的錯(cuò)誤。

如果要做前端的異常監(jiān)控,我們完全可以借助 errorHandler 函數(shù),完成前端異常的上報(bào)。

Vue3 還提供了在捕獲了后代組件傳遞的錯(cuò)誤時(shí)調(diào)用的生命周期鉤子(onErrorCaptured()):

function onErrorCaptured(callback: ErrorCapturedHook): void

type ErrorCapturedHook = (
  err: unknown,
  instance: ComponentPublicInstance | null,
  info: string
) => boolean | void

該生命周期鉤子(onErrorCaptured()) 也是在 handleError 函數(shù)實(shí)現(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) {
    // 1. 獲取父組件實(shí)例
    let cur = instance.parent
    const exposedInstance = instance.proxy
    const errorInfo = __DEV__ ? ErrorTypeStrings[type] : type
    // 2. 開啟 while 循環(huán),不斷向上遍歷,取得父組件實(shí)例
    while (cur) {
      // 3. 從父組件實(shí)例中獲取 onErrorCaptured 生命周期鉤子
      const errorCapturedHooks = cur.ec
      if (errorCapturedHooks) {
        // 4. 遍歷 onErrorCaptured 生命周期鉤子數(shù)組
        for (let i = 0; i < errorCapturedHooks.length; i++) {
          if (
            // 5. 執(zhí)行 onErrorCaptured 生命周期鉤子,
            // onErrorCaptured 返回 false 則退出 while 循環(huán),
            // 錯(cuò)誤會(huì)停止向上傳遞
            errorCapturedHooks[i](err, exposedInstance, errorInfo) === false
          ) {
            return
          }
        }
      }
      cur = cur.parent
    }
  }
}

由于 Vue3 的組件可以多次注冊(cè)同一個(gè)生命周期鉤子,所以 Vue3 內(nèi)部使用了數(shù)組來存儲(chǔ) onErrorCaptured 生命周期鉤子。Vue3 內(nèi)部會(huì)使用 while 循環(huán),不斷向上遍歷取得父組件實(shí)例的 onErrorCaptured 生命周期鉤子,然后遍歷執(zhí)行 onErrorCaptured 生命周期鉤子,如果 onErrorCaptured 生命周期鉤子返回 false ,則會(huì)退出 while 循環(huán),停止向上傳遞錯(cuò)誤。

錯(cuò)誤碼與錯(cuò)誤信息

前端在與后端進(jìn)行接口聯(lián)調(diào)的時(shí)候,肯定見過各種 HTTP 的狀態(tài)碼,比如 404(無法找到資源)、500(服務(wù)器內(nèi)部錯(cuò)誤)等。狀態(tài)碼的好處是可以讓用戶快速地知道當(dāng)前 HTTP 請(qǐng)求的狀態(tài),方便用戶排查錯(cuò)誤等。

因此,在 Vue 中,也定義了各種錯(cuò)誤碼(狀態(tài)碼),用來區(qū)分各種類型的錯(cuò)誤,方便開發(fā)者(Vue 用戶)排查錯(cuò)誤。

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

export const enum ErrorCodes {
  SETUP_FUNCTION,
  RENDER_FUNCTION,
  WATCH_GETTER,
  WATCH_CALLBACK,
  WATCH_CLEANUP,
  NATIVE_EVENT_HANDLER,
  COMPONENT_EVENT_HANDLER,
  VNODE_HOOK,
  DIRECTIVE_HOOK,
  TRANSITION_HOOK,
  APP_ERROR_HANDLER,
  APP_WARN_HANDLER,
  FUNCTION_REF,
  ASYNC_COMPONENT_LOADER,
  SCHEDULER
}

// 不同的狀態(tài)碼對(duì)應(yīng)不同的錯(cuò)誤信息
export const ErrorTypeStrings: Record<number | string, string> = {
  // 省略其他代碼
  [ErrorCodes.SETUP_FUNCTION]: 'setup function',
  [ErrorCodes.RENDER_FUNCTION]: 'render function',
  [ErrorCodes.WATCH_GETTER]: 'watcher getter',
  [ErrorCodes.WATCH_CALLBACK]: 'watcher callback',
  [ErrorCodes.WATCH_CLEANUP]: 'watcher cleanup function',
  [ErrorCodes.NATIVE_EVENT_HANDLER]: 'native event handler',
  [ErrorCodes.COMPONENT_EVENT_HANDLER]: 'component event handler',
  [ErrorCodes.VNODE_HOOK]: 'vnode hook',
  [ErrorCodes.DIRECTIVE_HOOK]: 'directive hook',
  [ErrorCodes.TRANSITION_HOOK]: 'transition hook',
  [ErrorCodes.APP_ERROR_HANDLER]: 'app errorHandler',
  [ErrorCodes.APP_WARN_HANDLER]: 'app warnHandler',
  [ErrorCodes.FUNCTION_REF]: 'ref function',
  [ErrorCodes.ASYNC_COMPONENT_LOADER]: 'async component loader',
  [ErrorCodes.SCHEDULER]:
    'scheduler flush. This is likely a Vue internals bug. ' +
    'Please open an issue at https://new-issue.vuejs.org/?repo=vuejs/core'
}

Vue 會(huì)根據(jù)不同的錯(cuò)誤碼取不同的錯(cuò)誤信息輸出,方便用戶排查錯(cuò)誤。

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

// 省略了一些無關(guān)的代碼
function logError(
  err: unknown,
  type: ErrorTypes,
  contextVNode: VNode | null,
  throwInDev = true
) {
  if (__DEV__) {
    // 根據(jù)錯(cuò)誤碼取出錯(cuò)誤信息輸出
    const info = ErrorTypeStrings[type]
    warn(`Unhandled error${info ? ` during execution of ${info}` : ``}`)
  } else {
    console.error(err)
  }
}

同時(shí)為了控制生產(chǎn)環(huán)境框架的代碼體積,利用了 Tree Shaking 機(jī)制,僅在開發(fā)環(huán)境(__DEV__)輸出內(nèi)部的錯(cuò)誤信息。

總結(jié)

錯(cuò)誤處理是框架設(shè)計(jì)的核心要素之一,Vue3 內(nèi)部提供了兩種處理異常的函數(shù),同步異常處理函數(shù)(callWithErrorHanding)和異步異常處理函數(shù)(callWithErrorHanding),異步異常處理函數(shù)、給用戶自定義的全局錯(cuò)誤處理函數(shù)、捕獲了子組件異常后調(diào)用的生命周期鉤子(onErrorCaptured)均是基于同步異常處理函數(shù)(callWithErrorHanding)實(shí)現(xiàn),而同步異常處理函數(shù)(callWithErrorHanding)本質(zhì)是對(duì) try catch 的封裝。

Vue3 還提供了錯(cuò)誤碼和對(duì)應(yīng)的錯(cuò)誤信息來幫助開發(fā)者(Vue 的用戶)快速地排查錯(cuò)誤。

從錯(cuò)誤處理到錯(cuò)誤碼和錯(cuò)誤信息,可以看出 Vue 的錯(cuò)誤處理做得是比較完善的。

到此這篇關(guān)于源碼剖析Vue3中如何進(jìn)行錯(cuò)誤處理的文章就介紹到這了,更多相關(guān)Vue3錯(cuò)誤處理內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論