Это 89-я оригинальная статья без воды.Если вы хотите получить больше оригинальных статей, выполните поиск в общедоступном аккаунте и подпишитесь на нас~ Эта статья была впервые опубликована в блоге Zhengcaiyun:Фронтенд-захват и обработка исключений
Кнопки не нажимаются, элементы не отображаются, страница пустая — все это сценарии, которые мы не хотим видеть на фронтенде. В процессе работы компьютерной программы всегда будут различные исключения. Поговорим о том, что такое исключения и как с ними бороться.
Введение
Что такое исключение, исключение — это неожиданное событие, которое часто влияет на корректную работу программы. Например, следующие сценарии:
-
Элемент страницы ненормальный (например, кнопка не нажимается, элемент не отображается)
-
страница застряла
-
белый экран
Эти ситуации сильно влияют на пользовательский опыт. Что касается внешнего интерфейса, хотя исключения не приводят к простою компьютера, они часто вызывают блокировку операций пользователя. Хотя исключения невозможно полностью исключить, есть веские причины понимать исключения и учиться с ними обращаться.
Важность обработки исключений в программировании неоспорима. Любое влиятельное веб-приложение нуждается в комплексном наборе механизмов обработки исключений, но на практике только серверная команда обычно вкладывает много энергии в механизмы обработки исключений. Хотя обработка исключений для клиентских приложений не менее важна, только в последние несколько лет к ней стали относиться серьезно. Как выдающиеся фронтенд-разработчики нового века, мы должны понимать, что такое исключения и какие средства и инструменты доступны нам, когда возникают исключения.
2. Классификация аномалий
По сути, исключение — это структура данных, в которой хранится соответствующая информация при возникновении исключения, например коды ошибок, сообщения об ошибках и т. д. Свойство сообщения является единственным свойством, которое гарантированно поддерживается всеми браузерами.Кроме того, IE, Firefox, Safari, Chrome и Opera добавляют в объект события другую важную информацию. Например, IE добавляет свойство описания, точно такое же, как свойство сообщения, а также добавляет свойство числа, которое содержит количество внутренних ошибок. Firefox добавляет имя файла, номер строки и стек (включая свойства стека). Таким образом, при рассмотрении совместимости с браузерами лучше просто использовать свойство сообщения.
Есть много типов ошибок, которые могут возникнуть во время выполнения JS. Каждая ошибка имеет соответствующий тип ошибки, и при возникновении ошибки создается соответствующий объект ошибки. Следующие семь типов ошибок определены в ECMA-262:
- Ошибка: базовый класс для ошибок, другие ошибки наследуются от этого типа.
- EvalError: исключение выполнения функции Eval
- RangeError: массив выходит за пределы
- ReferenceError: это исключение возникает при попытке сослаться на неопределенную переменную
- SyntaxError: необоснованный разбор синтаксиса
- TypeError: ошибка типа, используемая для представления ошибки, которая возникает, когда тип значения не соответствует ожидаемому типу.
- URIError: ошибка, вызванная неправильным использованием глобального обработчика URI.
3. Обработка исключений
Оператор try-catch был представлен в версии 3 стандарта ECMA-262 как стандартный способ обработки исключений в JavaScript.Основной синтаксис показан ниже. Это точно так же, как оператор try-catch в Java.
try {
// 可能会导致错误的代码
} catch (error) {
// 在错误发生时怎么处理
}
Если в каком-либо коде в блоке try возникает ошибка, выполнение кода немедленно прекращается и выполняется блок catch. В этот момент блок catch получит объект, содержащий информацию об ошибке.Информация, содержащаяся в этом объекте, варьируется от браузера к браузеру, но обычно существует свойство сообщения, которое содержит информацию об ошибке.
Предложение finally является необязательным в операторе try-catch, но как только оно используется, его код все равно выполняется. Другими словами, если код в блоке try выполняется нормально, предложение finally будет выполнено; если оператор catch выполняется из-за ошибки, предложение finally все равно будет выполнено. Пока код содержит предложение finally, независимо от того, какой код содержится в операторе try или catch, даже в операторе return, это не помешает выполнению предложения finally. Взгляните на результат выполнения следующей функции:
function testFinally {
try {
return "出去玩";
} catch (error) {
return "看电视";
} finally {
return "做作业";
}
return "睡觉";
}
Вызов этой функции якобы возвращает «тусоваться», потому что оператор, возвращающий «выходи и играй», находится в блоке try и выполняется без ошибок. На самом деле возвращается «сделать домашнюю работу», поскольку в конце есть предложение finally, результат приведет к тому, что оператор return в блоке try будет проигнорирован, что означает, что результат вызова может возвращать только «сделать домашнюю работу». . Если оператор finally будет удален, функция вернет «иди и играй». Поэтому, прежде чем использовать предложение finally, четко определите, как должен выглядеть ваш код. (Подумайте о том, может ли быть выдано исключение блока catch, если и блок catch, и блок finally выбрасывают исключения)
Но, к сожалению, попробуй не может обрабатывать асинхронный код и некоторые другие сценарии. Далее позвольте мне проанализировать несколько ненормальных сценариев и их решения.
В-четвертых, аномальный анализ
1. Ошибка кода JS
Ниже приведен снимок экрана стека вызовов ежедневной ошибки, о которой сообщает наша внутренняя платформа мониторинга ошибок:
Ошибка все еще относительно очевидна, это указывает на причину проблемы. Когда onOk использует обычную функцию, этот контекст оператора выполнения в функции является экземпляром компонента Antd.Modal, а метод changeFilterType не существует в компоненте Antd.Modal. Измените метод onOK на функцию стрелки, такую как метод onCancel, и укажите ее на родительский компонент.
TypeErrorТипы часто встречаются в JavaScript и могут вызвать эту ошибку при сохранении неожиданного типа в переменной или при доступе к несуществующему методу. Причины ошибки разные, но сводится она к тому, что тип переменной не соответствует требованиям при выполнении определенного вида операции. Давайте рассмотрим еще несколько примеров:
class People {
constructor(name) {
this.name = name;
}
sing() {}
}
const xiaoming = new People("小明");
xiaoming.dance(); // 抛出 TypeError
xiaoming.girlfriend.name; // 抛出 TypeError
Ошибки кода обычно обнаруживаются на этапах разработки и тестирования. Его также можно поймать с помощью try-catch:
// 代码
try {
xiaoming.girlfriend.name;
} catch (error) {
console.log(xiaoming.name + "没有女朋友", error);
}
// 运行结果
// 小明没有女朋友 TypeError: Cannot read property 'name' of undefined
2. Синтаксическая ошибка JS
Модифицируем код, меняем английскую точку с запятой на китайскую:
try {
xiaoming.girlfriend.name;// 结尾是中文分号
} catch(error) {
console.log(xiaoming.name + "没有女朋友", error);
}
// 运行结果
// Uncaught SyntaxError: Invalid or unexpected token
**SyntaxError ** Мы не можем отлавливать синтаксические ошибки с помощью try-catch, но синтаксические ошибки можно увидеть на этапе разработки, и он не сможет плавно перейти в онлайн.
Тем не менее, всегда есть исключения. Вы все еще можете получать некоторые предупреждения о грамматических ошибках в Интернете, но большинство из них вызваны ошибками синтаксического анализа JSON и совместимостью браузера.
Давайте рассмотрим еще несколько примеров:
JSON.parse('{name:xiaoming}'); // Uncaught SyntaxError: Unexpected token n in JSON at position 1
JSON.parse('{"name":xiaoming}'); // Uncaught SyntaxError: Unexpected token x in JSON at position 8
JSON.parse('{"name":"xiaoming"}'); // 正常
var testFunc () => { }; // 在 IE 下会抛出 SyntaxError,因为 IE 不支持箭头函数,需要通过Babel等工具事先转译下
Исключением при разборе с помощью JSON.parse является хорошее использование try-catch:
try {
JSON.parse(remoteData); // remoteData 为服务端返回的数据
} catch {
console.error("服务端数据格式返回异常,无法解析", remoteData);
}
Это не конец отлова ошибки.После отлова ошибки нам нужно подумать о том, когда она возникает:
-
Является ли ошибка фатальной, приведет ли она к другим сопутствующим ошибкам
-
Может ли последующая логика кода продолжать выполняться, и может ли пользователь продолжать работу
-
Нужно ли возвращать сообщение об ошибке пользователю, предлагая пользователю, как справиться с ошибкой
-
Нужно ли сообщать об ошибке на сервер
В соответствии с вышеуказанными проблемами здесь будет много решений, таких как:
- Если это вызвано неизвестным исключением сервера, вы можете заблокировать операции пользователя, а всплывающее окно предложит пользователю «Исключение сервера, повторите попытку позже». И предоставить пользователю кнопку обновления;
try {
return JSON.parse(remoteData);
} catch (error) {
Modal.fail("服务器异常,请稍后重试");
return false;
}
- Если это вызвано аномалией данных, пользовательские операции могут быть заблокированы, а всплывающее окно предлагает пользователю «Сервер неисправен, обратитесь в службу поддержки для обработки ~», и информация об ошибке передается на ненормальный сервер, и разработчик может определить причину проблемы с помощью стека исключений и скрытых пользователем точек;
try {
return JSON.parse(remoteData);
} catch (error) {
Modal.fail("服务器异常,请联系客服处理~");
logger.error("JSON数据解析出现异常", error);
return false;
}
- Если ошибка относится к ожидаемому случаю анализа данных, но также есть альтернативное значение по умолчанию, то значение по умолчанию может быть устранено непосредственно при ошибке;
try {
return JSON.parse(remoteData);
} catch (error) {
console.error("服务端数据格式返回异常,使用本地缓存数据", erorr);
return localData;
}
Одной из наиболее важных частей любой стратегии обработки ошибок является определение того, является ли ошибка фатальной.
3. Асинхронные ошибки
try {
setTimeout(() => {
undefined.map(v => v);
}, 1000)
} catch(e) {
console.log("捕获到异常:", e);
}
Uncaught TypeError: Cannot read property 'map' of undefined
at <anonymous>:3:15
не поймал исключение,try-catch
Он бессилен отлавливать синтаксические и асинхронные ошибки, и на это нужно обратить особое внимание.
Пять, захват исключений
5.1 window.onerror
когдаJS
Когда возникает ошибка времени выполнения,window
ТриггерErrorEvent
интерфейсerror
событие и выполнитьwindow.onerror()
.
/**
* @param {String} message 错误信息
* @param {String} source 出错文件
* @param {Number} lineno 行号
* @param {Number} colno 列号
* @param {Object} error Error对象(对象)
*/
window.onerror = function (message, source, lineno, colno, error) {
console.log("捕获到异常:", { message, source, lineno, colno, error });
};
Ошибки синхронизации могут быть обнаружены, однако обратите вниманиеwindow.error
Невозможно перехватить исключения статических ресурсов и ошибки кода JS.
5.2 Исключение загрузки статических ресурсов
Способ 1: OneError для захвата
<script>
function errorHandler(error) {
console.log("捕获到静态资源加载异常", error);
}
</script>
<script src="http://cdn.xxx.com/js/test.js" onerror="errorHandler(this)"></script>
<link rel="stylesheet" href="http://cdn.xxx.com/styles/test.css" onerror="errorHandler(this)">
Таким способом можно получить ошибку статических ресурсов, но недостаток очевиден, слишком навязчивый код, и к каждому тегу статического ресурса нужно добавлять метод onerror.
Способ 2: addEventListener("ошибка")
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>error</title>
<script>
window.addEventListener('error', (error) => {
console.log('捕获到异常:', error);
}, true)
</script>
</head>
<body>
<img src="https://itemcdn.zcycdn.com/15af41ec-e6cb-4478-8fad-1a47402f0f25.png">
</body>
</html>
Поскольку исключения сетевых запросов не всплывают, они должны быть перехвачены на этапе захвата.Однако, хотя этот метод может перехватывать исключения сетевых запросов, невозможно определить, является ли статус HTTP 404 или другим, например 500 и т. д. Таким образом, необходимо сотрудничать с журналом сервера для исследования и анализа.
5.3 Исключения обещаний
Исключение в Promise не может быть перехвачено с помощью try-catch и window.onerror.В настоящее время нам нужно прослушивать unhandledrejection, чтобы помочь нам перехватить эту часть ошибки.
window.addEventListener("unhandledrejection", function (e) {
e.preventDefault();
console.log("捕获到 promise 错误了");
console.log("错误的原因是", e.reason);
console.log("Promise 对象是", e.promise);
return true;
});
Promise.reject("promise error");
new Promise((resolve, reject) => {
reject("promise error");
});
new Promise((resolve) => {
resolve();
}).then(() => {
throw "promise error";
});
5.4 Реагировать на исключения
React обрабатывает исключения по-другому. Хотя try-catch работает со многими нетривиальными приложениями JavaScript, он работает только с императивным кодом. Поскольку компоненты React являются декларативными, try-catch не является надежным вариантом. Чтобы компенсировать это, React реализует так называемые границы ошибок. Границы ошибок — это компоненты React, которые «отлавливают ошибки JavaScript в любом месте дерева подкомпонентов», а также регистрируют ошибки и отображают резервный пользовательский интерфейс.
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false };
}
componentDidCatch(error, info) {
// 展示出错的UI
this.setState({ hasError: true });
// 将错误信息上报到日志服务器
logErrorToMyService(error, info);
}
render() {
if (this.state.hasError) {
// 可以展示自定义的错误样式
return <h1>Something went wrong.</h1>;
}
return this.props.children;
}
}
Обратите внимание, однако, что границы ошибок не перехватывают следующие ошибки:
-
обработчик события
-
асинхронный код
-
Код рендеринга на стороне сервера
-
ошибки в границах ошибок
Мы можем использовать ErrorBoundary следующим образом:
<ErrorBoundary>
<MyWidget />
</ErrorBoundary
5.5 Исключения Vue
Vue.config.errorHandler = (err, vm, info) => {
console.error("通过vue errorHandler捕获的错误");
console.error(err);
console.error(vm);
console.error(info);
};
5.6 Исключение запроса
Возьмем в качестве примера наиболее часто используемую библиотеку HTTP-запросов axios, чтобы смоделировать ситуацию, когда интерфейс отвечает на 401:
// 请求
axios.get(/api/test/401")
// 结果
Uncaught (in promise) Error: Request failed with status code 401
at createError (axios.js:1207)
at settle (axios.js:1177)
at XMLHttpRequest.handleLoad (axios.js:1037)
Видно, что исключение axios можно обрабатывать как исключение Promise:
// 请求
axios.get("http://localhost:3000/api/uitest/sentry/401")
.then(data => console.log('接口请求成功', data))
.catch(e => console.log('接口请求出错', e));
// 结果
接口请求出错 Error: Request failed with status code 401
at createError (createError.js:17)
at settle (settle.js:18)
at XMLHttpRequest.handleLoad (xhr.js:62)
Как правило, интерфейс 401 означает, что пользователь не вошел в систему, и ему нужно перейти на страницу входа, чтобы позволить пользователю снова войти в систему.Однако, если каждый метод запроса должен написать логику повторного перехода на страницу входа , это будет очень хлопотно, поэтому это будет рассмотрено в это время.Перехватчик axios используется для унифицированной сортировки.Так же исключения, которые могут быть обработаны единообразно, также могут быть обработаны в перехватчике.
// Add a response interceptor
axios.interceptors.response.use(
function (response) {
// Any status codes that falls outside the range of 2xx cause this function to trigger
// Do something with response error
},
function (error) {
if (error.response.status === 401) {
goLogin(); // 跳转登录页
} else if (error.response.status === 502) {
alert(error.response.data.message || "系统升级中,请稍后重试");
}
return Promise.reject(error.response);
}
);
5.7 Резюме
Существует семь типов исключений, и при их обработке необходимо различать фатальные и нефатальные ошибки.
-
Увеличение подозрительного района
try-catch
-
Глобальный мониторинг
JS
аномальныйwindow.onerror
-
Глобальный мониторинг исключений статических ресурсов
window.addEventListener
-
захват нет
catch
изPromise
Для исключенияunhandledrejection
-
Vue errorHandler
а такжеReact componentDidCatch
-
Axios
Перехватчик для запроса единой обработки исключенийinterceptors
-
Собирайте информацию об ошибках пользователей с помощью службы мониторинга журналов.
6. Ненормальная отчетность
Даже после того, как наша разработка внешнего интерфейса будет завершена, будет проведена серия предварительных проверок веб-приложений, таких как самотестирование, тестирование QA, проверка кода и т. д., чтобы гарантировать, что приложение может быть создано без аварий.
Но это имело неприятные последствия. Много раз мы получали онлайн-вопросы от клиентов. Иногда эти проблемы могут быть проблемами с вашим собственным кодом. Такие проблемы обычно можно воспроизвести в тестовой среде, и мы можем быстро определить ключевую позицию проблемы. Тем не менее, много раз были некоторые проблемы, которые мы не нашли в тесте, но некоторые люди появлялись в Интернете. Проблема действительно существует. В настоящее время наша тестовая среда не может быть воспроизведена, и есть некоторые случайные производственные случайные проблемы, эти проблемы трудно найти причину проблемы, что создает головную боль для разработчиков интерфейса.
И мы не можем каждый раз удаленно решать проблему за пользователя или позволять пользователю нажимать F12, чтобы открыть консоль браузера и предоставить нам снимок экрана с сообщением об ошибке. В настоящее время мы должны использовать некоторые инструменты для решения этой серии головных болей.
Интерфейсная система журнала мониторинга ошибок рождается из приложения. Когда ошибка возникает во внешнем коде во время производственной операции, она передается в систему мониторинга в первый раз, чтобы обнаружить и решить проблему в первый раз.
Есть много зрелых решений на выбор: ARMS, fundebug, BadJS, Sentry. Zhengcaiyun в настоящее время использует версию Sentry с открытым исходным кодом и вносит некоторые изменения в сочетании с бизнесом:
- В сочетании с системой сборки проект Sentry автоматически генерируется при сборке проекта и внедряется скрипт Sentry.
- После того, как клиентская сторона введет клиентский скрипт Sentry, настройте правила фильтрации тревожных событий в соответствии с различными уровнями детализации, такими как элементы и страницы.
- Подключайтесь к системе обмена сообщениями DingTalk и отправляйте тревожные сообщения группам подписки.
- Фильтрация ошибок интерфейса и оптимизация отчетов об ошибках Promise
В дальнейшем вы также можете представить отдельное введение о том, как объединить систему мониторинга ошибок с открытым исходным кодом для создания системы мониторинга с характеристиками компании.
Рекомендуемое чтение
Плагинная схема загрузки компонентов формы динамических форм
Написание высококачественного поддерживаемого кода: изящное именование
Карьера
ZooTeam, молодая, увлеченная и творческая команда, связанная с отделом исследований и разработок продукции Zhengcaiyun, базируется в живописном Ханчжоу. В настоящее время в команде более 40 фронтенд-партнеров, средний возраст которых составляет 27 лет, и почти 30% из них — инженеры полного стека, настоящая молодежная штурмовая группа. В состав членов входят «ветераны» солдат из Ali и NetEase, а также первокурсники из Чжэцзянского университета, Университета науки и технологий Китая, Университета Хандянь и других школ. В дополнение к ежедневным деловым связям, команда также проводит технические исследования и фактические боевые действия в области системы материалов, инженерной платформы, строительной платформы, производительности, облачных приложений, анализа и визуализации данных, а также продвигает и внедряет ряд внутренних технологий. Откройте для себя новые горизонты передовых технологических систем.
Если вы хотите измениться, вас забрасывают вещами, и вы надеетесь начать их бросать; если вы хотите измениться, вам сказали, что вам нужно больше идей, но вы не можете сломать игру; если вы хотите изменить , у вас есть возможность добиться этого результата, но вы не нужны; если вы хотите изменить то, чего хотите достичь, вам нужна команда для поддержки, но вам некуда вести людей; если вы хотите изменить установившийся ритм, это будет "5 лет рабочего времени и 3 года стажа работы"; если вы хотите изменить исходный Понимание хорошее, но всегда есть размытие того слоя оконной бумаги.. , Если вы верите в силу веры, верьте, что обычные люди могут достичь необыкновенных вещей, и верьте, что они могут встретить лучшего себя. Если вы хотите участвовать в процессе становления бизнеса и лично способствовать росту фронтенд-команды с глубоким пониманием бизнеса, надежной технической системой, технологиями, создающими ценность, и побочным влиянием, я думаю, что мы должны говорить. В любое время, ожидая, пока вы что-нибудь напишете, отправьте это наZooTeam@cai-inc.com