Весь прошлый год я работал с моим любимым фреймворком, Vue.js, описывал и представлял его. И я понял, что ещё не разобрался с обработкой ошибок во Vue. Мне бы хотелось объяснить это тем, что я пишу идеальный код, но мы все знаем, как обстоит дело в действительности. В течение нескольких последних дней я экспериментировал с различными методами обработки ошибок, которые предоставляет Vue, и решил поделиться своими открытиями. Очевидно, что этот обзор не охватит все возможные сценарии, но я надеюсь, что он вам поможет!
Ошибки
Чтобы протестировать различные методы обработки, я решил взять три разных типа ошибок (по крайней мере, для начала). В первом случае это было просто обращение к несуществующей переменной:
<div id="app" v-cloak>
Hello, {{name}}
</div>
В этом примере пользователю не выдаётся сообщение об ошибке, но в консоль выводится предупреждение [Vue warn].
Вот как выглядит выполнение этого примера:
Во втором примере я попытался связать переменную с вычисляемым свойством, которое должно генерировать ошибку:
<div id="app" v-cloak>
Hello, {{name2}}
</div>
const app = new Vue({
el:'#app',
computed:{
name2() {
return x;
}
}
})
В этом случае в консоль выводится как предупреждение [Vue warn], так и обычное сообщение об ошибке, но пользователю не выдаётся ничего.
Вот так выполняется код для этого примера:
В третьем примере я использовал метод, при выполнении которого должна возникать ошибка.
<div id="app" v-cloak>
<button @click="doIt">Do It</button>
</div>
const app = new Vue({
el:'#app',
methods:{
doIt() {
return x;
}
}
})
Как и в предыдущем случае, сообщение об этой ошибке в консоли будет выводиться дважды: одно предупреждение и одно сообщение о данной ошибке. Но, в отличие от него, ошибка возникает только при реальном нажатии на кнопку.
И вот демонстрация для этого примера:
Прежде чем мы продолжим, я хочу пояснить, что в этих примерах представлены не все типы ошибок, которые вы можете сделать. Это всего лишь несколько основных, которые, по моему мнению, могут часто встречаться в приложениях на базе Vue.js.
Итак, как же вы можете обрабатывать ошибки в приложениях Vue? Надо сказать, я был немного удивлен, что в основном руководстве по фреймворку Vue нет раздела, посвящённого обработке ошибок.
Да, подобный раздел в руководстве есть, но весьма короткий, и весь его смысл умещается в следующую цитату:
«Если при отрисовке компонента произойдёт ошибка выполнения, она будет передана в глобальную функцию конфигурации Vue.config.errorHandler, если таковая была указана. Наверное, полезно было бы использовать этот хук совместно с сервисом отслеживания ошибок, таким как Sentry, тем более что его интеграция с Vue официально поддерживается».
На мой взгляд, эта тема должна чуть больше раскрываться в документации (и я думаю, что мог бы помочь в её дополнении). В общем, обработка ошибок во Vue сводится к следующим средствам:
- errorHandler;
- warnHandler;
- renderError;
- errorCaptured;
- window.onerror (это средство не является специфическим для Vue).
Давайте подробно рассмотрим эти приёмы.
Средство обработки ошибок номер один: errorHandler
Первое средство — это errorHandler. Как вы, вероятно, догадались, это стандартный обработчик ошибок для приложений Vue.js. Вы можете назначить его следующим образом:
Vue.config.errorHandler = function(err, vm, info) {
}
В приведённом выше объявлении функции err — это описание текущей ошибки, info — строка информации об ошибке, специфичная для Vue, а vm — текущее приложение Vue. Помните, что на одной веб-странице могут одновременно выполняться несколько приложений Vue. Этот обработчик ошибок будет относиться к ним всем. Рассмотрим следующий простой пример:
Vue.config.errorHandler = function(err, vm, info) {
console.log(`Error: ${err.toString()}nInfo: ${info}`);
}
В случае первой ошибки этот код не выполняет никаких действий. Если вы помните, при этом генерируется предупреждение, а не сообщение об ошибке.
Во втором случае ошибка обрабатывается и выводится следующий текст:
Error: ReferenceError: x is not defined
Info: render
Наконец, третий пример даёт следующий результат:
Error: ReferenceError: x is not defined
Info: v-on handler
Обратите внимание, насколько «полезна» информация под заголовком Info в двух предыдущих примерах. Теперь проверим, как работает следующее средство.
Средство обработки ошибок номер два: warnHandler
warnHandler обрабатывает — что бы вы думали? — предупреждения Vue. Заметьте, что в продакшене этот обработчик игнорируется. Обработчик для этого метода также слегка отличается от предыдущего:
Vue.config.warnHandler = function(msg, vm, trace) {
}
Два первых аргумента — msg и vm — не требуют дополнительных пояснений, а аргумент trace должен быть деревом компонентов. Рассмотрим пример:
Vue.config.warnHandler = function(msg, vm, trace) {
console.log(`Warn: ${msg}nTrace: ${trace}`);
}
В первом примере теперь есть обработчик для генерируемого им предупреждения, и он возвращает следующее:
Второй и третий примеры не изменяются. Живые примеры для всех трёх случаев представлены ниже.
Средство обработки ошибок номер три: renderError
Теперь я продемонстрирую третий метод обработки ошибок: renderError. В отличие от предыдущих двух, это средство зависит от компонента и не является универсальным. Так же, как и в случае с warnHandler, этот обработчик в продакшене отключается.
Чтобы его использовать, вставьте его в свой компонент/приложение. Ниже приведён изменённый пример из документации.
const app = new Vue({
el:'#app',
renderError (h, err) {
return h('pre', { style: { color: 'red' }}, err.stack)
}
})
Если этот обработчик ошибок используется в первом примере, то он ничего не делает, что, если подумать, вроде бы логично, так как первый пример выдаёт предупреждение, а не ошибку. Если вы проверите этот обработчик во втором примере, где ошибку выдаёт вычисляемое свойство, то увидите, что его результат на экране отображается. Вы можете это увидеть в приведённой ниже демонстрации на CodePen.
Честно говоря, мне не совсем понятно, зачем использовать это средство, когда консоль гораздо удобнее, однако если отдел контроля качества или другие тестировщики не знакомы с консолью браузера, то более простое сообщение об ошибке на экране может им помочь.
Средство обработки ошибок номер четыре: errorCaptured
Наконец, есть средство errorCaptured (специфично для Vue), которое привело меня в замешательство и, честно говоря, до сих пор немного смущает. В документации говорится следующее:
«Вызывается, если фиксируется ошибка в любом дочернем компоненте. Этот хук получает три аргумента: ошибку, экземпляр компонента, который вызвал ошибку, и строку, содержащую информацию о том, где была зафиксирована ошибка. Хук может возвращать значение false, чтобы предотвратить дальнейшее распространение ошибки».
Согласно моим исследованиям (опять-таки, я сильно сомневаюсь в этом), этот обработчик ошибок должен использоваться только родительским компонентом, обрабатывающим ошибку дочернего компонента. Насколько я знаю, его нельзя использовать в основном экземпляре Vue, а можно только в компоненте, имеющем потомков.
Чтобы проверить это, я создал вот такой набор из родительского и дочернего компонентов:
Vue.component('cat', {
template:`
<div><h1>Cat: </h1>
<slot></slot>
</div>`,
props:{
name:{
required:true,
type:String
}
},
errorCaptured(err,vm,info) {
console.log(`cat EC: ${err.toString()}ninfo: ${info}`);
return false;
}
});
Vue.component('kitten', {
template:'<div><h1>Kitten: {{ dontexist() }}</h1></div>',
props:{
name:{
required:true,
type:String
}
}
});
Обратите внимание, что компонент kitten содержит ошибку. Теперь, если я попытаюсь использовать этот компонент следующим образом,
<div id="app" v-cloak>
<cat name="my cat">
<kitten></kitten>
</cat>
</div>
я получу от обработчика сообщение:
cat EC: TypeError: dontexist is not a function
info: render
Вы можете это увидеть в приведённом ниже примере.
Так что да, интересное средство. Я полагаю, что оно будет в основном использоваться теми, кто создаёт библиотеки компонентов с отношениями типа родитель/потомок. Это средство скорее подходит для разработчика библиотеки, чем для обычного разработчика, если такое деление имеет смысл. Но опять-таки, это только моё первое впечатление от данного средства.
Единое средство для управления всем на свете: window.onerror
Последний (и самый мощный) вариант — использовать window.onerror, глобальный обработчик ошибок для всего, что только может случиться при выполнении вашего JavaScript-кода. Этот обработчик имеет следующий формат:
window.onerror = function(message, source, line, column, error) {
}
Вероятно, единственное, о чём вы не можете догадаться в приведённом выше коде, это смысл аргумента source, который представляет собой URL-адрес скрипта.
Вот здесь-то и начинается самое интересное. Если вы определите эту функцию, но при этом не используете Vue.config.errorHandler, то она вам не поможет. Vue ожидает, что вы определите Vue.config.errorHandler, и, если вы этого не сделаете, не распространит ошибку за свои пределы. Наверное, в этом есть какой-то смысл… Даже не знаю, для меня особого смысла в этом нет. Ещё более странная вещь: допустим, в самом вашем обработчике ошибок Vue есть ошибка. Она также не дойдёт до обработчика window.onerror.
Вот демонстрация на CodePen с соответствующим примером. Я закомментировал ошибку в errorHandler, но если вы уберёте комментарий, то увидите, что глобальный обработчик ошибок не сработает. Он сработает только в одном случае: если вы нажмёте вторую кнопку.
Заключение
Надеюсь, материал этой статьи будет полезен читателям. Как уже написано в самом начале, этой темой я только начал заниматься, поэтому, разумеется, жду комментариев, замечаний и предложений. Я с удовольствием прочитаю, как другие разработчики используют эти средства в своих приложениях!
Фото в начале статьи: автор — Дэвид Коваленко, сайт Unsplash
Автор: andreymoul