KimuRender โ
KimuRender is the rendering engine of the KIMU framework, based on the Lit library. It provides an efficient and reactive system for rendering templates and managing the DOM.
Overview โ
KimuRender handles:
- Template rendering using Lit HTML templates
- Reactive updates based on property changes
- Performance optimization with efficient diff algorithms
- Component lifecycle integration
- Style encapsulation with Shadow DOM
Key Features โ
Lit-based Template System โ
Uses Lit's powerful template system for declarative UI:
typescript
import { html, css } from 'lit';
import { KimuRender } from './kimu-render';
const template = html`
<div class="container">
<h1>${title}</h1>
<p>${description}</p>
</div>
`;
KimuRender.render(template, container);Reactive Property System โ
Automatic re-rendering when properties change:
typescript
@KimuComponent({
tag: 'reactive-component',
name: 'Reactive Component'
})
export class ReactiveComponent extends HTMLElement {
@property({ type: String })
title = 'Default Title';
@property({ type: Number })
count = 0;
render() {
return html`
<div>
<h1>${this.title}</h1>
<p>Count: ${this.count}</p>
<button @click=${this.increment}>+</button>
</div>
`;
}
private increment() {
this.count++;
// Automatic re-render triggered
}
}Core Methods โ
render() โ
typescript
public static render(
template: TemplateResult,
container: HTMLElement | ShadowRoot,
options?: RenderOptions
): void {
render(template, container, options);
}Renders a Lit template into a DOM container.
Parameters:
template(TemplateResult): The Lit HTML template to rendercontainer(HTMLElement | ShadowRoot): Target containeroptions(RenderOptions): Rendering options
Example:
typescript
const template = html`<p>Hello, ${name}!</p>`;
KimuRender.render(template, this.shadowRoot);createTemplateResult() โ
typescript
public static createTemplateResult(
strings: TemplateStringsArray,
values: any[]
): TemplateResult {
return html(strings, ...values);
}Creates a template result from template strings and values.
renderStyles() โ
typescript
public static renderStyles(styles: CSSResult[]): TemplateResult {
return html`
<style>
${styles.map(style => style.cssText).join('\n')}
</style>
`;
}Renders CSS styles as a template result.
Template Syntax โ
Basic Interpolation โ
typescript
const name = 'World';
const template = html`<h1>Hello, ${name}!</h1>`;Conditional Rendering โ
typescript
const template = html`
<div>
${isLoggedIn
? html`<p>Welcome back!</p>`
: html`<p>Please log in</p>`
}
</div>
`;List Rendering โ
typescript
const items = ['apple', 'banana', 'orange'];
const template = html`
<ul>
${items.map(item => html`<li>${item}</li>`)}
</ul>
`;Event Handling โ
typescript
const template = html`
<button @click=${handleClick}>
Click me
</button>
`;
function handleClick(event: Event) {
console.log('Button clicked!');
}Property Binding โ
typescript
const template = html`
<input
.value=${inputValue}
@input=${handleInput}
?disabled=${isDisabled}
/>
`;Attribute Binding โ
typescript
const template = html`
<div
class=${classNames}
id=${elementId}
data-value=${dataValue}
>
Content
</div>
`;Advanced Features โ
Directives โ
typescript
import { classMap, styleMap } from 'lit/directives/class-map.js';
const template = html`
<div
class=${classMap({
active: isActive,
disabled: isDisabled,
'custom-class': hasCustomClass
})}
style=${styleMap({
color: textColor,
fontSize: `${fontSize}px`,
display: isVisible ? 'block' : 'none'
})}
>
Content with dynamic classes and styles
</div>
`;Refs and Element Access โ
typescript
import { ref, createRef } from 'lit/directives/ref.js';
export class MyComponent extends HTMLElement {
private inputRef = createRef<HTMLInputElement>();
render() {
return html`
<input ${ref(this.inputRef)} type="text" />
<button @click=${this.focusInput}>Focus Input</button>
`;
}
private focusInput() {
this.inputRef.value?.focus();
}
}Async Templates โ
typescript
import { until } from 'lit/directives/until.js';
export class AsyncComponent extends HTMLElement {
private async loadData(): Promise<TemplateResult> {
const data = await fetch('/api/data').then(r => r.json());
return html`<div>${data.content}</div>`;
}
render() {
return html`
<div>
${until(
this.loadData(),
html`<div class="loading">Loading...</div>`
)}
</div>
`;
}
}Style Management โ
CSS Results โ
typescript
import { css } from 'lit';
const styles = css`
:host {
display: block;
padding: 1rem;
}
.title {
color: var(--primary-color, #007acc);
font-size: 1.5rem;
margin-bottom: 1rem;
}
.content {
line-height: 1.6;
}
`;Dynamic Styles โ
typescript
const dynamicStyles = css`
.container {
background-color: ${backgroundColor};
border: 1px solid ${borderColor};
}
`;Style Composition โ
typescript
const baseStyles = css`
:host {
display: block;
}
`;
const themeStyles = css`
.dark-theme {
background: #333;
color: #fff;
}
`;
const combinedStyles = [baseStyles, themeStyles];Performance Optimization โ
Keyed Lists โ
typescript
import { repeat } from 'lit/directives/repeat.js';
const template = html`
<ul>
${repeat(
items,
(item) => item.id, // Key function
(item) => html`<li>${item.name}</li>` // Template function
)}
</ul>
`;Lazy Rendering โ
typescript
import { lazy } from 'lit/directives/lazy.js';
const template = html`
<div>
${lazy(() => expensiveTemplate())}
</div>
`;Memoization โ
typescript
import { cache } from 'lit/directives/cache.js';
const template = html`
<div>
${cache(
isDetailView
? html`<detail-view .data=${data}></detail-view>`
: html`<summary-view .data=${data}></summary-view>`
)}
</div>
`;Integration with KIMU Components โ
Component Base Class โ
typescript
@KimuComponent({
tag: 'my-component',
name: 'My Component'
})
export class MyComponent extends HTMLElement {
private shadowRoot: ShadowRoot;
constructor() {
super();
this.shadowRoot = this.attachShadow({ mode: 'open' });
}
connectedCallback() {
this.render();
}
private render() {
const template = html`
<style>
${this.getStyles()}
</style>
<div class="container">
${this.getContent()}
</div>
`;
KimuRender.render(template, this.shadowRoot);
}
private getStyles() {
return css`
.container {
padding: 1rem;
border: 1px solid #ddd;
}
`;
}
private getContent() {
return html`
<h2>My Component</h2>
<p>This is a KIMU component using KimuRender</p>
`;
}
}Reactive Properties โ
typescript
export class ReactiveKimuComponent extends HTMLElement {
private _title = '';
private _items: string[] = [];
get title() { return this._title; }
set title(value: string) {
this._title = value;
this.requestUpdate();
}
get items() { return this._items; }
set items(value: string[]) {
this._items = value;
this.requestUpdate();
}
private requestUpdate() {
// Debounced re-render
if (!this.updateScheduled) {
this.updateScheduled = true;
requestAnimationFrame(() => {
this.render();
this.updateScheduled = false;
});
}
}
private render() {
const template = html`
<div>
<h1>${this.title}</h1>
<ul>
${this.items.map(item => html`<li>${item}</li>`)}
</ul>
</div>
`;
KimuRender.render(template, this.shadowRoot);
}
}Error Handling โ
Template Error Boundaries โ
typescript
function safeTemplate(templateFn: () => TemplateResult): TemplateResult {
try {
return templateFn();
} catch (error) {
console.error('Template error:', error);
return html`
<div class="error">
<h3>โ ๏ธ Rendering Error</h3>
<p>Something went wrong while rendering this component.</p>
</div>
`;
}
}
// Usage
const template = safeTemplate(() => html`
<div>${potentiallyErrorProneContent}</div>
`);Validation โ
typescript
function validateTemplate(template: unknown): template is TemplateResult {
return template && typeof template === 'object' && 'strings' in template;
}Best Practices โ
Template Organization โ
typescript
export class WellOrganizedComponent extends HTMLElement {
// Separate template methods for better organization
private renderHeader() {
return html`
<header class="component-header">
<h1>${this.title}</h1>
</header>
`;
}
private renderContent() {
return html`
<main class="component-content">
${this.items.map(item => this.renderItem(item))}
</main>
`;
}
private renderItem(item: any) {
return html`
<div class="item" data-id=${item.id}>
<h3>${item.title}</h3>
<p>${item.description}</p>
</div>
`;
}
private renderFooter() {
return html`
<footer class="component-footer">
<button @click=${this.handleAction}>Action</button>
</footer>
`;
}
render() {
return html`
<div class="component-wrapper">
${this.renderHeader()}
${this.renderContent()}
${this.renderFooter()}
</div>
`;
}
}Performance Tips โ
- Use keyed lists for dynamic content
- Memoize expensive templates with cache directive
- Avoid creating functions in templates - they break memoization
- Use lazy directive for expensive content
- Batch property updates to avoid multiple re-renders
Accessibility โ
typescript
const accessibleTemplate = html`
<div
role="button"
tabindex="0"
aria-label=${buttonLabel}
aria-pressed=${isPressed}
@click=${handleClick}
@keydown=${handleKeydown}
>
${buttonText}
</div>
`;Related Components โ
- KimuComponentElement - Base class for components
- KimuComponent Decorator - Component registration
- KimuEngine - Template engine integration
- Extension Development - Using render in extensions