My first reactive component with Vue.js
My experience moving from jQuery DOM manipulation to Vue.js's data-driven approach, and how I wrote my first reactive component.
For a long time I had been writing JavaScript with jQuery. Form submissions, DOM updates, small interactions — all of it with $('#element').html(...). It worked, but as things grew it produced a pile of code that got harder and harder to follow. A single button click would update the DOM in three different places, and if you forgot to update one of them the screen would be left in an inconsistent state.
I had been hearing about Vue.js for a few months. Last week, before kicking off a new project, I decided to take a closer look. In this post I share what I found: first impressions, first component.
What is the reactive approach?
Reactive programming is based on the principle that the UI updates automatically when the data changes. With jQuery, you had a mental model of the data while you manually updated the DOM; with Vue, you don’t update the DOM — Vue does. You only change the data.
That might sound like a small difference, but in practice it changes quite a lot.
First step: installation
Vue 1.x is the current release. A CDN is enough to add it to a page via a script tag:
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/1.0.26/vue.min.js"></script>
You can also install it via npm, but CDN is more practical for getting started.
First component: a simple counter
The simplest example is a counter:
<div id="uygulama">
<p>Sayı: {{ sayi }}</p>
<button v-on:click="artir">Artır</button>
<button v-on:click="azalt">Azalt</button>
</div>
<script>
new Vue({
el: '#uygulama',
data: {
sayi: 0
},
methods: {
artir: function () {
this.sayi++;
},
azalt: function () {
this.sayi--;
}
}
});
</script>
When sayi inside data changes, the DOM element containing the {{ sayi }} expression is updated automatically. If I had written this with jQuery:
// jQuery version
var sayi = 0;
$('#artir').click(function () {
sayi++;
$('#sayac').text('Sayı: ' + sayi);
});
$('#azalt').click(function () {
sayi--;
$('#sayac').text('Sayı: ' + sayi);
});
I have to update the DOM myself. With Vue, writing this.sayi++ is enough; Vue handles the rest.
There is a hidden trap in the jQuery version too: the sayi variable lives in the global scope, or needs to be wrapped in a closure. As soon as you have more than one counter, that gets messy fast. In Vue, each component’s data function brings its own closed scope; components never step on each other’s data.
Something more realistic: a list filter
The counter example was fine, but it doesn’t teach much. Let’s try a real UI piece — a searchable list:
<div id="uygulama">
<input v-model="arama" placeholder="Ara...">
<ul>
<li v-for="urun in filtreliUrunler">{{ urun.ad }}</li>
</ul>
<p v-if="filtreliUrunler.length === 0">Sonuç bulunamadı.</p>
</div>
<script>
new Vue({
el: '#uygulama',
data: {
arama: '',
urunler: [
{ ad: 'Klavye' },
{ ad: 'Mouse' },
{ ad: 'Monitör' },
{ ad: 'Kulaklık' }
]
},
computed: {
filtreliUrunler: function () {
var arama = this.arama.toLowerCase();
return this.urunler.filter(function (urun) {
return urun.ad.toLowerCase().indexOf(arama) !== -1;
});
}
}
});
</script>
v-model binds the input value to arama two-way. The computed property filtreliUrunler is automatically recalculated whenever arama changes. v-for hands the list off to Vue to manage.
Writing this in jQuery isn’t hard, of course, but you have to wire everything up yourself: listen for the input event, filter the list, clear the existing li elements, create new ones, append them to the DOM.
Computed properties make a meaningful difference here. A value in the computed block is read from cache as long as the data it depends on hasn’t changed. That means filtreliUrunler isn’t recalculated from scratch on every keystroke — it’s recalculated when arama changes. For a small list this difference is imperceptible, but it matters as the list grows.
My observations
Vue’s learning curve is low. You can drop it into an existing HTML page with a <script> tag and start using it right away. That allows you to adopt it incrementally without having to rewrite the whole project from scratch — a significant plus for me.
Directives like v-bind, v-on, v-if, and v-for are clear and readable inside the HTML. You can glance at a template file and quickly understand what it’s doing.
On the downside: how cross-component state management will work at scale is something I haven’t fully figured out yet. The question of what to do when two separate components need the same data is still open. For now, it’s very satisfying for small-to-medium-sized UI pieces.
In the coming weeks I’m planning to look into components and Vue Router.
Comments
Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.