brue

Brue allows you to express Vue templating in 100% vanilla javascript resulting in more concise components. Brue componants are indistinguishable from Vue componants and can be used interchangably. Now re-written specifically for Vue 3.x.

Usage no npm install needed!

<script type="module">
  import brue from 'https://cdn.skypack.dev/brue';
</script>

README

Brue

Brue allows you to express Vue templating in 100% vanilla javascript resulting in more concise components. Brue componants are indistinguishable from Vue componants and can be used interchangably. Now re-written specifically for Vue 3.x.

Quick comparison

with Brue:

import Brue from 'brue';
import { reactive } from 'vue';
import CustomComponent from './CustomComponent.vue'

export default Brue('incomingProp', props => {
  const state = reactive({
    double: props.incomingProp * 2
  })
  const doClick = () => console.log('click');
  return $ => { $
    .a('div .example')
      .b('h1').text('Twice as much')
      .b(CustomComponent, { customProp: true })
      .b('div .link').text(state.double)
        .click(doClick)
  }
});

with Vue:

<template>
  <div class="example">
    <h1>Twice as much</h1>
    <custom-component :customProp="true" />
    <div class="link" @click="doClick">
      {{ state.double }}
    </div>
  </div>
</template>
<script>
import { reactive } from 'vue';
import CustomComponent from './CustomComponent.vue'

export default {
  props: ['incomingProp'],
  components: { 
    CustomComponent
  },
  setup(props){
    const state = reactive({
      double: props.incomingProp * 2
    })
    const doClick = () => console.log('click');
    return {
      state,
      doClick
    }
  }
}
</script>

Creating components

Brue() accepts the component properties followed by the setup function. The property names can be spread across the arguments or as a single object or array - Brue expects the setup function to be the last argument. If a property is not specified it will be treated like an HTML attribute.

//Any of these work...
Brue('prop1','prop2', props => {

});
Brue(['prop1','prop2'], (props, context) => {

});
Brue(
  { 
      prop1: String, 
      prop2: Number 
  },
  (props, { slots, attrs, emit}) => {
      // ...
  }
);

Tree builder - $

The component function passed to Brue needs to return a render function. The first argument of the render function (the tree builder variable $) is used to construct the virtual DOM node tree. Every $ function returns $, allowing for continuous chaining. The chain can be suspended to perform loops or execute javascript and then reestablished with $. E.g.

$ => { $
  .a('ul')
  users.map(user => { $
    .b('li').key(user.id)
      .c('span').text(user.name)
  });
}

Defining nodes

.a(), .b(), .c(), etc. (through z) are used to establish a new node (html tag or component), and establish the child-parent relationship. c nodes are children of b nodes, etc.

Strings

Strings passed into nodes as arguments can define the element type, class, and/or id of the node:

  • no prefix indicates the element type
  • . prefix indicates a class
  • # prefix indicates the id
$
.a('div')
  .b('h1 .headerClass #homeHeader').text('Home')
//same as
.a('div')
  .b('h1','#homeHeader', {class: 'headerClass'}).text('Home')

Objects

Objects passed into nodes as arguments will define properties and/or attributes. There's no need to use v-bind - variables and values can be used directly. Note Since Vue components are objects: if the first argument is an object, Brue will assume you're defining the node type as a vue component.

$
.a('div')
  .b(MyComponent, {someProp: true, title: 'Example attribute'})

Node modifiers

Node modifiers are $ functions that can further define characteristics of a node. Any non-alphabet $ function will always modify the last established node.

$
.a('div')
  .style({fontSize: '14px'})
  .on('click', doSomething)

Text

Text nodes can be defined a number of ways:

// all equivalent:
$.a('div').text('')

$.a('div')
  .b().text('Hello')

$.a('div')
  .b(String, 'Hello')

The .b(String, 'Hello') format can make it a little easier to express inline tags (not a Brue strong point):

// <div>Inline <em>tags</em> are a bit verbose</div>
$
.a('div')
  .b(String, 'Inline ')
  .b('em').text('tags')
  .b(String, ' are a bit verbose')

$ API

More to come.

.class({})

Assign classes to the node based on boolean values. The equivalent of v-bind:class.

.a('div .mynode').class({
    active: true,
    deleted: false
})
// <div class="mynode active"></div>

.html(str)

Set the contents of the node to pre formatted html. Equivalent to innerHTML. Any inner children or text nodes will be igonored.

.key()

Within a loop specify a unique key to optimize rendering. Vue docs: Maintaining State

.on()

Add an event listener to the node. Accepts either a single listener or an object of listeners. In addition, event modifiers can be added as suffixes (event.mod) or as optional arguments. Some examples:

$.a('div')
  .on('click', doClick)
//
$.a('div')
  .on({
    'focus': () => console.log('focus'),
    'keydown.ctrl': onCtrlKey
  })
//
$.a('div')
  .on('contextmenu', rightClickFunc, 'prevent','stop')

Modifiers: | events | mouse | key values | key mods | |---|---|---|---| | stop
prevent
capture
self
once
passive | left
right
middle | enter
tab
delete
esc
space
up
down
left
right | ctrl
alt
shift
meta
exact |

.ref()

Assign a reference to the element. Vue docs: Refs

.style({})

Assign styles to the node. The equivalent of v-bind:style.

.a('.mynode').style({
    color: 'red',
    fontSize: '14px'
})

.text(str)

Sets the inner text of the node: .a('p').text('Hello') creates <p>Hello</p>. If the node has any other children, the text will be inserted after the children. Alternatively, use String as the first argument of a node: .a('p').b(String, 'Hello')