For a long time I've iterated on my own small front-end framework fueled by ongoing developer gripes with popular libraries. My goal here is to write a series of posts as a retrospective on that work, summarize design goals and document the problems I faced and how I chose to solve them.
Repo link: j-templates
The framework was used to make Qwiqwit, a multi-player web-game I built with my friend, Paul.
Some of the initial goals were:
- No transpile step besides TypeScript
- Fully typed in the editor, no bindings in string or .html templates
- Sensibly reactive, no need to self-manage change detection
My goals were reactions to pitfalls present in early React/Angular development. This was 6+ years ago so a lot of these complaints have been addressed since.
Hello World
import { Component } from 'j-templates';
import { div } from 'j-templates/DOM';
// Create the Component class
class HelloWorld extends Component {
// Override the Template function
Template() {
return div({}, () => "Hello world");
}
}
// Convert the class to an element function
const helloWorld = Component.ToFunction("hello-world", null, HelloWorld);
// Append a new hello-world element to the page
Component.Attach(document.body, helloWorld({}));
Produces the following markup:
<hello-world>
<div>Hello world</div>
</hello-world>
All elements are represented as functions that take a configuration object. The HelloWorld
component class is converted to a function using the Component.ToFunction(...)
method. Calling one of these function returns a NodeDefinition
object which is the framework's virtual representation of the DOM.
Element functions (i.e. div, span, p) take a second parameter to define children. This function can return a string to create a text node or one/many NodeDefinition
objects. This is all you need to start building static views.
import { Component } from 'j-templates';
import { div, span, h1, b } from 'j-templates/DOM';
class MessageHeader extends Component {
Template() {
return h1({}, () => "Incoming Message");
}
}
const messageHeader = Component.ToFunction("message-header", null, MessageHeader);
class HelloWorld extends Component {
Template() {
return [
messageHeader({}),
div({}, () => [
b({}, () => "Message Body: "),
span({}, () => "Hello world")
])
];
}
}
const helloWorld = Component.ToFunction("hello-world", null, HelloWorld);
Component.Attach(document.body, helloWorld({}));
Produces the following markup:
<hello-world>
<message-header><h1>Incoming Message</h1></message-header>
<div><b>Message Body: </b><span>Hello world</span></div>
</hello-world>
Give it a shot: Code Sandbox. Next post will describe how to bind to data.
Top comments (0)