A Lightweight JavaScript Template Compilation Engine

Converting template markup into an executable JavaScript function enables on-demand HTML generation. Runtime data serves as the execution context, allowing the compiled routine to produce markup strings dynamically. While string concatenation suffices for modern WebKit environments, legacy Internet Explorer rendering benefits significantly from array-based joining to prevant quadratic memory overhead.

Markup Definition

<script id="dynamic-row" type="text/template">
<% 
    for(var i = 0, len = users.length; i < len; i++) { 
        var record = users[i];
%>
    <tr class="<%= i === 0 ? 'row-start' : '' %>">
        <td><%= record.name %></td>
        <td><%= record.age %></td>
        <td><%= record.location || 'Unknown' %></td>
    </tr>
<% } %>
</script>

Delimiters enclosed by <% %> contain executable statements. Syntax featuring <%= %> handles direct variable interpolation. The data payload defaults to the identifier users, which can be overridden via configuration.

Execution Workflow

document.addEventListener('DOMContentLoaded', () => {
    const renderer = new MiniTemplate('dynamic-row');
    
    const payload = {
        users: [
            { name: 'Alice', age: 28, location: 'Shanghai' },
            { name: 'Bob', age: 34, location: 'Beijing' },
            { name: 'Charlie', age: 22 }
        ]
    };

    document.querySelector('#output-table').innerHTML = renderer.render(payload);
});

Caching the instance allows repeated rendering passes without triggering recompilation cycles.

Core Implementation

The parsing routine employs regular expressions to tokanize markup, isolating static segments from dynamic blocks. These segments are transformed into a safe aggregation sequence enclosed within a Function constructor.

const MiniTemplate = (() => {
    const PATTERN_LOGIC = /<%\s*([\s\S]*?)\s*%>/g;
    const PATTERN_OUTPUT = /<%=\s*([\s\S]*?)\s*%>/g;
    const DEFAULT_CTX = 'data';

    function compile(sourceId, cfg = {}) {
        const el = document.getElementById(sourceId);
        if (!el) throw new ReferenceError('DOM element not found.');

        let raw = el.innerHTML;
        const ctxAlias = cfg.ctx || DEFAULT_CTX;
        
        // Combine patterns for sequential processing
        const combinedRegex = new RegExp(
            `(${PATTERN_OUTPUT.source})|(${PATTERN_LOGIC.source})`, 'g'
        );

        let builder = `var _buffer=[], ${ctxAlias}=this;`;
        let pos = 0;
        let match;

        while ((match = combinedRegex.exec(raw)) !== null) {
            const staticChunk = raw.slice(pos, match.index).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n');
            builder += `_buffer.push('${staticChunk}');`;
            
            if (match[1] !== undefined) {
                builder += `_buffer.push(String(${match[1]}));`;
            } else {
                const statement = match[2].trim().replace(/\n/g, ';');
                builder += `${statement};`;
            }
            pos = combinedRegex.lastIndex;
        }

        const remainder = raw.slice(pos).replace(/\\/g, '\\\\').replace(/'/g, "\\'").replace(/\n/g, '\\n');
        builder += `_buffer.push('${remainder}');`;
        builder += 'return _buffer.join("");';

        return new Function(builder);
    }

    return class {
        constructor(id, opts) {
            this.id = id;
            this.opts = opts || {};
            this._fn = compile(id, this.opts);
        }
        render(context) {
            return this._fn.call(context);
        }
    };
})();

Architecture Optimization

Switching from iterative += operators to an array buffer drasticaly reduces garbage collection pressure in constrained environments. Quote and newline escaping during static segment extraction guarantees syntactic validity when the generated string feeds into the Function constructor. This design maintains minimal footprint while delivering predictable performance across diverse runtime versions.

Tags: javascript template-engine frontend-architecture dom-rendering code-generation

Posted on Sat, 04 Jul 2026 18:03:51 +0000 by amylisa