欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

Angular如何由模板生成DOM树的方法

程序员文章站 2022-03-07 09:37:31
angular等现代web框架极大的提高了开发效率,比如我们经常会在开发过程中写出类似下面的代码:
{{title}}

angular等现代web框架极大的提高了开发效率,比如我们经常会在开发过程中写出类似下面的代码:

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

export class appcomponent {
 title = 'angular';
}

这种模板写法并不是html原生支持的,那么angular又是如何转换这些代码,并显示成我们期望的界面呢? 首先我们来看看angular把上述代码编译成什么样子:

 ...省略了其他代码
 i0.ɵɵelementstart(0, "div");
 i0.ɵɵtext(1, " hello angular\n");
 i0.ɵɵelementend()
 ...省略了其他代码

可以看到,angular把我们写的模板编译成指令的方式,然后通过这些指令生成对应的html.这个过程包含两个步骤:

  1. 把模板编译成上面的产物
  2. 执行产物代码生成html

本文主要围绕步骤二进行展开,步骤一的话可能会在后续另写一篇进行阐述。

观察上面的产物代码,我们不难发现有三个主要方法:elementstart、text、elementend.从它们的命名不难推测,这三个方法的作用分别是开始生成标签、内容赋值、闭合标签。下面我们来尝试自己实现这几个方法,最简单的基础版本大概会是这样:

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 = document.createelement(tagname);
  currentparent!.appendchild(element);
  currentnode = element;
}

function text(textcontent: string): void {
  currentnode!.textcontent = textcontent;
}

function elementend(tagname: string): void {
  currentnode = currentparent;
  currentparent = currentnode!.parentnode;
}

然后在html中可以这样使用:

 <div id="container"></div>
 <script>
 function render() {
  elementopen('div');
  text('div content');
   elementopen('p');
   text('p content');
   elementend('p');
  elementend('div');
 }
 patch(document.getelementbyid('container'), render);
 </script>

上述代码中,text方法参数都被写固定了,实际生成的代码可能类似于text(comp.title)这种形式。那么既然是以变量的形式赋值,当用户进行操作的时候,更新这个变量的值,岂不是又要完全重新执行一遍patch函数么?我们知道dom操作是耗时的,当我们的项目较大时,如果不采取优化措施,势必会影响框架性能。为此我们很容易想到的一个优化思路,在再次执行patch函数时,如果dom节点已经存在我们就重复利用,不再去重新创建并插入dom树。基于这个思路,我们来更新一下代码:

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 && firstchild.tagname.tolowercase() === tagname) {
    currentparent = firstchild;
    return;
  }

  const element = document.createelement(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;
}

本文所述代码,只是表述angular由模板生成dom树的大致思路。具体的angular做了许多优化,而且它实现细节也和本文有区别。不同于现今较为流行的virtual dom实现方式,angular这种实现思路不需要单独创建中间dom对象,减少了内存分配。对此感兴趣的读者可以自行去看angular的实现。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。