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)