When to use Vue or Petite-Vue?
Petite-Vue is an alternative version of the Vue framework. It shares a lot of the same ideas but is significantly smaller package. When using Vue 3 without a build step you have to load a ~60kb
package and ~30kb
when using a build step (recommended). Petite-Vue is just ~6kb
.
The point of
petite-vue
is not just about being small. It's about using the optimal implementation for the intended use case (progressive enhancement).
Although being namesake and very similar, you can't really use them in the same way. With Petite-Vue you can't use all the plugins available. You'll have to find the VanillaJS
version of whatever slider or drag&drop you want and implement it yourself in your programs.
Use case.
Most Vue (or React/Svelte) tutorials will show you how to create a Single Page Application
with its own router and views but in real life you might work with an existing PHP/Python/.NET Multi Page Application
. It doesn't really make sense to rewrite all that to a SPA. So you'll progressively enchance your existing views.
This is the use case we'll dive into with a focus to the developer experience.
Examples.
For Vue components I'll use the Options API which is applicable to Vue 2 & 3. It is actually more similar to the Petite-Vue syntax than the Composition API. For these examples we'll use a very minimal html base with only loading the respective Vue packages and mounting into a div with and id "app".
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Vue Demo</title>
</head>
<body>
<div id="app"></div>
</body>
</html>
To avoid repetition, only fragments of the full html is shown. To open the apps I use the lite-server npm package. If you are trying these examples, use separate base files.
Initialation
Now, we are using cdn sources but you should propably host the files yourself when in production for integrity reasons.
Petite-Vue:
<div id="app" @vue:mounted="init()"></div>
<script src="https://unpkg.com/petite-vue"></script>
<script>
const { createApp } = window.PetiteVue;
createApp({
init() {
console.log('Petite-Vue app mounted!')
}
}).mount('#app')
</script>
Vue:
With Vue it is easier to use the importmap and module syntax.
<div id="app"></div>
<script type="importmap">
{
"imports": {
"vue": "https://cdnjs.cloudflare.com/ajax/libs/vue/3.2.32/vue.esm-browser.min.js"
}
}
</script>
<script type="module">
import { createApp } from 'vue';
createApp({
methods: {
init() {
console.log('Vue 3 mounted!')
}
},
mounted() {
this.init()
}
}).mount('#app')
</script>
We already see some differences in the syntax. Both rely on plain JS objects but the biggest difference is that the Petite-Vue doesn't have any keywords like the Options API has specific keywords on the root level. Also the lifecycle events are called similarly like @click
.
Templates
Petite-Vue:
To display a message from a variable in the template we just add the variable to the app object root and surround it with curly braces in the template. Also we have to add v-scope
attribute to the #app
element to define the scope where the variables are accessible.
<div id="app" @vue:mounted="init()" v-scope>
<h1>{{ message }}</h1>
</div>
<script>
const { createApp } = window.PetiteVue;
createApp({
message: 'We are using Petite-Vue!',
init() {
console.log('Petite-Vue app mounted!')
}
}).mount('#app')
</script>
Vue:
The same in Vue is a bit different with the Options API. The message
variable has to be inside a data
object to be visible in the template. No need for v-scope
here but the curly braces templating is identical.
<div id="app">
<h1>{{ message }}</h1>
</div>
<script type="module">
import { createApp } from 'vue';
createApp({
data: () => ({
message: 'We are using Vue 3!'
}),
methods: {
init() {
console.log('Vue 3 mounted!')
}
},
mounted() {
this.init()
}
}).mount('#app')
</script>
Components
Let's create a simple counter component where you click a button to add a count.
Petite-Vue
We use the v-scope attribute again here but now to define the scope of the PetiteCounter component.
<div id="app" @vue:mounted="init()" v-scope>
<h1>{{ message }}</h1>
<div v-scope="PetiteCounter({})">
<p>The count is officially: {{ count }}</p>
<p>Count multiplied: {{ multiplyCount }}</p>
<button @click="add">Add to count</button>
</div>
</div>
The component object is wrapped inside a function. It has the same syntax and idea that the root object has. To have a computed property like the multiplyCount
here, just use plain JS getter function.
function PetiteCounter(props) {
return {
count: 0,
get multiplyCount() {
return this.count * 2;
},
add() {
this.count += 1;
}
}
}
Add the component function to the root object like you would any other variable.
createApp({
PetiteCounter,
...
})
Vue
In the html template components are introduced in kebab-case
where as the JS objects are declared in CamelCase
.
<div id="app">
<h1>{{ message }}</h1>
<example-counter></example-counter>
</div>
Vue component has to have its own template inside the component object. Again this will repeat the same Options API rules as before in the root app. The computed properties are defined inside a specific computed
object as a function in the same manner as the getter
function in a plain JS object. Just without the get
keyword.
const ExampleCounter = {
template: `<div>
<p>The count is officially: {{ count }}</p>
<p>Count multiplied: {{ multiplyCount }}</p>
<button @click="add">Add to count</button>
</div>`,
data: () => ({
count: 0,
}),
computed: {
multiplyCount() {
return this.count * 2;
}
},
methods: {
add() {
this.count += 1;
}
}
}
In Vue Options API we introduce components by declaring them in the specific components
object.
createApp({
components: {
ExampleCounter,
},
...
})
The Petite-Vue really seems more simpler but the Vue version could be used in a project that is powered by the build step.
Conclusion.
It is really tempting just to use regular Vue after all. Often times even prototypes and minimal viable products end up growing to be the actual product at least partially even though it is highly not recommended. You'll wish you could reuse some of the code after all.
And in the case of MPA, it is possible to configure your build systems to have the view codes in the same project but make the distribution targets multiple. So you can convert your Vue views to use the build step and all the tree shaking available.
I also might be quite biased having used regular Vue with and without build step. I'd argue that in most web applications it works just fine. Petite-Vue is still much more powerful than VanillaJS
so it could be great in environments where file sizes really have to be small.