petite-vue

repository·main·Indexed 27 days ago

https://github.com/vuejs/petite-vue

A lightweight (~6kb) subset of Vue optimized for progressive enhancement. It is designed to sprinkle interactivity onto existing HTML rendered by server frameworks by mutating the DOM in place. Features include a minimal API with createApp(), v-scope, v-effect, and support for custom directives and components.

Tokens
3.3K
Snippets
8
Records
26
Agent score
94%

What's inside petite-vue

  1. Compare petite-vue with Alpine.js

    main

    petite-vue is designed as a minimal, Vue-compatible alternative to Alpine.js for progressive enhancement. Key differences include:

    • Size: petite-vue is approximately half the size of Alpine.
    • Transitions: petite-vue does not include a transition system.
    • Vue Compatibility: While Alpine has its own design goals, petite-vue aims to align with standard Vue behavior to reduce friction when migrating to full Vue applications.
  2. Create reusable components with functions and templates

    main

    In petite-vue, components are implemented as functions that return a scope object.

    Logic-only components

    Return an object containing state and methods. You can pass props to the function to initialize state.

    Components with templates

    To reuse a piece of HTML, include a $template key in the returned object. The value can be a template string or an ID selector for a <template> element. Using a <template> element is recommended for better performance.

    function Counter(props) {
      return {
        $template: '#counter-template',
        count: props.initialCount,
        inc() {
          this.count++
        }
      }
    }
    <script type="module">
      import { createApp } from 'https://unpkg.com/petite-vue?module'
    
      function Counter(props) {
        return {
          $template: '#counter-template',
          count: props.initialCount,
          inc() {
            this.count++
          }
        }
      }
    
      createApp({
        Counter
      }).mount()
    </script>
    
    <template id="counter-template">
      My count is {{ count }}
      <button @click="inc">++</button>
    </template>
    
    <!-- reuse it -->
    <div v-scope="Counter({ initialCount: 1 })"></div>
    <div v-scope="Counter({ initialCount: 2 })"></div>
  3. Install and use petite-vue via CDN

    main

    You can use petite-vue without a build step by loading it from a CDN.

    Auto-initialization

    To automatically find and initialize all elements with the v-scope attribute, use the init attribute in your script tag:

    <script src="https://unpkg.com/petite-vue" defer init></script>
    
    <div v-scope="{ count: 0 }">
      {{ count }}
      <button @click="count++">inc</button>
    </div>

    Manual Initialization

    If you prefer manual control, remove the init attribute and call createApp().mount() manually. You can use the global build or the ES module build.

    Global Build (IIFE):

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

    ES Module Build:

    <script type="module">
      import { createApp } from 'https://unpkg.com/petite-vue?module'
      createApp().mount()
    </script>
    <script src="https://unpkg.com/petite-vue" defer init></script>
    
    <!-- anywhere on the page -->
    <div v-scope="{ count: 0 }">
      {{ count }}
      <button @click="count++">inc</button>
    </div>
  4. Configure custom delimiters

    main

    If you are using a server-side templating engine that also uses mustache syntax (e.g., {{ }}), you can change the petite-vue delimiters by passing $delimiters to the root scope in createApp().

    createApp({
      $delimiters: ['${', '}']
    }).mount()
  5. Initialize petite-vue via the `init` attribute

    main
    If you are loading petite-vue via a <script> tag, you can automatically initialize and mount the application to the document by adding the init attribute to the script tag. This is useful for quick setups without manual JavaScript calls.
  6. Security considerations for XSS and CSP

    main

    XSS Prevention

    petite-vue evaluates JavaScript expressions in templates. If you mount petite-vue on a DOM region containing non-sanitized user-submitted HTML, it may be vulnerable to XSS attacks. To mitigate this:

    • Use an explicit mount target to ensure petite-vue only processes DOM elements controlled by you.
    • Sanitize any user-submitted HTML used within v-scope attributes.

    Content Security Policy (CSP)

    petite-vue uses new Function() to evaluate expressions. This may be blocked by strict CSP settings. There is no dedicated CSP build because the library relies on an expression parser to remain lightweight. If your project requires a strict CSP, consider using standard Vue with pre-compiled templates instead.

  7. Use lifecycle events vue:mounted and vue:unmounted

    main

    You can listen to lifecycle events for elements using the vue: prefix. The available events are @vue:mounted and @vue:unmounted.

    <div
      v-if="show"
      @vue:mounted="console.log('mounted on: ', $el)"
      @vue:unmounted="console.log('unmounted: ', $el)"
    ></div>
  8. Mount petite-vue to a specific target

    main

    To limit petite-vue to a specific region of the page, pass a CSS selector or a DOM element to the .mount() method. This allows you to run multiple independent petite-vue applications on the same page.

    createApp().mount('#only-this-div')
  9. Implement custom directives

    main

    Custom directives are registered using .directive(name, handler). The handler receives a context object (ctx) containing:

    • el: The element the directive is bound to.
    • exp: The raw value expression (e.g., x in v-dir="x").
    • arg: The argument (e.g., foo in v-dir:foo).
    • modifiers: An object of modifiers (e.g., { mod: true } for v-dir.mod).
    • get(): Evaluates the expression and returns its value.
    • get(expression): Evaluates an arbitrary expression in the current scope.
    • effect(fn): Runs a reactive effect that re-runs when get() values change.

    Returning a function from the handler allows for cleanup when the element is unmounted.

    const myDirective = (ctx) => {
      // ... access ctx.el, ctx.exp, etc.
      
      ctx.effect(() => {
        console.log(ctx.get())
      })
    
      return () => {
        // cleanup
      }
    }
    
    createApp().directive('my-dir', myDirective).mount()
  10. Execute reactive statements with v-effect

    main

    The v-effect directive allows you to execute reactive inline statements. The code inside the directive will re-run whenever any reactive data source used within it changes.

    <div v-scope="{ count: 0 }">
      <div v-effect="$el.textContent = count"></div>
      <button @click="count++">++</button>
    </div>
  11. Configure the root scope with createApp()

    main

    The createApp function accepts a data object that serves as the root scope for all expressions in the application. This object can contain reactive properties, getters, and methods.

    When using v-scope on an element, you can omit the value if the scope is already provided by the root createApp configuration.

    <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>
    
    <!-- v-scope value can be omitted -->
    <div v-scope>
      <p>{{ count }}</p>
      <p>{{ plusOne }}</p>
      <button @click="increment">increment</button>
    </div>