Advanced Vue Debugging: A Detective Story
Kevin Ball | October 16th, 2018 | 4 min read
Modern frameworks like Vue.js do a ton for you. They manage DOM updates, maintain component lifecycles, and more.
Layer on higher level frameworks like Nuxt.js and even more is taken care of you. The plugin system lets you mix in behavior into all of your components automatically. Layouts, pages, and components are all seamlessly interwoven behind the scenes.
But one of the key questions is - what happens when things break down? When something goes wrong, and there is so much happening behind the scenes outside of your code, how do you debug it?
How do you follow the complex interweaving of runtime code, framework code, third party plugins, and your own code? It's almost like unraveling a murder mystery. So much so, that I decided that the right way to talk about it might be to tell it as an unraveling mystery.
So here we go. Follow me as we walk through debugging a recent tricky bug I ran into, tracing the "culprit" down and along the way learning what types of forensic tooling we have at our disposal.
The setup
The bug occurred for me in a client application where I was using the vue-select
component.
I updated the version of the vue-select
package from v2.4.0 to v2.5.0 in order to get a new feature, but upon testing I found that the package was no longer working right.
In particular, this plugin allows you to type into an input, and it will automatically filter down the list of available options. After upgrading, that filtering wasn't working.
My first assumption was that something in the way I was using the component was no longer supported. I looked at the documentation, and it looked like everything I was doing was right... in fact there was an example almost identical to the way I was using it.
I pulled down the package repository and put my example into it - it worked fine there. So clearly something weird was going on. Time to really dig into debugging.
Starting place - the console
My first stop was to open up the browser console and see if there was a javascript error. Maybe something else in my application was breaking, and the component was not getting fully set up.
While I didn't find an error, I did see an odd pair of warnings:
'Method "filterBy" has already been defined as a prop' - that seemed like a hint. Maybe I was defining filterBy somewhere I shouldn't? But searching through my code turned up nothing at all.
Looking into the vue-select
source code was no more illuminating. I could see filterBy
being defined as a prop, but no redefinition anywhere that might lead to this warning.
If there had been an actual error, I could have looked at the backtrace, but the warnings were coming from deep inside Vue internals.
Inspecting the component
To try to figure out what was going on, I used the Vue devtools to get a look at the component during runtime.
I knew that by default, the filterBy
prop for vue-select
should get set to a function that looked like:
function(option, label, search) {
return (label || '').toLowerCase().indexOf(search.toLowerCase()) > -1
}
I selected the component in the Vue devtools and used the fact that it created a reference to the component named $vm0
to log out the function:
Not a match at all! There was in fact a new function coming in... but when I searched through my codebase for the function signature filterBy (arr, search)
, or even just the arguments, nothing turned up.
And I still didn't have any indication as to what was setting that new function, and when... only that something was in fact setting a new function.
Had I been a little smarter just then, I could have found it, but I didn't. We'll come back to what I could have done better, but first, the breakthrough that led to the solution:
The Breakthrough: Inserting a Breakpoint within warn
The breakthrough came when I realized that even though the warning was not happening in code that I could point to, it might have context that I could use to trace things down.
I clicked through to the warning code in the chrome DevTools and inserted a breakpoint.
Reloading with this in place would let me then examine the state of the Vue vm at the time the warning was triggered.
Doing so, I saw nothing immediately obvious... the vm
had filterBy
defined, but it looked like the expected function.
So I started stepping forward in the debugger, and it popped me up to the function that had triggered the warning:
Ah-hah! Now we can directly inspect the method that is triggering the warning: it's in the methods
object:
The key is the link to source location. It was in the vendors.app.js
bundle... if I had properly set up sourcemapping in the project to include node modules it would have pointed me straight to the source, but even without that I could click through and scroll up to see the webpack annotation to show me the source:
The function was coming from vue2-filters
, another 3rd party plugin that I had installed without worrying too much about it. This plugin installs a set of common filters, including some that are implemented as methods. One of those methods just happened to be named filterBy
, and was clobbering the prop in vue-select
.
Voila! The solution!
The shortcut that could have been
I mentioned earlier that if I'd been a little smarter, I could have found the issue when I first logged the filterBy
function and saw it was different.
It turns out, had I inspected the $vm0
object that was my VSelect
component, rather than just logging out the filterBy
function, devtools would have actually let me click through to the source code at that point.
So I could have arrived at the solution one step faster. Interestingly, in the Firefox version of the devtools this reference does not appear to be there, but the warn approach still functions.
More Information and Resources
I hope you've enjoyed this "detective story" and gotten some ideas that will help you debug your Vue.js applications.
If you want to explore this debugging problem yourself, I've set up a barebones application with Nuxt 2.0 that reproduces the issue in github here.
All of these debugging examples were done with Chrome devtools plus the Vue Devtools Chrome extension. A similar extension exists for Firefox and also as an Electron app that will work in any environment.
You may also be interested in these additional resources:
Click to load comments...