Petite-vue - a JavaScript guide

We're going to look at how you can use a library called petite-vue to greatly simplify building complex web GUIs. Petite-vue is a lightweight front-end framework that has a similar API to the heavyweight Vue. Other front-end frameworks include Angular and React.

Before reading this guide, try my learn JavaScript tutorial or my guide to events and the event loop. You should know the basics of HTML, CSS and JavaScript before you start learning a framework like Preact.

Hello world

In a new folder, perhaps called petite-vue, put this in an index.html:

... all other code you'll see will go below this.

The <script> above loads the petite-vue library and initialises it, looking for things like {{ }} and v-... directives in your HTML.

Let's use those {{ }} and petite-vue will interpret their contents as a JavaScript expression. This is similar to using `${x}`, but here we're inside HTML rather than JavaScript:

Ensure you have vite installed then serve the index.html with vite. Access it in a web browser to check it works, and you should see a page that looks like this:

We could acheive the same in JavaScript without petite-vue:

... but it can quickly get messy when building up more than just one element.

Simple interactive counters

We can create a variable with v-scope="{ count: 5 }", then count++ to increment that when a button is clicked. In petite-vue use @click="..." rather than onclick="...":

Here's the same in vanilla JavaScript:

...hopefully this demonstrates how using petite-vue can greatly simplify your code.

You could easily have more counters, each with its own starting value:

Or better, use v-for to loop over all elements in an array:

The above code will also almost work if you leave out the v-scope="{ count }", but will only increase the counts from the 2nd time you click them. It's important to include the v-scope to ensure the variables are reactive. To create the reactivity petite-vue wraps your variables in an advanced JavaScript concept called proxies, which enables it to be notified when your variables change, so that it can trigger a re-render of parts of the DOM that use them.

Building a todo list

Let's build something a bit more complex - a todo list. Let's plan how we want it to be structured in HTML first:

... so we're going to have a form with a text input and a button. When that form is submitted (by clicking the button or pressing 'enter' in the input), we want a function to run which will add whatever is currently written in the input to the list, and show it.

We don't have to change our HTML much to have the basics of this working interactively with petite-vue:

Instead of onsubmit, with petite-vue we use @submit, just like we did with onclick vs @click. This gives the code we run on submit access to the object we set with v-scope. In this case, that object has a reactive property/variable called todos which is an array that we can push() to.

The .prevent in @submit.prevent stops the form submission from doing its default action, which is to submit the form contents to a server.

It's generally a good idea not to give elements ids if we can help it. Instead of id="item" and todos.push(item.value) we'll let petite-vue control the input element as part of its scope object by including an item property within it with v-scope="{ todos: [], item: '' }". Now we can just do todos.push(item) to add to the list:

We'll also make use of v-if and v-else directives to conditionally render some HTML:

Have you noticed how when you add an item to the list, annoyingly the item you just added is still in the input box? What we want to do here is reset the input to an empty string with item = '', but we're already doing todos.push(item) and we're not allowed to have 2 statements in the submit handler. What we need here is a function which does both actions, then to call that function.

We're going to call the function 'add' and call it with @submit.prevent="add". That function needs access to the scope object properties todos and item, so it's going to live within that scope object:

v-scope="{ todos: [], item: '', add () { this.todos.push(this.item); this.item = '' } }"

Importantly, the add() method needs to refer to other properties within the scope object prefixed with this..

It's starting to get a bit messy as this v-scope object grows to multiple properties including a method. At this stage, it probably makes sense to take that object outside of the HTML and into a separate <script> tag:

Which is then made the scope of your todo list with v-scope="app":

Components

In the world of front-end frameworks like Vue and React we like to talk about pages being composed of multiple components. Above we were building a todo list component, but let's go for a much simpler component for now that just shows a random number.

And this JavaScript inside a <script> tag:

We might want to include multiple copies of this component on a page, each with its own random number:

... except they all have the same random number, because widget is just one JavaScript object. To get them to have their own random number, we'll make a function called widget() instead, which returns a new object, and ensure we call that function when binding the scope:

Sometimes this is called the factory pattern. We have a widget factory, where each widget is separate from every other widget created from that factory.

At the moment, our widget components all look the same (but with different random numbers), but there's nothing stopping the user from varying them:

This may or may not be desirable. If it isn't, then a template can be used to force all instances of a component to have a particular HTML structure:

Notice the $template bit in the JavaScript:

By convention, when using functions to create components, we give those functions uppercase first letters and we call any parameters we send into those component functions props, and use a single object for those props:

Extending our todo list

We're going to improve our todo list and make it into a reusable component, where we can have multiple independent copies of it on a single page.

Our HTML hasn't changed from earlier, but now its inside a <template> so we don't have to define the same structure multiple times:

And we have a function TodoList() used to create new instances of the component, which references the above template's id with $template: '#todo-list'. Notice how todos is given a default value of a blank array, but a user can override this to create a todo list with some items already in it:

Multiple independent instances of this component can then be added to the page:

To finish things off, we're going to make it so when you click a todo list item it gets deleted:

Notice how we get the index i in the todos array, which we then pass as an argument to the del() function which we define within the component:

When should you use petite-vue?

Let's compare the final todo list component in petite-vue:

Or if we knew we only wanted one todo list on the page, so didn't need to make it into a component:

With the equivalent written in vanilla JavaScript, without any libraries (though this version will only work with a single todo list, not multiple ones):

You'll probably find the petite-vue version easier to read and understand - it does a lot of the hard work for us. Here are some other reasons to prefer petite-vue:

Other frameworks

I chose to teach you petite-vue because it's lightweight, doesn't require a build step and is relatively easy to understand. Here are some other options: