petite-vue is an alternative distribution of Vue optimized for progressive enhancement.

Related tags

petite-vue
Overview

petite-vue

petite-vue is an alternative distribution of Vue optimized for progressive enhancement. It provides the same template syntax and reactivity mental model with standard Vue. However, it is specifically optimized for "sprinkling" small amount of interactions on an existing HTML page rendered by a server framework. See more details in how it differs from standard Vue.

  • Only ~5.8kb
  • Vue-compatible template syntax
  • DOM-based, mutates in place
  • Driven by @vue/reactivity

Status

  • This is pretty new. There are probably bugs and there might still be API changes, so use at your own risk. Is it usable though? Very much. Check out the examples to see what it's capable of.

  • The issue list is intentionally disabled because I have higher priority things to focus on for now and don't want to be distracted. If you found a bug, you'll have to either workaround it or submit a PR to fix it yourself. That said, feel free to use the discussions tab to help each other out.

  • Feature requests are unlikely to be accepted at this time - the scope of this project is intentionally kept to a bare minimum.

Usage

petite-vue can be used without a build step. Simply load it from a CDN:

{{ count }}
">
<script src="https://unpkg.com/petite-vue" defer init>script>


<div v-scope="{ count: 0 }">
  {{ count }}
  <button @click="count++">incbutton>
div>
  • Use v-scope to mark regions on the page that should be controlled by petite-vue.
  • The defer attribute makes the script execute after HTML content is parsed.
  • The init attribute tells petite-vue to automatically query and initialize all elements that have v-scope on the page.

Manual Init

If you don't want the auto init, remove the init attribute and move the scripts to end of :

">
<script src="https://unpkg.com/petite-vue">script>
<script>
  PetiteVue.createApp().mount()
script>

Or, use the ES module build:

import { createApp } from 'https://unpkg.com/petite-vue?module' createApp().mount() ">
<script type="module">
  import { createApp } from 'https://unpkg.com/petite-vue?module'
  createApp().mount()
script>

Production CDN URLs

The short CDN URL is meant for prototyping. For production usage, use a fully resolved CDN URL to avoid resolving and redirect cost:

  • Global build: https://unpkg.com/[email protected]/dist/petite-vue.iife.js
    • exposes PetiteVue global, supports auto init
  • ESM build: https://unpkg.com/[email protected]/dist/petite-vue.es.js
    • Must be used with

      {{ count }}

      {{ plusOne }}

      ">
      <script type="module">
        import { createApp } from 'https://unpkg.com/petite-vue?module'
      
        createApp({
          // exposed to all expressions
          count: 0,
          // getters
          get plusOne() {
            return this.count + 1
          },
          // methods
          increment() {
            this.count++
          }
        }).mount()
      script>
      
      
      <div v-scope>
        <p>{{ count }}p>
        <p>{{ plusOne }}p>
        <button @click="increment">incrementbutton>
      div>

Note v-scope doesn't need to have a value here and simply serves as a hint for petite-vue to process the element.

Explicit Mount Target

You can specify a mount target (selector or element) to limit petite-vue to only that region of the page:

createApp().mount('#only-this-div')

This also means you can have multiple petite-vue apps to control different regions on the same page:

createApp({
  // root scope for app one
}).mount('#app1')

createApp({
  // root scope for app two
}).mount('#app2')

Lifecycle Events

You can listen to the mounted and unmounted lifecycle events for each element:

">
<div
  v-if="show"
  @mounted="console.log('mounted on: ', $el)"
  @unmounted="console.log('unmounted: ', $el)"
>div>

v-effect

Use v-effect to execute reactive inline statements:

">
<div v-scope="{ count: 0 }">
  <div v-effect="$el.textContent = count">div>
  <button @click="count++">++button>
div>

The effect uses count which is a reactive data source, so it will re-run whenever count changes.

Another example of replacing the todo-focus directive found in the original Vue TodoMVC example:

">
<input v-effect="if (todo === editedTodo) $el.focus()" />

Components

The concept of "Components" are different in petite-vue, as it is much more bare-bones.

First, reusable scope logic can be created with functions:

import { createApp } from 'https://unpkg.com/petite-vue?module' function Counter(props) { return { count: props.initialCount, inc() { this.count++ }, mounted() { console.log(`I'm mounted!`) } } } createApp({ Counter }).mount()

{{ count }}

{{ count }}

">
<script type="module">
  import { createApp } from 'https://unpkg.com/petite-vue?module'

  function Counter(props) {
    return {
      count: props.initialCount,
      inc() {
        this.count++
      },
      mounted() {
        console.log(`I'm mounted!`)
      }
    }
  }

  createApp({
    Counter
  }).mount()
script>

<div v-scope="Counter({ initialCount: 1 })" @mounted="mounted">
  <p>{{ count }}p>
  <button @click="inc">incrementbutton>
div>

<div v-scope="Counter({ initialCount: 2 })">
  <p>{{ count }}p>
  <button @click="inc">incrementbutton>
div>

Components with Template

If you also want to reuse a piece of template, you can provide a special $template key on a scope object. The value can be the template string, or an ID selector to a