js錯誤的實質,也是發出一個事件,處理他javascript
對象屬性html
對象類型(7種)前端
SyntaxError
對象是解析代碼時發生的語法錯誤 ReferenceError
對象是引用一個不存在的變量時發生的錯誤RangeError
對象是一個值超出有效範圍時發生的錯誤(一是數組長度爲負數,二是Number
對象的方法參數超出範圍,以及函數堆棧超過最大值)TypeError
對象是變量或參數不是預期類型時發生的錯誤:對字符串、布爾值、數值等原始類型的值使用new
命令URIError
對象是 URI 相關函數的參數不正確時拋出的錯誤:使用函數不當eval
函數沒有被正確執行時,會拋出EvalError
錯誤 - 再也不使用,爲了代碼兼容自定義錯誤vue
function UserError(message) { this.message = message || '默認信息'; this.name = 'UserError'; } UserError.prototype = new Error(); UserError.prototype.constructor = UserError; new UserError('這是自定義的錯誤!');
try..catch…finallyjava
window.onerrornode
JS
運行時錯誤發生時,window
會觸發一個 ErrorEvent
接口的 error
事件參數git
/** * @param {String} message 錯誤信息 * @param {String} source 出錯文件 * @param {Number} lineno 行號 * @param {Number} colno 列號
*/ window.onerror = function(message, source, lineno, colno, error) { console.log('捕獲到異常:',{message, source, lineno, colno, error}); } ```
函數只有在返回
true 的時候,異常纔不會向上拋出,不然即便是知道異常的發生控制檯仍是會顯示
Uncaught Error: xxxxxonerror
最好寫在全部 JS
腳本的前面,不然有可能捕獲不到錯誤;(捕獲的是全局錯誤)Event
接口的 error
事件,並執行該元素上的onerror()
處理函數,有瀏覽器兼容問題)注意:只能捕獲沒法冒泡github
window.addEventListener('error', (error) => { console.log('捕獲到異常:', error); }, true) // 必定要加true,捕獲但不冒泡
Script error跨域的靜態資源加載異常捕獲(cdn文件等)數據庫
跨域文件只會報Script error,沒有詳細信息,怎麼解決:express
<script src="http://jartto.wang/main.js" crossorigin></script>
使用window.onerror
<iframe src="./iframe.html" frameborder="0"></iframe> <script> window.frames[0].onerror = function (message, source, lineno, colno, error) { console.log('捕獲到 iframe 異常:',{message, source, lineno, colno, error}); return true; }; </script>
catch
的 Promise
中拋出的錯誤沒法被 onerror
或 try-catch
捕獲到爲了防止有漏掉的 Promise
異常,建議在全局增長一個對 unhandledrejection
的監聽,用來全局監聽Uncaught Promise Error
window.addEventListener("unhandledrejection", function(e){ console.log(e); });
VUE errorHandler
Vue.config.errorHandler = (err, vm, info) => { console.error('經過vue errorHandler捕獲的錯誤'); console.error(err); console.error(vm); console.error(info); }
新概念Error boundary(React16):UI的某部分引發的 JS
錯誤不會破壞整個程序(只有 class component
能夠成爲一個 error boundaries
)
不會捕獲如下錯誤
1.事件處理器 2.異步代碼 3.服務端的渲染代碼 4.在 error boundaries 區域內的錯誤
Eg: 全局一個error boundary
組件就夠用啦
class ErrorBoundary extends React.Component { constructor(props) { super(props); this.state = { hasError: false }; } componentDidCatch(error, info) { // Display fallback UI this.setState({ hasError: true }); // You can also log the error to an error reporting service logErrorToMyService(error, info); } render() { if (this.state.hasError) { // You can render any custom fallback UI return <h1>Something went wrong.</h1>; } return this.props.children; } } <ErrorBoundary> <MyWidget /> </ErrorBoundary>
卡頓
JS
可能沒法及時執行解決:window
對象的 load
和 beforeunload
事件實現了網頁崩潰的監控
window.addEventListener('load', function () { sessionStorage.setItem('good_exit', 'pending'); setInterval(function () { sessionStorage.setItem('time_before_crash', new Date().toString()); }, 1000); }); window.addEventListener('beforeunload', function () { sessionStorage.setItem('good_exit', 'true'); }); if(sessionStorage.getItem('good_exit') && sessionStorage.getItem('good_exit') !== 'true') { /* insert crash logging code here */ alert('Hey, welcome back from your crash, looks like you crashed on: ' + sessionStorage.getItem('time_before_crash')); }
崩潰
JS
都不運行了,還有什麼辦法能夠監控網頁的崩潰,並將網頁崩潰上報呢解決:Service Worker
來實現網頁崩潰的監控
Service Worker
有本身獨立的工做線程,與網頁區分開,網頁崩潰了,Service Worker
通常狀況下不會崩潰;Service Worker
生命週期通常要比網頁還要長,能夠用來監控網頁的狀態;navigator.serviceWorker.controller.postMessage API
向掌管本身的 SW
發送消息。Ajax
發送數據Ajax
請求自己也有可能會發生異常,並且有可能會引起跨域問題,通常狀況下更推薦使用動態建立 img
標籤的形式進行上報。img
標籤的形式 更經常使用,簡單,無跨越問題 function report(error) { let reportUrl = 'http://jartto.wang/report'; new Image().src = `${reportUrl}?logs=${error}`; }
Reporter.send = function(data) { // 只採集 30% if(Math.random() < 0.3) { send(data) // 上報錯誤信息 } }
sentry 是一個實時的錯誤日誌追蹤和聚合平臺,包含了上面 sourcemap 方案,並支持更多功能,如:錯誤調用棧,log 信息,issue管理,多項目,多用戶,提供多種語言客戶端等,
這裏不過多敘述,以後在搭建sentry服務時,會再補篇博文
全棧開發,後端採用express庫,在這裏補充一下,node服務的錯誤處理方案
錯誤分類
步驟
1. 構建一個自定義 Error 構造函數:讓咱們方便地得到堆棧跟蹤
class CustomError extends Error { constructor(code = 'GENERIC', status = 500, ...params) { super(...params) if (Error.captureStackTrace) { Error.captureStackTrace(this, CustomError) } this.code = code this.status = status } } module.exports = CustomError
2.處理路由:對於每個路由,咱們要有相同的錯誤處理行爲
wT:在默認狀況下,因爲路由都是封裝的,因此 Express 並不真正支持那種方式
解決:實現一個路由處理程序,並把實際的路由邏輯定義爲普通的函數。這樣,若是路由功能(或任何內部函數)拋出一個錯誤,它將返回到路由處理程序,而後能夠傳給前端
const express = require('express') const router = express.Router() const CustomError = require('../CustomError') router.use(async (req, res) => { try { const route = require(`.${req.path}`)[req.method] try { const result = route(req) // We pass the request to the route function res.send(result) // We just send to the client what we get returned from the route function } catch (err) { /* This will be entered, if an error occurs inside the route function. */ if (err instanceof CustomError) { /* In case the error has already been handled, we just transform the error to our return object. */ return res.status(err.status).send({ error: err.code, description: err.message, }) } else { console.error(err) // For debugging reasons // It would be an unhandled error, here we can just return our generic error object. return res.status(500).send({ error: 'GENERIC', description: 'Something went wrong. Please try again or contact support.', }) } } } catch (err) { /* This will be entered, if the require fails, meaning there is either no file with the name of the request path or no exported function with the given request method. */ res.status(404).send({ error: 'NOT_FOUND', description: 'The resource you tried to access does not exist.', }) } }) module.exports = router // 實際路由文件 const CustomError = require('../CustomError') const GET = req => { // example for success return { name: 'Rio de Janeiro' } } const POST = req => { // example for unhandled error throw new Error('Some unexpected error, may also be thrown by a library or the runtime.') } const DELETE = req => { // example for handled error throw new CustomError('CITY_NOT_FOUND', 404, 'The city you are trying to delete could not be found.') } const PATCH = req => { // example for catching errors and using a CustomError try { // something bad happens here throw new Error('Some internal error') } catch (err) { console.error(err) // decide what you want to do here throw new CustomError( 'CITY_NOT_EDITABLE', 400, 'The city you are trying to edit is not editable.' ) } } module.exports = { GET, POST, DELETE, PATCH, }
3.構建全局錯誤處理機制
process.on('uncaughtException', (error: any) => { logger.error('uncaughtException', error) }) process.on('unhandledRejection', (error: any) => { logger.error('unhandledRejection', error) })
1.可疑區域增長 Try-Catch
2.全局監控 JS
異常 window.onerror
3.全局監控靜態資源異常 window.addEventListener
4.捕獲沒有 Catch
的 Promise
異常:unhandledrejection
5.VUE errorHandler
和 React componentDidCatch
6.監控網頁崩潰:window
對象的 load
和 beforeunload
7.跨域 crossOrigin
解決