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 相关文章推荐
[Web]防止用户复制页面内容和另存页面的方法
Feb 06 Javascript
Firefox div高度自适应
Apr 28 Javascript
JavaScript中用于四舍五入的Math.round()方法讲解
Jun 15 Javascript
js实现select下拉框菜单
Dec 08 Javascript
prototype框架中美元符号$用法分析
Jan 22 Javascript
jQuery实现图片轮播效果代码(基于jquery.pack.js插件)
Jun 02 Javascript
jQuery实现边框动态效果的实例代码
Sep 23 Javascript
jquery编写日期选择器
Mar 16 Javascript
node.js学习之事件模块Events的使用示例
Sep 28 Javascript
解决vue组件中使用v-for出现告警问题及v for指令介绍
Nov 11 Javascript
微信小程序实现动态设置placeholder提示文字及按钮选中/取消状态的方法
Dec 14 Javascript
Vue实现浏览器打印功能的代码
Apr 17 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
星际中的相关伤害
2020/03/04 星际争霸
E路文章系统PHP
2006/12/11 PHP
PH P5.2至5.5、5.6的新增功能详解
2014/07/14 PHP
PHP对文件进行加锁、解锁实例
2015/01/23 PHP
老生常谈PHP数组函数array_merge(必看篇)
2017/05/25 PHP
PHP验证码无法显示的原因及解决办法
2017/08/11 PHP
因str_replace导致的注入问题总结
2019/08/08 PHP
一端时间轮换的广告
2006/06/26 Javascript
基于JQuery实现的类似购物商城的购物车
2011/12/06 Javascript
js实现收缩菜单效果实例代码
2013/10/30 Javascript
jquery获取当前日期的方法
2015/01/14 Javascript
PHP实现的各种中文编码转换类分享
2015/01/23 Javascript
在JavaScript中操作时间之getYear()方法的使用教程
2015/06/11 Javascript
javascript实现图片上传前台页面
2015/08/18 Javascript
jQuery+formdata实现上传进度特效遇到的问题
2016/02/24 Javascript
js事件处理程序跨浏览器解决方案
2016/03/27 Javascript
angularjs封装bootstrap时间插件datetimepicker
2016/06/20 Javascript
让div运动起来 js实现缓动效果
2017/07/06 Javascript
nodejs密码加密中生成随机数的实例代码
2017/07/17 NodeJs
详解vue-cli 接口代理配置
2017/12/13 Javascript
使用vue中的v-for遍历二维数组的方法
2018/03/07 Javascript
在Vue项目中引入腾讯验证码服务的教程
2018/04/03 Javascript
node中modules.exports与exports导出的区别
2018/06/08 Javascript
axios封装,使用拦截器统一处理接口,超详细的教程(推荐)
2019/05/02 Javascript
vue + typescript + video.js实现 流媒体播放 视频监控功能
2019/07/07 Javascript
python编写暴力破解FTP密码小工具
2014/11/19 Python
Python中用memcached来减少数据库查询次数的教程
2015/04/07 Python
Python编程实现线性回归和批量梯度下降法代码实例
2018/01/04 Python
python+opencv实现车道线检测
2021/02/19 Python
html5 svg 中元素点击事件添加方法
2013/01/16 HTML / CSS
大学优秀班集体申报材料
2014/05/23 职场文书
敬老院标语
2014/06/27 职场文书
党的群众路线教育实践活动学习笔记范文
2014/11/06 职场文书
小学班主任培训心得体会
2016/01/07 职场文书
springboot应用服务启动事件的监听实现
2022/04/06 Java/Android
Qt数据库应用之实现图片转pdf
2022/06/01 Java/Android