In my winter break I’ve been working on a Vue.js client for Mastodon. I’ve got two big reasons for doing it: I want link/video previews on my timeline and I have to get familiar with Vue for work.
I’m not very familiar with Mastodon’s API, nor with Vue, so I think I’ve been running into lots of problems that I shouldn’t have. For example, I added card (that’s the Mastodon name for link or video previews) autoloading to the timeline and I noticed something weird.
The timeline (at this point) is really simple: I first retrieve the last few toots using REST, dump the result in a
statuses array, then open a Websocket stream and
unshift all new incoming toots to the start of that array, while trimming it to 20 toots. The timeline is then displayed with a
v-for of that array.
As for the individual toots, most of the data is in computed properties derived from the raw prop passed in by the timeline. Except for the card, which was initialized with the toot – that is, if the toot has any card on it.
What I noticed was that the cards wouldn’t move down the array along with the toots, but stay at the position they were initialized at. Which makes perfect sense, if you think about it. I fetched the card with the
created () hook, so it would only run once and stay static then. They are attached to the component objects themselves, not the underlying data.
I was just changing the data and relying on Vue to reflect those changes in the objects. Except I didn’t tell Vue about the cards that it was supposed to pay attention to. I was expecting an object-unique relation between elements of the data and the components, which is clearly not the case.
I fixed it temporarily by attaching a watcher to the relevant prop and reinitializing the card whenever there is a change – which happens when the toot gets pushed down the timeline array. This obviously adds an unnecessary transmission overhead (the card is retrieved every single time a new toot appears on the timeline).
A more suitable solution turned out to be using
.sync for the data prop and adding the card’s data to that if there is one. This way it’s only retrieved once (if there is any) and since its data flows back into the timeline’s status array, it sticks to whichever toot it was loaded for.
I’m sure that in a few months I’ll look back at this and laugh at how little I understood of the Dao of Vue. (Who am I kidding I never look back at old blog posts.)