Speed up your Vue app: The most unexpected and perhaps silliest way 🤪🚀
Tags: vue, javascript, webdev, performance
Published: 2022-04-26 17:41:36

I was experimenting with Vue's `v-model` the other day. I wanted to figure out if what the Vue docs state about `v-model` was true. Here's what they say:

> Remember that:
> ```html
<input v-model="searchText">
```
> does the same thing as:
> ```html
<input 
  v-bind:value="searchText" 
  v-on:input="searchText = $event.target.value">
```

This phrase says that one can rebuild `v-model` by setting the `value` prop of a form control (any, really) and setting the prop to the target value in an event listener for the `input` event. Simple enough, right?

And at first glance, that's very much true. They _do_ the same thing. Updating the prop from somewhere else updates whatever's written in the input field, and updating the input field updates the prop.

Let's look at the _generated_ code, though, to verify that it's really, _actually_ the same.

The example app we'll work with is the most basic example we can build for this:

```html
<template>
  <div>
    <input
      v-model="someProp"
    >
    {{ someProp }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      someProp: 'Foobar',
    }
  }
}
</script>
```

A simple template with an input field and a text node, the script provides the text with some default value.

Next, we execute `npm run build` and open up the generated `dist/js/app.something.js`. Ugh, minified. But nothing my IDE can't handle. Doing a quick Ctrl+F for `someProp` will show us the relevant part of the code:

```js
return n("div", [n("input", {
  directives: [{
    name: "model",
    rawName: "v-model",
    value: e.someProp,
    expression: "someProp"
  }], 
  domProps: { value: e.someProp }, 
  on: {
    input: function (t) {
      t.target.composing || (e.someProp = t.target.value)
    }
  }
}) // ... ])
```

We see here the component from above but expressed as render functions. The interesting parts are the `domProps` and the `on` keys. They tell us how `v-model` generated the two-way binding.

Now, let's rebuild the above component to use `:value` and `@input` instead:

```html
<template>
  <div>
    <input
      :value="someProp"
      @input="e => someProp = e.target.value"
    >
    {{ someProp }}
  </div>
</template>

<script>
export default {
  data() {
    return {
      someProp: 'Foobar',
    }
  }
}
</script>
```

And do the same shenanigans as before to get the generated code:

```js
return n("div", [n("input", {
  domProps: { value: e.someProp },
  on: {
    input: function (t) {
      return e.someProp = t.target.value
    }
  }
}) // ... ])
```

Notice the difference? The `directives` key is missing. That's not that surprising, we don't use the `v-model` directive after all, right? The only difference in functionality is the missing `t.target.composing || ` in the "self-made" version. We can add that, though:

```html
<input
  :value="someProp"
  @input="e => e.target.composing || (someProp = e.target.value)"
>
```

What we'll get is this:

```js
return n("div", [n("input", {
  domProps: { value: e.someProp }, 
  on: {
    input: function (t) {
      t.target.composing || (e.someProp = t.target.value)
    }
  }
}) // ... ])
```

And _that's_ the equivalent to `v-model`. Without the `directives` key, though.

*And that's the point.*

Using the `@input`+`:value` approach makes the built JS around 85 bytes smaller (estimate, it may vary as it's dependent on the variable's name, the minimum is 71) per usage of `v-model`.

Now imagine our app has 100 usages of `v-model`, that'll be roughly **8.5kb less built JS**.

The price is readability, though. Who wants to bloat their templates with event listeners and bindings if they could just use `v-model`, right? Also, the gain is minimal compared to the amount of annoyance this can cause. So, we *probably* shouldn't do this.

> ☕ I also want to shout out to *Andras* for supporting me via *buymeacoffee*! I enjoyed the coffee in the sun on my favorite bench - Just the right thing to relax after a long day. Thank you!

---

*I hope you enjoyed reading this article as much as I enjoyed writing it! If so, leave a* ❤️ *or a* 🦄*! I write tech articles in my free time and like to drink coffee every once in a while.*

*If you want to support my efforts,* [*you can offer me a coffee* ☕](http://buymeacoffee.com/pthormeier) *or* [*follow me on Twitter* 🐦](https://twitter.com/pthormeier)*!* *You can also support me directly via [Paypal](https://www.paypal.me/pthormeier)!*

[![Buy me a coffee button](https://dev-to-uploads.s3.amazonaws.com/i/x55hp6jopwyy161d8e2u.png)](http://buymeacoffee.com/pthormeier)