A minimalistic vanilla web component framework
Using native browser features to maximum performance with a few exceptions
Features:
- Templating with JSX
- Pass
objects
to custom element attributes - Observable
props
andstate
Installation:
yarn add @vanille/core
No dependencies
All features are in-house implementations to maximize native functionality, with a few exceptions (check out below!)
Extending web components for native performance
import { BaseView } from '@vanille/core';
export class App extends BaseView {}
Fast templating web components with in-house JSX
export class App extends BaseView {
render() {
return (
<div>
<span>JSX!</span>
</div>
);
}
}
Simple routing
<v-route path="/">
Home
</v-route>
<v-route path="/dashboard">
Dashboard
</v-route>
<v-route path="/users/:id">
User with id
</v-route>
Observables
const object = observable({
user: {
email: '',
contact: {
firstName: ''
}
}
});
object.user.$on('email', (newValue, oldValue, user) => {
console.log(newValue, oldValue, user);
});
object.user.contact.$on('firstName', (newValue, oldValue, user) => {
console.log(newValue, oldValue, user);
});
user.email = 'vanille@js.com';
// log: 'vanille@js.com' '' { email: '', contact: { firstName: '' } }
user.contact.firstName = 'vanille';
// log: 'vanille' '' { contact: { firstName: '' } }
Pass objects in web component attributes
const user = { name: 'vanille' };
<v-app user="user"></v-app>;
export class App extends BaseView {
render() {
return (
<p>{this.props.user.name}</p>
)
}
}
Web component attributes become observable props
const user: User = { name: 'vanille' };
<v-app user="user"></v-app>;
export class App extends BaseView {
setBindings() {
this.props.$on('user', (newValue: User) => {
// user changed
});
this.props.user.$on('name', (newValue: string) => {
// name changed
});
}
}
Private state as observables
export class App extends BaseView {
data() {
return {
name: 'vanille'
};
}
setBindings() {
this.state.$on('name', (newValue) => {
// name changed
});
}
}
Query the DOM with refs
to update elements
export class App extends BaseView {
setBindings() {
this.props.$on('name', (newValue: string) => {
this.refs.name.textContent = newValue;
});
}
render() {
return (
<div>
<span ref="name">JSX!</span>
</div>
);
}
}
Declarative testing with JSX
import { mount } from './test-utils';
// load the component
import './test-utils/Test';
test('can render from jsx', () => {
const $shadow = mount(<v-test />) <---- JSX!
const $el = $shadow.querySelector('[data-id="test"');
expect($el).toBeTruthy();
});