Почему вы должны справляться со своими ошибками, история из окопов
Есть как минимум две причины, по которым вы хотели бы настроить пользовательскую обработку ошибок в своем JS-проекте: Одна прагматичная, а другая пугающая.
В первом случае могут быть ошибки, которые, как вы знаете, произойдут, и вы просто хотите что-то исправить, когда это произойдет. Допустим, у вас есть компонент, который определенно ломается в очень старых браузерах, и вы просто хотите отключить его всякий раз, когда он дает сбой для какого-то пользователя IE9. Хорошо, настройте для этого обработчик ошибок.
Самый страшный случай - это возникающие ошибки, о которых вы не знаете.
Время рассказа: я собирался на конференцию Strange Loop в Сент-Луисе и заказывал аренду автомобиля на веб-сайте крупной компании по аренде автомобилей. Я не хочу смущать их, называя их имена, поэтому давайте назовем их Пюджетом. После того, как я ввел номер моей кредитной карты, имя и почтовый адрес, регистрационная форма сломалась. Кнопка отправки ничего не сделала. Вот где нормальный человек сдался бы и использовал конкурента. Однако, если вы закаленный программист, которому нужно что-то доказать, это когда вы открываете консоль разработчика. Оказывается, форма молча терпела неудачу, потому что мой почтовый адрес содержал букву «Ø» и, следовательно, не совпадал с /^[a-z0-9 ]+$/i
.
Если вы упадете на букву Ø, вы потеряете небольшое количество норвежских клиентов, что может быть не самой большой проблемой в общем плане. Но подумайте о количестве людей в мире, которые пишут на кириллице, ханзи, кана, хангыль… Черт, даже немцы любят время от времени добавлять немного ß, а говорящие по-испански могут иногда использовать ñ. Ограничиваясь [a-z]
в качестве компании по аренде автомобилей, вы отвергаете большую часть неанглоязычного мира, а это, вероятно, именно тех людей, которые заинтересованы в аренде автомобиля, когда они находятся в США.
Это именно та глупая ошибка, которую мы все совершили, и все это происходило молча. Клиентам с кредитной картой в руке отказывали, и никто из ответственных за это не знал. Это еще одна причина настроить обработку ошибок, чтобы уведомлять вас, когда что-то неизбежно ломается, чтобы вы могли спать спокойно, зная, что ничего не ломается без вашего ведома.
Итак, как мне обрабатывать ошибки с помощью Vue.js?
Vue имеет несколько довольно простых механизмов для настройки обработчиков ошибок. Однако дьявол кроется в деталях, и оказывается, что при прямом подходе будут упущены определенные классы ошибок. В конце мы опишем решение, позволяющее отловить каждую ошибку, возникающую в рабочем приложении.
Вы должны знать две особенно важные части API Vue.js: errorHandler
и warnHandler
. Как вы уже догадались, они предназначены для обработки ошибок и предупреждений соответственно. Просто установите эти обработчики перед инициализацией вашего приложения.
Vue.config.errorHandler = function(err, vm, info) { /*your code*/ } Vue.config.warnHandler = function(msg, vm, info) { /*your code*/}
Здесь err
- это выданный объект ошибки JavaScript, vm
- соответствующий экземпляр Vue, а info
- строка, указывающая, в какой части жизненного цикла Vue произошла ошибка. То же самое и с warnHandler, за исключением того, что первый аргумент msg
- это строка, содержащая предупреждение.
Что такое ошибка и что такое предупреждение?
Здесь ошибки - это исключения, которые генерируются в JavaScript и не обрабатываются, а предупреждения относятся к проблемам, которые сам Vue обнаруживает во время рендеринга. Vue будет выдавать предупреждения только в режиме разработки, поэтому настройка кода для их обработки может иметь ограниченное применение.
Границы ошибок и errorCaptured
Еще один замечательный механизм, который предоставляет Vue (начиная с версии 2.5), - это errorCaptured
, который позволяет выявлять ошибки на уровне компонентов. Это, в свою очередь, позволяет реализовать границы ошибок. Вы можете настроить errorCaptured при определении вашего компонента:
Vue.component('parent', { template: '<div><slot></slot></div>', errorCaptured: (err, vm, inf) => alert('I have a broken child :(') }) Vue.component('child', { template: '<h1>{{ fail() }}</h1>' })
Затем вы можете использовать эти компоненты следующим образом:
<parent> <child></child> </parent>
Родительский компонент перехватит ошибки дочернего компонента. Обратите внимание, что errorCaptured будет только перехватывать ошибки в дочерних компонентах, а не в самом компоненте. Таким образом, родитель будет видеть недостатки своих детей, но не свои недостатки, как в вашем воспитании.
Если функция errorCaptured возвращает false, она не распространяет ошибку до своего родительского компонента. Это позволяет нам создать общий механизм остановки ошибки, называемый границей ошибки.
Vue.component('error-boundary', { template: '<div><slot></slot></di>', errorCaptured: (err, vm, info) => { console.log('We have an error'); return false; } })
Теперь у нас есть общий механизм, в который мы можем заключать компоненты.
<error-boundary> <something-that-can-fail></something-that-can-fail> </error-boundary>
Это похоже на попытку разметки для выявления проблем, возникающих во время рендеринга.
Но подождите: это не обработает все ошибки!
errorCaptured, сможешь ли ты с этим справиться?
warnHandler, ты справишься?
errorHandler, ты справишься?
Не думаю, что они с этим справятся!
- Destiny’s Child, первые сторонники создания глобального обработчика ошибок для ошибок, которые происходят вне процесса рендеринга Vue.
У всего этого есть проблема: он не улавливает все ошибки. По сути, он выявляет только те ошибки, которые может увидеть код Vue. Vue - это не тоталитарный фреймворк, который охватывает все, что вы делаете, поэтому все еще есть ошибки, которые эти обработчики никогда не поймают. Как и покемоны, мы хотим поймать их всех. Наша храбрость вытащит нас. Ты учишь меня, а я учу тебя.
Ошибки, происходящие вне процесса рендеринга Vue, не будут обнаружены. Например, если вы привязываете функцию к событию щелчка, и ваша функция создает ошибку, она не будет перехвачена ни одним из вышеперечисленных механизмов. Обычно эти обработчики не перехватывают ошибки, возникшие в вашем коде.
Как на самом деле отловить каждую ошибку
Чтобы действительно отловить каждую отдельную ошибку, вам необходимо настроить глобальный обработчик ошибок в браузере, назначив функцию свойству window.onerror
.
window.onerror = function(msg, src, linenum, colnum, error) { /*your code*/ }
Это будет вызываться для всех неперехваченных ошибок. Здесь msg
- это сообщение об ошибке, src
- это URL-адрес файла, в котором произошла ошибка, linenum
и colnum
- это номер строки и символа, в котором произошла ошибка, а error
- объект ошибки. Однако имейте в виду, что этот API - беспорядок с точки зрения несоответствий браузера.
- Объект ошибки не передается в IE10, Safari 9 и браузере Android.
colnum
не передается в IE8 и 9.- Свойство
error.stack
очень полезно, но нестандартно и содержит разную информацию от браузера к браузеру.
Также имейте в виду, что ошибки, возникающие в сценариях, загруженных из другого источника, будут отображаться только как Ошибка сценария, если вы не позаботитесь о настройке CORS.
Тем не менее, если вы позаботитесь о решении этих проблем, у вас будет надежный механизм обработки ошибок, который вызывается всякий раз, когда возникает необработанная ошибка.
Как регистрировать каждую ошибку с помощью CatchJS
Если вы хотите перехватывать каждую ошибку, возникающую на стороне клиента, и постоянно регистрировать ее на сервере, вы можете добиться этого, просто добавив скрипт CatchJS.
<script src="//cdn.catchjs.com/catch.js"></script>
Вам понадобится учетная запись в сервисе, но если вы используете Vue.js, вам не нужно вносить какие-либо изменения в свой код. Простое добавление этого скрипта установит глобальный обработчик ошибок в браузере и автоматически регистрирует все ошибки вместе с телеметрией, необходимой для воспроизведения обстоятельств ошибки. Эта опция хороша тем, что она может дать вам след кликов, который приводит к ошибке, и снимки экрана того, как выглядел экран, когда возникла проблема. Это не зависит от фреймворка, поэтому он так же хорошо работает с Angular, React и всем остальным, что можно использовать в интерфейсе.
Помните, друзья не позволяют друзьям сбрасывать неизвестные исключения, предварительно не зарегистрировав их. Будьте в безопасности!
Первоначально опубликовано на catchjs.com.