Component
Github Repository Example Project Blog PostComponent is a reactive javascript library.
Note: I will refer to React a lot because that is the library I am most familiar with. So a lot of the things I do in this project comes from the perspective of someone who is heavily influenced by React.
How does it work?
The biggest difference between Component and React is how rendering is handled. React components create a tree of elements for each block of JSX. Each node of this tree represents an element, either an HTML element, a React Component or a Fragment. Note that these aren't the elements themselves, these are simply representations of them. The actually rendering of them is handled by the ReactDOM.render()
function, where it takes this tree, runs its diff algorithm on it to generate/modify the actual HTML element that it corresponds to and then recursively calls this render function on the children of each node of the tree to finally create the complete HTML you see on on your browser screen. So react will start calling the render function and run it's diff algorithm and element generation of each individual element whether it changes on not. Now any component you write is mostly static. Most of the elements within your component will not change during the course of its life, you'll change some styles and swap between other components and HTML elements. But it is very unlikely that your changing parts will outnumber your static parts. Now react assumes everybit of your component can change at any moment so this is why it does this.
How component does this is very different. It parses each block of JSX during build time to convert all the static parts into a static HTML element that will act as a template and converts all the dynamic parts into a function that applies those changes to that template. So each block will only get diffed at it's root to see if it's the same template as before. Internal elements that are know to be unchanging are not examined during runtime. Component does this with a babel plugin.
The source of a component and output of it in react and component:// Source
function Component(){
return (
<div style="background: green">
<p>Hello, world!</p>
<br/>
<NestedComponent propName={propValue}/>
{condition?(<span />):(<div />)}
</div>
)
}
// React Output
function Component(){
return React.createElement('div', {'style': 'background: green'},
React.createElement('p', {}, 'Hello, world!'),
React.createElement('br'),
React.createElement(NestedComponent, {'propName': propValue})
condition?React.createElement('span'):React.createElement('div'),
);
}
// Component Output
// The generated templates
const template0 = template(
"<div style="background: green"><p>Hello, world!</p><br></div>",
(domElement) => {
elm0: domElement,
elm1: domElement.firstChild.nextSibling.nextSibling
}
);
const template1 = template(
"<span></span>",
(domElement) => {}
);
const template2 = template(
"<div></div>",
(domElement) => {}
);
function Component(){
// Root div element
return {
type: "template",
template: template0,
templateFunction: (templateState, context)=>{
let anchor = { node: elm1 };
// The NestedComponent
templateState.children[0] = compareAndCreate(
templateState.children[0],
{
type: "component",
component: NestedComponent
},
elm0,
anchor
)
// The conditional expression
templateState.children[1] = compareAndCreate(
templateState.children[1],
condition?
{
type: "template",
template: template1,
templateFunction: (templateState, context)=>{}
}:
{
type: "template",
template: template2,
templateFunction: (templateState, context)=>{}
},
elm0,
anchor
)
}
}
}
Now the output of the component code looks very large and complicated. It's just really verbose. What's happening is that component takes every root JSX element and creates a new template so JSX blocks that are contained within expressions also get converted into templates. I could make this a lot less verbose but I just haven't gotten around to it. I shall eventually.
SolidJS does this same thing but the final output looks much nicer. I'll get there. But what component does differently is in how it manages component lifecycle. SolidJS uses hooks. I chose not to use hooks and instead chose to make them classes with life cycle methods like in the early days of React. This approach seemed more intuitive to me.
State is not something that resides outside of your component that you access and manage using a hook. Normally, the state of any object is the object itself, the fields/values of that object. So the state of your component is just the fields of that class. There is no special state field. There's just a rerender method that initiates a re-render of your component once you've changed state.
Some things it does entirely differently
I think the biggest difference in component and other libraries is how component can be used in code that isn't component code and vice versa. What enables this is:
- Every component has a
getDomElement()
method which will return the HTML Element that is generated by that component. And so you can go place this HTML element anywhere you want in your exising UI. - There is a special component
<Sub child={}/>
with a propchild
that accepts an HTML Element and it just places that HTML element wherever Sub is and this element will not be subject to the diff algorithm. It will exist as it is and it can be manipulated directly as you please. So you can use any 3rd part libraries you want with this. - Comibing the two things above, what you can do is create a component, get its HTML Element with getDomElement() and then place that in any <Sub /> component you want anywhere in your app thereby moving the component within the tree while still preseriving the state of that component.
Why did I go through the trouble of doing all this?
In essence I tried to write my own reactive javascript library for a larger project I was working on. I didn't want to use existing frameworks because performance and customizability were some things I wanted prioritized. And using an existing framework would mean having to either structure a project around that framework and possibly it's limitations and restriction or modifying the framework to my liking which would then mean learning it inside out just to jerry-rig it. Neither of these sounded appealing and so I decided to write my own with customizability in mind but also since I myself am writing it, I wouldn't have to learn a whole new code base to change it to. I would just know just what to change since I wrote it myself.