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


Posted in Javascript onDecember 23, 2019

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的实现。

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

Javascript 相关文章推荐
一个很简单的办法实现TD的加亮效果.
Jun 29 Javascript
wordpress之js库集合研究介绍
Aug 17 Javascript
JavaScript三元运算符的多种使用技巧
Apr 16 Javascript
javascript特效实现——当前时间和倒计时效果的简单实例
Jul 20 Javascript
简洁实用的BootStrap jQuery手风琴插件
Aug 31 Javascript
node.js中module.exports与exports用法上的区别
Sep 02 Javascript
利用node.js搭建简单web服务器的方法教程
Feb 20 Javascript
使用 Vue.js 仿百度搜索框的实例代码
May 09 Javascript
vuejs实现递归树型菜单组件
Jan 13 Javascript
原生JS+HTML5实现跟随鼠标一起流动的粒子动画效果
May 03 Javascript
可能被忽略的一些JavaScript数组方法细节
Feb 28 Javascript
如何用JavaScipt测网速
May 09 Javascript
Vue+Node实现的商城用户管理功能示例
Dec 23 #Javascript
java遇到微信小程序 &quot;支付验证签名失败&quot; 问题解决
Dec 22 #Javascript
webpack打包html里面img后src为“[object Module]”问题
Dec 22 #Javascript
node.js事件轮询机制原理知识点
Dec 22 #Javascript
javascript实现fetch请求返回的统一拦截
Dec 22 #Javascript
详解vue-router 动态路由下子页面多页共活的解决方案
Dec 22 #Javascript
判断JavaScript中的两个变量是否相等的操作符
Dec 21 #Javascript
You might like
Linux中用PHP判断程序运行状态的2个方法
2014/05/04 PHP
php表单提交实例讲解
2015/11/12 PHP
Zend Framework开发入门经典教程
2016/03/23 PHP
windows下的WAMP环境搭建图文教程(推荐)
2017/07/27 PHP
List the Stored Procedures in a SQL Server database
2007/06/20 Javascript
JavaScript 定义function的三种方式小结
2009/10/16 Javascript
javascript XMLHttpRequest对象全面剖析
2010/04/24 Javascript
JavaScript自执行闭包的小例子
2013/06/29 Javascript
快速解决FusionCharts联动的中文乱码问题
2013/12/04 Javascript
jQuery中[attribute^=value]选择器用法实例
2014/12/31 Javascript
Jquery实现地铁线路指示灯提示牌效果的方法
2015/03/02 Javascript
Angularjs整合微信UI(weui)
2016/03/15 Javascript
HTML页面,测试JS对C函数的调用简单实例
2016/08/09 Javascript
NodeJS中的MongoDB快速入门详细教程
2016/11/11 NodeJs
小程序获取周围IBeacon设备的方法
2018/10/31 Javascript
浅谈 Webpack 如何处理图片(开发、打包、优化)
2019/05/15 Javascript
小程序采集录音并上传到后台
2019/11/22 Javascript
uniapp 仿微信的右边下拉选择弹出框的实现代码
2020/07/12 Javascript
JavaScript编写开发动态时钟
2020/07/29 Javascript
vue 通过 Prop 向子组件传递数据的实现方法
2020/10/30 Javascript
[01:13:59]LGD vs Mineski Supermajor 胜者组 BO3 第三场 6.5
2018/06/06 DOTA
Python生成MD5值的两种方法实例分析
2019/04/26 Python
Python整数对象实现原理详解
2019/07/01 Python
Python 基于wxpy库实现微信添加好友功能(简洁)
2019/11/29 Python
python里反向传播算法详解
2020/11/22 Python
css3实现椭圆轨迹旋转的示例代码
2018/10/29 HTML / CSS
自动化专业个人求职信范文
2013/12/30 职场文书
犯错检讨书
2014/02/21 职场文书
高中班主任评语大全
2014/04/25 职场文书
无传销社区工作方案
2014/05/13 职场文书
企业理念标语
2014/06/09 职场文书
小学生感恩父母演讲稿
2014/08/28 职场文书
工程安全生产协议书
2014/11/21 职场文书
2015年人力资源工作总结
2015/04/08 职场文书
行政答辩状范文
2015/05/21 职场文书
2016年优秀党务工作者先进事迹材料
2016/02/29 职场文书