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 相关文章推荐
使用Microsoft Ajax Minifier减小JavaScript文件大小的方法
Apr 01 Javascript
关于jquery ajax 调用带参数的webservice返回XML数据一个小细节
Jul 31 Javascript
Windows8下搭建Node.js开发环境教程
Sep 03 Javascript
JS实现光滑展开合拢的菜单效果代码
Sep 16 Javascript
超漂亮的Bootstrap 富文本编辑器summernote
Apr 05 Javascript
jQuery查找节点并获取节点属性的方法
Sep 09 Javascript
JavaScript用构造函数如何获取变量的类型名
Dec 23 Javascript
js实现旋转木马效果
Mar 17 Javascript
通过学习bootstrop导航条学会修改bootstrop颜色基调
Jun 11 Javascript
Vue和React组件之间的传值方式详解
Jan 31 Javascript
在Vue中使用Select选择器拼接label的操作
Oct 22 Javascript
如何使用 vue-cli 创建模板项目
Nov 19 Vue.js
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
PHP新手上路(八)
2006/10/09 PHP
php错误级别的设置方法
2013/06/17 PHP
Yii框架登录流程分析
2014/12/03 PHP
详解php魔术方法(Magic methods)的使用方法
2016/02/14 PHP
Smarty模板简单配置与使用方法示例
2016/05/23 PHP
php-fpm服务启动脚本的方法
2018/04/27 PHP
针对thinkPHP5框架存储过程bug重写的存储过程扩展类完整实例
2018/06/16 PHP
javascript读取xml
2006/11/04 Javascript
JavaScript实现动态增加文件域表单
2009/02/12 Javascript
Extjs根据条件设置表格某行背景色示例
2014/07/23 Javascript
jQuery拖拽插件gridster使用指南
2015/04/21 Javascript
使用postMesssage()实现跨域iframe页面间的信息传递方法
2016/03/29 Javascript
jQuery实现左侧导航模块的显示与隐藏效果
2016/07/04 Javascript
Javascript表单特效之十大常用原理性样例代码大总结
2016/07/12 Javascript
JS实现放大、缩小及拖拽图片的方法【可兼容IE、火狐】
2016/08/23 Javascript
Angular动态添加、删除输入框并计算值实例代码
2017/03/29 Javascript
javascript中的面向对象
2017/03/30 Javascript
深究AngularJS中ng-drag、ng-drop的用法
2017/06/12 Javascript
基于ES6 Array.of的用法(实例讲解)
2017/09/05 Javascript
vue proxyTable 接口跨域请求调试的示例
2017/09/12 Javascript
vue-router 权限控制的示例代码
2017/09/21 Javascript
微信小程序登录session的使用
2019/03/17 Javascript
javascript实现商品图片放大镜
2019/11/28 Javascript
np.newaxis 实现为 numpy.ndarray(多维数组)增加一个轴
2019/11/30 Python
python时间与Unix时间戳相互转换方法详解
2020/02/13 Python
Python使用urllib模块对URL网址中的中文编码与解码实例详解
2020/02/18 Python
python selenium自动化测试框架搭建的方法步骤
2020/06/14 Python
如何在django中运行scrapy框架
2020/04/22 Python
Tiqets荷兰:出售欧洲最美丽的景点和博物馆门票
2018/01/09 全球购物
阿尔卡特(中国)的面试题目
2014/08/20 面试题
超市业务员岗位职责
2013/12/05 职场文书
社会治安综合治理责任书
2015/01/29 职场文书
给女朋友的道歉短信
2015/05/12 职场文书
毕业设计致谢语
2015/05/14 职场文书
nginx配置之并发频次限制
2022/04/18 Servers
Python如何让字典保持有序排列
2022/04/29 Python