As someone who has used jQuery for many. years and has recently become a Vue convert, I thought it would be an interesting topic to discuss the migration process of working with one to the other.
Before I begin though, I want to ensure one thing is crystal clear. I am not, in any way whatsoever, telling anyone to stop using jQuery. That’s been pretty fashionable lately, and heck, I wrote up something similar myself a few year ago (“How I’m (Not) Using jQuery”). If you get things done with jQuery and your end users are successfully using your site, then more power to you. Keep using what works for you.
This guide is more for people who may be coming from years of jQuery experience and want to see how things can be done with Vue. With that in mind, I’m going to focus on what I consider “core” jQuery use cases. I won’t cover every single possible feature but instead take a “I often did [X] with jQuery” approach that may be more relatable to people considering learning Vue. (As an aside, also note that how I write my examples are simply one way of performing a task. Both jQuery and Vue give provide multiple ways to accomplish the same goal and that’s a great thing!)
With that in mind, let’s consider some high level things we might turn to jQuery for:
- Finding something in the DOM (with the idea of doing something with it later)
- Changing something in the DOM (e.g. the text of a paragraph or the class of a button)
- Reading and setting form values
- Form validation (which is really just a combination of the items above)
- Ajax calls and handling the results
- Event handling (e.g. on button click, do something)
- Measuring or changing the styles of an element
There’s more to jQuery, of course, but these uses (at least in my opinion), cover the most common use cases. Also note there’s a lot of cross pollination in the list above. So, should we start with a simple one-to-one comparison of each? Nope, not so fast. Let’s begin by covering the major difference in Vue applications.
Defining where Vue “works”
When we drop jQuery onto a page, we are basically adding a Swiss Army knife to JavaScript code to handle common web development tasks. We can do any of the uses case we’re going to cover in whatever order we see fit. For example, a client may ask for form validation today, then in a month or so, ask to do an Ajax-based search form in the header of the site.
Vue has one significant difference in that respect. When starting a with Vue project, we start by defining an “area” in the DOM we want it to focus on. So, let’s consider a simple prototype web page:
<body>
<header>
Fancy header stuff here
</header>
<div id="sidebar">
Some sidebar doohicky here
</div>
<main>
<p>
Main site content here, super important stuff...
</p>
<div id="loginForm">
A login form of course
</div>
</main>
</body>
In a typical jQuery application, we may write code to work with the header, sidebar, and login form or something. No big whoop:
$(document).ready(function() {
$('header') // something...
$('#sidebar') // something...
$('#loginForm') // something...
});
In a Vue application, we first specify what are we’re working with. Imagine our client first asked to add validation to the loginForm
element. Our Vue code would specify that:
new Vue({
el: '#loginForm',
// Code here...
});
This means that we’d typically end up adding a second Vue application if the client later decides to have us add something to the sidebar:
new Vue({
el:'#loginForm',
// Code here...
});
new Vue({
el:'#sideBar',
// Code here...
});
Is that a bad thing? Absolutely not. Right away, we get the benefit of encapsulation. If we accidentally use a variable with a generic name (we’ve all done that), we don’t have to worry about conflicts with other parts of your code. Later on when the client adds yet another request, having our unique, logical sets of Vue code separated out like this gives us some great peace of mind that things won’t step on each other.
So, yes, a good thing. But it absolutely caused me to stop a bit when I first began using Vue. Now, onto our use cases.
Finding Stuff in the DOM
Another aspect you’ll find interesting, or scary, is how to “find stuff in the DOM.” That’s a bit vague, but let’s consider a firm example. We have a button, and when it’s clicked, we make something happen. Here’s an abbreviated example of how this could look:
<button id="myButton">Click Me!</button>
<!-- More stuff... -->
<script>
$(document).ready(function() {
$('#myButton').click(function() {
alert(1);
});
});
</script>
Now let’s compare that to how it can be done with Vue:
<div id="app">
<button v-on:click="doSomething">Click Me!</button>
</div>
<script>
const app = new Vue({
el:'#app',
methods: {
doSomething: function() {
alert(1);
}
}
});
</script>
The Vue application is a bit more verbose, but note how the markup has a direct connection between the action (“click”) and the function that will be called. Vue’s code doesn’t have a tie back to the DOM (outside of the el
portion where we define where it needs to work). This was easily one of the things that sold me on Vue the most — it feels easier to tell what is going on. Also, I didn’t need to worry so much about the ID value and selectors. If I change the class or ID of the button, I don’t need to go back into my code and worry about updating selectors.
Let’s consider another example: finding and changing text in the DOM. Imagine that button, on click, now changes the text of another part of the DOM.
<button id="myButton">Click Me!</button>
<span id="result"></span>
<!-- More stuff... -->
<script>
$(document).ready(function() {
$('#myButton').click(function() {
$('#result').text('You clicked me, thanks!');
});
});
</script>
I’ve added a new span and now, when the button is clicked, we use another selector to find it and use a jQuery utility method to change the text inside it. Now consider the Vue version:
<div id="app">
<button v-on:click="doSomething">Click Me!</button>
<!-- On click, change text in this span -->
<span>{{resultText}}</span>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
resultText: ''
},
methods: {
doSomething: function() {
this.resultText = 'You clicked me, thanks!';
}
}
});
</script>
In this example, we’re using Vue’s template language (the highlighted line) to specify that we want to render a variable inside the span, which is resultText
in this case. Now, when the button is clicked, we change that value and the span’s inner text will change automatically.
As an aside, Vue supports a shorthand for the v-on
attribute, so the button in the example could have been written with @click="doSomething"
instead.
Reading and writing form variables
Working with forms is probably one of the most common — and useful — things that we can do with JavaScript. Even before JavaScript, most of my early “web development” was writing Perl script to handle form submissions. As the primary way of accepting user input, forms have always been critical to the web and that’s probably going to stay the same for quite some time. Let’s consider a simple jQuery example of reading a few form fields and setting another:
<form>
<input type="number" id="first"> +
<input type="number" id="second"> =
<input type="number" id="sum">
<button id="sumButton">Sum</button>
</form>
<script>
$(document).ready(function() {
let $first = $('#first');
let $second = $('#second');
let $sum = $('#sum');
let $button = $('#sumButton');
$button.on('click', function(e) {
e.preventDefault();
let total = parseInt($first.val(),10) + parseInt($second.val(),10);
$sum.val(total);
});
});
</script>
This code demonstrates how jQuery can both read and write via the val()
method. We end up getting four items from the DOM (all three form fields and the button) and use simple math to generate a result. Now consider the Vue version:
<form id="myForm">
<input type="number" v-model.number="first"> +
<input type="number" v-model.number="second"> =
<input type="number" v-model="sum">
<button @click.prevent="doSum">Sum</button>
</form>
<script>
new Vue({
el: '#myForm',
data: {
first: 0,
second: 0,
sum: 0
},
methods: {
doSum: function() {
this.sum = this.first + this.second;
}
}
})
</script>
This introduces some interesting Vue shortcuts. First, v-model
is how Vue creates two way data binding between values in the DOM and in JavaScript. The data
block variables will automatically sync up with the form fields. Change the data, and the form updates. Change the form, and the data updates. The .number
is a flag to Vue to treat the inherit string values of form fields as numbers. If we leave this off and do addition as we are, we’ll see string additions and not arithmetic. I’ve been working with JavaScript for nearly a century and still screw this up.
Another neat “trick” is @click.prevent
. First, @click
defines a click handler for the button, then the .prevent
portion blocks the browser’s default behavior of submitting the form (the equivalent of event.preventDefault()
).
The final bit is the addition of the doSum
method that’s bound to that button. Note that it simply works with the data variables (which Vue makes available in the this
scope).
While this is mostly my personal feeling here, I really love the lack of query selectors in the script when writing in Vue and how the HTML is much more clear about what it’s doing.
Finally, we could even get rid of the button completely:
<form id="myForm">
<input type="number" v-model.number="first"> +
<input type="number" v-model.number="second"> =
<input type="number" v-model="sum">
</form>
<script>
new Vue({
el: '#myForm',
data: {
first: 0,
second: 0
},
computed: {
sum: function() {
return this.first + this.second;
}
}
})
</script>
One of the cooler features of Vue is computed properties. They are virtual values that recognize when their derived values are updated. In the code above, as soon as any of the two form fields change, the sum will update. This works outside of form fields too. We could render the sum like so:
The total is {{sum}}.
Working with Ajax
It’s commendable how easy jQuery has made it to use Ajax. In fact, I can say I’ve done Ajax “the vanilla” way probably a grand total of one time. (If you’re curious, you can take a look at the spec for XMLHttpRequest and probably be happy you avoided it yourself.) jQuery’s simple $.get(...)
method worked in a large number of cases and when it’s needed for something more complex, $.ajax()
made it easy as well. Another thing jQuery did well is the way it handles JSONP requests. While mostly unnecessary now with CORS, JSONP was a way to handle making requests to APIs on different domains.
So, what does Vue do for you to make Ajax easier? Nothing!
OK, that sounds scary but it really isn’t. There are many options out there for working with HTTP requests, and Vue.js took a more agnostic route of letting us, the developers, decide how we want to handle it. So yes, that does mean a bit more work, but we’ve got some great options.
The first one to consider is Axios, this is a Promise-based library that is very popular among the Vue community. Here’s a simple example of it in action (taken from their README file):
axios.get('/user?ID=12345')
.then(function (response) {
// handle success
console.log(response);
})
.catch(function (error) {
// handle error
console.log(error);
})
.then(function () {
// always executed
});
Axios supports POST requests, of course, and lets us specify headers among many other options.
While Axios is very popular among Vue developers, it isn’t something that really clicked with me. (At least not yet.) Instead, I’ve been much more a fan of Fetch. Fetch is not an external library but is a web standard way of performing HTTP requests. Fetch has very good support at roughly 90% of browsers, though that means it isn’t completely safe to use, but we can always use a polyfill we need to.
While it’s totally out of the scope of what we’re discussing here, Kingsley Silas has written an excellent guide on using both Axios and Fetch with React.
Like Axios, Fetch is Promise-based and has a friendly API:
fetch('http://example.com/movies.json')
.then(function(response) {
return response.json();
})
.then(function(myJson) {
console.log(JSON.stringify(myJson));
});
Both Axios and Fetch cover all types of HTTP requests, so either will fit an any number of needs. Let’s look at a simple comparison. Here’s a simple jQuery demo that makes use of the Star Wars API.
<h1>Star Wars Films</h1>
<ul id="films">
</ul>
<script>
$(document).ready(function() {
$.get('https://swapi.com/api/films', function(res) {
let list = '';
res.results.forEach(function(r) {
list += `<li>${r.title}</li>`;
});
$('#films').html(list);
});
});
</script>
In the sample above, I use $.get
to hit the API and return a list of films. Then I generate a list of titles as li
tag elements with that data and insert it all into a ul
block.
Now, let’s consider an example of this using Vue:
<div id="app">
<h1>Star Wars Films</h1>
<ul>
<li v-for="film in films">{{film.title}}</li>
</ul>
</div>
<script>
const app = new Vue({
el: '#app',
data: {
films: []
},
created() {
fetch('https://swapi.com/api/films')
.then(res => res.json())
.then(res => {
this.films = res.results;
});
}
})
</script>
Probably the best part of this is the use of the v-for
template. Notice how Vue isn’t concerned with the layout (well, at least the JavaScript). The data is fetched from the API. It’s assigned a variable. The layout handles displaying it. I’ve always hated having HTML in my JavaScript and, while solutions exist for that with jQuery, having it baked into Vue makes it a natural fit.
A full (if somewhat trivial) example
To bring it home a bit, let’s consider a more real world example. Our client has asked us to build a fancy Ajax-enabled front-end search interface to a product API. The feature list includes:
- Support filtering by name and product category
- Form validation such that we must supply a search term or a category
- While the API is being hit, show a message to the user and disable the submit button
- When done, handle reporting that no products were shown or list the matches
Let’s begin with the jQuery version. First, the HTML:
<form>
<p>
<label for="search">Search</label>
<input type="search" id="search">
</p>
<p>
<label for="category">Category</label>
<select id="category">
<option></option>
<option>Food</option>
<option>Games</option>
</select>
</p>
<button id="searchBtn">Search</button>
</form>
<div id="status"></div>
<div id="results"></div>
There’s a form with our two filters and two divs. One’s used as a temporary status when searching or reporting errors and one is used to render results. Now, check out the code.
const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';
$(document).ready(() => {
let $search = $('#search');
let $category = $('#category');
let $searchBtn = $('#searchBtn');
let $status = $('#status');
let $results = $('#results');
$searchBtn.on('click', e => {
e.preventDefault();
// First clear previous stuff
$status.html('');
$results.html('');
// OK, now validate form
let term = $search.val();
let category = $category.val();
if(term === '' && category === '') {
$status.html('You must enter a term or select a category.');
return false;
}
$searchBtn.attr('disabled','disabled');
$status.html('Searching - please stand by...');
$.post(productAPI, { name:term, category:category }, body => {
$searchBtn.removeAttr('disabled');
$status.html('');
if(body.results.length === 0) {
$results.html('<p>Sorry, no results!</p>');
return;
}
let result = '<ul>';
body.results.forEach(r => {
result += `<li>${r.name}</li>`
});
result += '</ul>';
$results.html(result);
});
});
});
The code begins by creating a set of variables for each of the DOM items we want to work with — the form fields, button, and divs. The core of the logic is within the click handler for the button. We do validation, and if everything is OK, do a POST request against the API. When it returns, we either render the results or show a message if nothing was matched.
You can work with a complete version of this demo using the CodePen below.
See the Pen
jQuery Full by Raymond Camden (@cfjedimaster)
on CodePen.
Now let’s consider the Vue version. Again, let’s start with the layout:
<div id="app">
<form>
<p>
<label for="search">Search</label>
<input type="search" v-model="search">
</p>
<p>
<label for="category">Category</label>
<select v-model="category">
<option></option>
<option>Food</option>
<option>Games</option>
</select>
</p>
<button @click.prevent="searchProducts" :disabled="searchBtnDisabled">Search</button>
</form>
<div v-html="status"></div>
<ul v-if="results">
<li v-for="result in results">{{result.name}}</li>
</ul>
</div>
From the top, the changes include:
- Wrapping the layout in a div that can be used to let Vue know where to work.
- Using
v-model
for the form fields to make it easy to work with the data. - Using
@click.prevent
to handle doing the main search operation. - Using
:disabled
to bind whether or not the button is disabled to a value in the Vue application (we’ll see that in action in a moment). - The status value is a bit different than earlier examples. While jQuery has a specific method to set text in a DOM item and another for HTML, Vue requires using
v-html
when assigning HTML to a value that’s going to be rendered. If we tried to do{{status}}
with HTML, the tags would be escaped. - Finally, using
v-if
to conditionally render a list of results along withv-for
to handle the iteration.
Now let’s look at the code.
const productAPI = 'https://wt-c2bde7d7dfc8623f121b0eb5a7102930-0.sandbox.auth0-extend.com/productSearch';
const app = new Vue({
el: '#app',
data: {
search: '',
category: '',
status: '',
results: null,
searchBtnDisabled: false
},
methods: {
searchProducts:function() {
this.results = null;
this.status = '';
if(this.search === '' && this.category === '') {
this.status = 'You must enter a term or select a category.';
return;
}
this.searchBtnDisabled = true;
this.status = 'Searching - please stand by...';
fetch(productAPI, {
method: 'POST',
headers: {
'Content-Type':'application/json'
},
body: JSON.stringify({name:this.search,category:this.category})
}).then(res => res.json())
.then(res => {
this.status = '';
this.searchBtnDisabled = false;
this.results = res.results;
if(this.results.length === 0) this.status = '<p>Sorry, no results!</p>';
});
}
}
});
The first block worth calling out is the set of data
fields. Some map to form fields and others to results, status messages, and the like. The searchProducts
method handles much of the same stuff as the jQuery version but, in general, there’s much less code directly tied to the DOM. For example, even though we know the results are listed in an unordered list, the code itself doesn’t worry about that. It simply assigns the value and the markup handles rendering it. Overall, the JavaScript code is much more concerned about logic in comparison to the jQuery code which “feels” like a much nicer separation of concerns.
As before, I’ve got a CodePen for you to try this out yourself:
See the Pen
Vue Full by Raymond Camden (@cfjedimaster)
on CodePen.
Death to jQuery! Long Live Vue!
OK, that’s a bit over the top. As I said in the beginning, I absolutely think that you shouldn’t change a thing if like working with jQuery and it’s working for you.
I can say, however, that Vue feels like a great “next step” for people who are used to working with jQuery. Vue supports complex applications and has a great command line tool for scaffolding and building projects. But for simpler tasks, Vue works as a great “modern jQuery” replacement that has become my tool of choice for development!
For another perspective on using Vue in place of jQuery, check out Sarah Drasner’s “Replacing jQuery With Vue.js: No Build Step Necessary” because it includes a handful of other super helpful examples.
Second article I’ve read from you today Raymond, on point as always. I made the switch early last year and I’ll never look back!
Hi,
How to handle dynamically added the elements to the dom in vue. Say for example, in jQuery,
$(‘button’,{‘onclick’:’callfunction()’}). append.()
May you please explain and elaborate how it xan be useful in scenarios where we donot have control over the html template. In Jquery world we can parse the rendered DOM and work on it. Does vue provide any such feature ?
Hi Raymond,
What a great article showing how Vue is better than jQuery. I was looking for something like this only. Now I will definitely try Vue.js.
Thanks man.
Comparing Vue with jQuery straightforwardly is like comparing apple with orange. And that makes no sense.
Two different things should not be compared with each other straightforwardly. If you would really like to compare, at least specify what respect you are comparing. Like this:
When it comes to DOM traversing and manipulation, jQuery is better than Vue.
When it comes to making apps, Vue is better than jQuery.
Great article..!!
I was thinking of moving since 2-3 months and this article.
Finally decided to move :-)
Great article and really useful comparisons here, will definitely be trying these out!
Thanks!
The declarative approach of Vue surely makes it future-proof. Thank you for one heck of good transititioning article I would recommend someone with jQuery knowledge!
In the section ‘Reading and writing form variables’, why does your html look dodgy with a plus(+) sign and an equal(=) sign after the input elements? Knowing nothing about Vue I began to think this is Vue syntax. But thanks for a great article, my first intro to Vue (I know React well enough).
That’s not Vue, it’s just representing addition in the HTML. Imagine how it would render on the page: “input (first) plus input (second) equals input (sum)”.
Phonegap already use this in many project templates… So, now we can use it in every web and apps project. Great future in code, less frameworks and more productively code
Saying “In a typical jQuery application…” is weird, in my opinion, because jQuery is not created for making applications.
As you mentioned, jQuery is a swiss knife to JavaScript code. It simplifies how we write JavaScript code. That’s it.
At the end of the day, jQuery is JavaScript-simplifying library and Vue is an app-making library. Arguing which one is better is not very meaningful because they are two different things and each one has its advantages and usages.
It would have probably been better to say “here’s how vue means you don’t need to use jquery”.
jQuery has had it’s day for newer projects as dedicated libraries or native API replace it.
Vue is better?! Oh my God!
In all the examples presented here with jquery we need less code to get it, the code is simpler and cleaner with less possibilities of having errors with missing parentheses.
The problems of the variables do not exist if you define them within the scope that you are working.
This is completely a “it works better for me” type article as I hope I made it clear. LOC isn’t always a measure of how simple something is imo. Also, I feel having {{foo}} in my HTML versus writing HTML in JavaScript and using jQuery .html() type things to be better.
Shrug – use what works best for you! :)
Hi! I don’t get what you mean by:
“This was easily one of the things that sold me on Vue the most — it feels easier to tell what is going on. […] If I change the class or ID of the button, I don’t need to go back into my code and worry about updating selectors”
If I change the id=”” value (in you example “app”) don’t I need to change it inside the Vue code too, where it says:
el: ‘#app’
?
Yes but you only have that id correlation once per Vue instance, not many times as you might using the “jQuery way”
@SB please explain what is jQuery way? If I need #app in several places then I declare var myApp = $(“#app”); and use the variable – which in case of update takes one place only.
So you need to add to the html to make stuff work? That will be a mess to implement in your average CMS. Then I prefer jquery. Much cleaner.
The article is good but the premise that jQuery is somehow bad compared to Vue is completely rediculous. I suppose you need controversy for click conversion but really that doesnt belong in the coding world. Overall this article fails in that regard. Please flush the toilet of this library vs that library!
Didn’t get far past the title, did ya?
Hi,
I am a newbie developing a quiz website for my classes at school. I use symfony and currently have a navbar that uses bootstrap (with jquery – it is the only thing on the whole site that uses it). I’m keen to embrace new ideas and was wondering if it makes sense to develop a vue navbar using (bootstrapvue)?? I don’t want to move to a full SPA as I like the simplicity of the routing provided by symfony. I hope my question makes sense, I’m just looking for opinions.
Vue.js is described as a progressive framework, which means it can be plugged into parts of an existing application. It can but does not have to be a complete SPA tool.
data should be function that returns an object rather than an object.
data can be a function. I was trying to keep it simple. :)
Tremendous article and excellent examples. Thank you!
How about an article for jQuery to React?
Yes Ian, comparing a Frontend-Framework with a Frontend-Library is like comparing apple with pears and makes no sense.
Nice and clear article. It would be interesting also to compare a third player: pure javascript.
My doubt often is not if this or that framework is better, but if I really need one.
Adding a framework is always an overload and a slowdown of the the site and in many cases I have the feeling to shoot a fly with a cannon.
That’s a very valid point, and many of my samples are really small, but I was trying to keep things short for the article. I do think “you always need a framework” is a bad thing to assume, however, in most projects I’ve worked on the complexity grew over time and rarely shrank. The minified version of Vue is also really darn small. :)
Great article!
It’s high time to move to Vue.js from jQuery! :)
I just did a massive rewrite. Over a year I had been moving little parts. After the rewrite I have over 36,000 lines moved over. Its so much nicer to be using node modules with a build system and not having the page refresh on each page change. Its a much nicer workflow and a much nicer user experience.
Jquery 4 is coming soon, and it deprecates the sizzle engine, and embraces querySelectorAll among other advances. Jquery is not going anywhere.
Vue looks great, and I like how this article is presented. I have bookmarked and may try Vue one day.
For my needs right now, as mid-level frontend professional, building functional responsive websites with nice presentation tricks and interface features, Jquery is still my go-to library.
Jquery is reliable. It doesn’t complain, it doesn’t break easily, it just works.
I’ve moved on from Jquery animation of course, and fully embrace CSS animation. But for DOM manipulation including chaining Ajax events with interface events, Jquery is still a fantastic library.