SoFunction
Updated on 2025-04-13

How to generate DOM tree from templates in Angular

Modern Web frameworks such as Angular have greatly improved development efficiency. For example, we often write codes like the following during the development process:

<div>
  {{title}}
</div>

export class AppComponent {
 title = 'angular';
}

This template writing method is not natively supported by HTML, so how does Angular convert these codes and display them into the interface we expect? First, let’s take a look at what Angular compiles the above code to:

 ...Other codes are omitted
 i0.ɵɵelementStart(0, "div");
 i0.ɵɵtext(1, " hello angular\n");
 i0.ɵɵelementEnd()
 ...Other codes are omitted

As you can see, Angular compiles the templates we wrote into instructions, and then generates the corresponding HTML through these instructions. This process consists of two steps:

  1. Compile the template into the above product
  2. Execute product code to generate HTML

This article mainly focuses on step two, and if step one is in the future, another article may be explained.

Observing the product code above, it is not difficult to find that there are three main methods: elementStart, text, and elementEnd. It is not difficult to infer from their naming that the functions of these three methods are to start generating labels, content assignment, and closing labels. Let's try to implement these methods yourself. The simplest basic version will probably be like this:

let currentNode: Node | null = null;
let currentParent: Node | null = null;

function patch(host: Node | DocumentFragment, render: () => void): void {
  currentNode = host;
  render();
}

function elementOpen(tagName: string): void {
  currentParent = currentNode;
  const element = (tagName);
  currentParent!.appendChild(element);
  currentNode = element;
}

function text(textContent: string): void {
  currentNode!.textContent = textContent;
}

function elementEnd(tagName: string): void {
  currentNode = currentParent;
  currentParent = currentNode!.parentNode;
}

Then in HTML it can be used like this:

 <div ></div>
 <script>
 function render() {
  elementOpen('div');
  text('div content');
   elementOpen('p');
   text('p content');
   elementEnd('p');
  elementEnd('div');
 }
 patch(('container'), render);
 </script>

In the above code, the text method parameters are written and fixed, and the actual generated code may be similar to the form of text(). So since the value is assigned in the form of a variable, when the user performs operations, wouldn’t he have to completely execute the patch function again? We know that DOM operations are time-consuming. When our project is large, if optimization measures are not taken, it will inevitably affect the performance of the framework. For this reason, an optimization idea that we can easily think of is that when executing the patch function again, if the DOM node already exists, we will reuse it and no longer recreate and insert the DOM tree. Based on this idea, let's update the code:

let currentNode: Node | null = null;
let currentParent: Node | null = null;


function patch(host: Node | DocumentFragment, render: () => void): void {
  currentNode = host;
  render();
}

function elementOpen(tagName: string): void {
  currentParent = currentNode;

  const firstChild = (currentParent as Element).firstElementChild;
  if (firstChild && () === tagName) {
    currentParent = firstChild;
    return;
  }

  const element = (tagName);
  currentParent!.appendChild(element);
  currentNode = element;
}

function text(textContent: string): void {
  if (currentNode!.textContent !== textContent) {
    currentNode!.textContent = textContent;
  }
}

function elementEnd(tagName: string): void {
  currentNode = currentParent;
  currentParent = currentNode!.parentNode;
}

The code described in this article only expresses the general idea of ​​Angular to generate dom trees from templates. The specific Angular has made many optimizations, and its implementation details are also different from those in this article. Unlike the more popular virtual DOM implementation method today, Angular implementation idea does not require the creation of intermediate DOM objects separately, which reduces memory allocation. Readers who are interested in this can check out the implementation of Angular on their own.

The above is all the content of this article. I hope it will be helpful to everyone's study and I hope everyone will support me more.