About The Author

Sarah Drasner is an award-winning Speaker, Senior Developer Advocate at Microsoft, and Staff Writer at CSS-Tricks. Sarah is also the co-founder of Web Animation … More about Sarah

Replacing jQuery With Vue.js: No Build Step Necessary

Quick Summary

Did you know that you can incorporate Vue into your project the same way that you would incorporate jQuery — with no build step necessary? Let’s cover some common use cases in jQuery and how we can switch them over to Vue, and why we’d even want to do so.

Table of Contents
Membership counter

Members support Smashing

Wonderful, friendly people who keep this lil' site alive — and get smarter every day.

Are you smashing, too? →

It’s been impossible to ignore all of the hype surrounding JavaScript frameworks lately, but they might not be the right fit for your projects. Perhaps you don’t want to set up an entire build system for some small abstractions you could feasibly do without. Perhaps moving a project over to a build system and thus, different deployment method would mean a lot of extra time and effort that you might not be able to bill to a client. Perhaps you don’t want to write all of your HTML in JavaScript. The list goes on.

What some people might not know is, you can incorporate Vue into your project the same way that you would incorporate jQuery, no build step necessary. Vue is flexible in the sense that we can use it directly in the HTML.

So, if your current page structure looks like this:

<main>
  <div class="thing">
     <p>Some content here</p>
  </div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script>
  //some jquery code here
</script>

You could literally change the script tag here and still use the HTML and JS in tandem just as you did before, refactoring only a few small bits of code. You don’t have to rewrite the HTML in JavaScript, you don’t have to use webpack, and you don’t have to set up a giant system:

<main>
  <div class="thing">
     <p>Some content here</p>
  </div>
</main>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.3/vue.min.js"></script>
<script>
  //some vue code here
</script>

You can replace the tags and leave the markup as is. The best part is, you might think the code will get more complicated, but you might find in reading through this article and seeing the examples, that Vue is extremely simple, legible, and easy to maintain and adapt. In terms of size, they’re pretty comparable as well- to use them as is from a CDN, minified, Vue version 2.5.3 is 86KB. jQuery 3.2.1 is 87KB.

Let’s cover some common use cases in jQuery and how we’d switch them over to Vue, and why we’d even want to do so.

Capturing User Inputs

A really common use case for needing JavaScript on a site is capturing user input from a form, so let’s start there. We won’t actually incorporate the full form yet in the interest of simplicity and clarity, but we’ll work up to it by the end.

To capture information as a user types, here’s how we would do this in jQuery and Vue — side by side:

See the Pen jQuery capture information from a form input by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="thing">Name:</label>
  <input id="thing" type="text" />
  <p class="formname"></p>
</div>
// this is an alias to $(document).ready(function() {
$(function() {
  //keypress wouldn't include delete key, keyup does. We also query the div id app and find the other elements so that we can reduce lookups
  $('#app').keyup(function(e) {
    var formname = $(this).find('.formname');
    //store in a variable to reduce repetition
    var n_input = $(this).find('#thing').val();
    formname.empty();
    formname.append(n_input);
  });
});

See the Pen Vue capture information from a form input by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="name">Name:</label>
  <input id="name" type="text" v-model="name" /> <!--v-model is doing the magic here-->
  <p>{{ name }}</p>
</div>
//this is a vue instance
new Vue({
  //this targets the div id app
  el: '#app',
  data: {
    name: '' //this stores data values for ‘name’
  }
})

I use this example because it reveals a few of Vue’s strengths. Vue is reactive, which makes it particularly capable of responding to changes. You can see how, as we’re updating what we’re typing, it changes instantly- there’s no delay.

You can also see that in the jQuery version, the DOM is in control- we’re fetching things out of the DOM, listening to it, and responding to it. This ties us to the way that the DOM is currently set up, and forces us to think about how to traverse it. If the structure of the HTML elements were to change, we’d have to adapt our code to correspond to those changes.

In the Vue version, we’re storing the state- we keep track of one property we want to update and change, and track the element we want to change by a thing called a directive. This means it’s attached directly to the HTML element we need to target. The structure of the DOM can change, the HTML can move around, and none of this would impact our performance or capturing these events. In our case, we’re using that v-model attribute on the input to connect to the data we’re storing in the JavaScript.

But! This isn’t as common a use case as storing something as you hit the enter key, so let’s look at that next.

Storing User Input On A Single Event

The interesting thing about the way Vue works is that it’s decoupled from having to think about specific DOM events when storing and retrieving data. In essence, we already have an idea of what we want to capture; we’re giving it shape by picking an event with which to alter it. In contrast, jQuery is tightly coupled to what the DOM does and rests on those DOM events to build out the variables it stores, which can be placed anywhere, rather than one consistent group (in data) for retrieval. We can see this in the updated version of the last example, where the information is gathered on an enter keypress:

See the Pen jQuery capture information from a form input- on enter by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="thing">Name:</label>
  <input id="thing" type="text" />
  <p class="formname"></p>
</div>
// this is an alias to $(document).ready(function() {
$(function() {
  //We query the div id app and find the other elements so that we can reduce lookups
  $('#app').change(function(e) {
    var n_input = $(this).find('#thing').val();
    $(this).find('.formname').append(n_input);
  });
});

See the Pen Vue capture information from a form input, enter key by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="name">Name:</label>
  <input id="name" type="text" v-model.lazy="name" />
  <p>{{ name }}</p>
</div>
new Vue({
  el: '#app',
  data: {
    name: ''
  }
});

In this version, the jQuery is simplified somewhat because we don’t have to capture things on every keystroke, but we’re still fishing things out of the DOM and responding step by step to these changes. Our code in jQuery will always go a little something like this:

"Go get this element, see what it’s doing, hold on to these changes, do something with these changes."

In comparison: In Vue, we’re in control of what’s changing, and the DOM responds to those changes based on our commands. We attach it directly to the thing we’d like to update. In our case, we have a small abstraction called a modifier: v-model.lazy. Vue now knows not to start storing this until after a change event occurs. Pretty neat!

Toggling Classes

The next thing we’ll cover is toggling CSS classes because, as the almighty, ever-watching Googly has informed me, it’s the most common jQuery functionality.

See the Pen Toggle Class jQuery by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <button aria-pressed="false">Toggle me</button>
  <p class="toggle">Sometimes I need to be styled differently</p>
</div>
.red {
  color: red;
}

JS
$(function() {
  $('button').click(function(e) {
    $('.toggle').toggleClass('red');
    $(this).attr('aria-pressed', ($(this).attr('aria-pressed') == "false" ? true : false));
  });
});

See the Pen Toggle Class Vue by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <button @click="active = !active" :aria-pressed="active ? 'true' : 'false'">Toggle me</button>
  <p :class="{ red: active }">Sometimes I need to be styled differently</p>
</div>
.red {
  color: red;
}

JS
new Vue({
  el: '#app',
  data: {
    active: false
  }
})

Again, what we see here is that in the jQuery version we’re storing the state in the DOM. The element has the class, and jQuery makes a decision based on the presence of the class, which it checks by pinging the DOM. In the Vue version, we store a condition, and we style it according to that state. We’re not asking the DOM for this information, we hold it ourselves.

We store active in the data, the button switches the condition, and .red is altered based on that condition. Even the states for accessibility, aria-pressed, are stated much quicker, as we don’t have to set anything in the script in Vue, we’re able to switch between states directly inline in the template based on the state of ‘active.’

You’ll also note in the last few examples, you might have thought it would be a lot more code to start working with Vue.js than jQuery, but they’re actually pretty comparable.

Hiding And Showing

Another common jQuery use case is hiding and showing something. jQuery has always done a really good job of making this task really simple, so let’s take a look at what it looks like side to side with Vue.

See the Pen jQuery show hide by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <button type="button" id="toggle" aria-expanded="false">
    Toggle Panel
  </button>
  <p class="hello">hello</p>
</div>
$(function() {
  $('#toggle').on('click', function() {
    $('.hello').toggle();
    $(this).attr('aria-expanded', ($(this).attr('aria-expanded') == "false" ? true : false));
  });
});

See the Pen Vue show hide by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <button @click="show = !show" :aria-expanded="show ? 'true' : 'false'">
    Toggle Panel
  </button>
  <p v-if="show">hello</p>
</div>
new Vue({
  el: '#app',
  data: {
    show: true
  }
})

Both jQuery and Vue do a nice job of keeping this task simple, but there are a couple of reasons that I really working with Vue for something like a toggle. Vue has a tool called Vue devtools. This is not unlike the Chrome devtools, but when we use it, we get some special information about what’s going on with Vue.

In both the jQuery and Vue version, we can see that the element hides and appears. But what if something were to go wrong? What if something about our code wasn’t working the way we expected? In order to start debugging with jQuery, we’d probably add in some console.logs or set some breakpoints to try to track down where things were erroring.

Now, there ain’t nothin’ wrong with console.logs, but with the aid of the Vue devtools, we can actually get a hands-on Vue (couldn’t resist) of what Vue thinks is happening. In this gif below, you can see as we toggle the button, the Vue devtools updates the state of true/false accordingly. If the DOM was ever not to be working the way we expected, we could see the data in Vue in real time. This makes it much so much easier to debug; it’s actually quite wonderful.

data in vue in real time

The other thing I like about this is that the v-if is easy to extend to other conditions. I can decide to use a thing called v-show instead of v-if if the thing I’m toggling will show and hide frequently: v-if will completely unmount the element, while v-show will merely toggle the visibility of it. This distinction is really important because it is much more performant to toggle the visibility in a style rather than completely unmounting/mounting the DOM node. I can show or hide something based on a lot of conditions, or even the presence of user input or other conditions as well. This is usually where jQuery can get a bit messy, pinging the DOM in multiple locations and coordinating them. Below is an example of coordinating showing something based on the presence of user input:

See the Pen Show button based on content Vue by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="textarea">What is your favorite kind of taco?</label>
  <textarea id="textarea" v-model="tacos"></textarea>
  <br>
  <button v-show="tacos">Let us know!</button>
</div>
new Vue({
  el: '#app',
  data() {
    return {
      tacos: ''
    }
  }
})

See the Pen Show button based on content jQuery by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <label for="textarea">What is your favorite kind of taco?</label>
  <textarea id="textarea"></textarea>
  <br>
  <button v-show="tacos">Let us know!</button>
</div>
$(function() {
  var button = $('.button');
  var textarea = $('#textarea');

  button.hide();
  textarea.keyup(function() {
    if (textarea.val().length > 0) {
      button.show();
    } else {
      button.hide();
    }
  })
});

In this example, you can see the value of having Vue hold the state- we’re reacting to the changes very naturally and with less code altogether. Once you get used to the style, it’s faster to understand because you don’t have to trace the logic line by line. A lot of people call this difference “imperative vs. declarative.”

Submitting A Form

The canonical use case for jQuery has historically been submitting a form with an AJAX call, so we should take a look at that as well. Vue actually does not have a built-in thing like AJAX; it’s typical in Vue application to use something like Axios (a JavaScript library for making HTTP requests) to help with this task.

This example is a little more complicated than the rest. We’re going to do a few things here:

  1. The button will appear grey before we start typing in our form, then it will receive an “active” class and turn blue;
  2. When we submit the form, we’ll keep the page from loading;
  3. When the form is submitted, we’ll show the response data on the page.

See the Pen jQuery form submission AJAX by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <form action="/">
    <div>
      <label for="name">Name:</label><br>
      <input id="name" type="text" name="name" required/>
    </div>
    <div>
      <label for="email">Email:</label><br>
      <input id="email" type="email" name="email"  required/>
    </div>
    <div>
      <label for="caps">HOW DO I TURN OFF CAPS LOCK:</label><br>
      <textarea id="caps" name="caps" required></textarea>
    </div>
    <button class="submit" type="submit">Submit</button>
    <div>
      <h3>Response from server:</h3>
      <pre class="response"></pre>
    </div>
  </form>
</div>
$(function() {
  var button = $("button");
  var name = $("input[name=name]");

  name.keyup(function() {
    if (name.val().length > 0) {
      button.addClass('active');
    } else {
      button.removeClass('active');
    }
  });

  $("form").submit(function(event) {
    event.preventDefault();

    //get the form data
    var formData = {
      name: $("input[name=name]").val(),
      email: $("input[name=email]").val(),
      caps: $("input[name=caps]").val()
    };

    // process the form
    $.ajax({
      type: "POST",
      url: "//jsonplaceholder.typicode.com/posts",
      data: formData,
      dataType: "json",
      encode: true
    }).done(function(data) {
      $(".response")
        .empty()
        .append(JSON.stringify(data, null, 2));
    });
  });
});

In here, we’ll see lines 2-10 deal with the handling of the button class, similarly to how we did this before. We pass in a parameter called event to the form, and then say event.preventDefault() to keep from reloading the page. Then we collect all of the form data from the form inputs, process the form, and then put the response into the .done() call from the AJAX request.

See the Pen Vue form submission by Sarah Drasner (@sdras) on CodePen.

<div id="app">
  <form @submit.prevent="submitForm">
    <div>
      <label for="name">Name:</label><br>
      <input id="name" type="text" v-model="name" required/>
    </div>
    <div>
      <label for="email">Email:</label><br>
      <input id="email" type="email" v-model="email" required/>
    </div>
    <div>
      <label for="caps">HOW DO I TURN OFF CAPS LOCK:</label><br>
      <textarea id="caps" v-model="caps" required></textarea>
    </div>
    <button :class="[name ? activeClass : '']" type="submit">Submit</button>
    <div>
      <h3>Response from server:</h3>
      <pre>{{ response }}</pre>
    </div>
  </form>
</div>
new Vue({
  el: '#app',
  data() {
    return {
      name: '',
      email: '',
      caps: '',
      response: '',
      activeClass: 'active'
    }
  },
  methods: {
    submitForm() {
      axios.post('//jsonplaceholder.typicode.com/posts', {
        name: this.name,
        email: this.email,
        caps: this.caps
      }).then(response => {
        this.response = JSON.stringify(response, null, 2)
      })
    }
  }
})

In the Vue version, we decide what fields we need to populate in the form, and then attach them with that v-model we used earlier. We check for the presence of name in order to toggle the class. Instead of passing in event and writing event.preventDefault(), all we have to do is write @submit.prevent on our form element, and that’s taken care of for us. To submit the post itself, we use Axios, and we’ll store the response in the Vue instance in response.

There are still many things we’d want to do to have a production-ready form, including validation, error handling, and writing tests, but in this small example, you can see how clean and legible Vue can be while handling a lot of things updating and changing, including user input.

Conclusion

It is definitely ok to use jQuery if it suits you! This article serves to show that Vue is also a pretty nice abstraction for small sites that don’t need a lot of overhead. Vue is comparable in size, easy to reason about, and it’s fairly trivial to switch small pieces of functionality to Vue without rewriting your HTML in JavaScript and adopting a build system if you don’t have the bandwidth. This all makes it pretty compelling to consider.

Due to Vue’s flexibility, it’s also easy to transition this code to a build step and component structures if you’d like to adopt a more complex structure over time. It’s actually pretty fun to try it out, so when you’re ready to do so, check out the vue-cli. What this tool does is give you the ability to scaffold an entire production-level Vue and webpack build with just a couple of terminal commands. This allows you to work with single-file components, where you can use HTML, CSS, and Script in tandem in one file that makes up single, reusable components. You don’t have to configure the webpack build unless you’d like to do something special, so you save a lot of time setting things up. They even have a built-in command to get everything ready for production deployment.

The nice thing about the flexibility to choose either way of incorporating Vue into your project means that you’re not pressed to change your style of working all at once, and you can even make changes slowly over time. This is why people call Vue the progressive framework.

Smashing Editorial (ra, il)