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 相关文章推荐
JavaScript基本对象
Jan 11 Javascript
Javascript 类型转换方法
Oct 24 Javascript
jQuery实现的Email中的收件人效果(按del键删除)
Mar 20 Javascript
jQuery AJAX实现调用页面后台方法和web服务定义的方法分享
Mar 01 Javascript
PageSwitch插件实现100种不同图片切换效果
Jul 28 Javascript
jQuery实现带有上下控制按钮的简单多行滚屏效果代码
Sep 04 Javascript
xmlplus组件设计系列之按钮(2)
Apr 26 Javascript
vue实现的组件兄弟间通信功能示例
Dec 04 Javascript
小程序hover-class点击态效果实现
Feb 26 Javascript
js prototype深入理解及应用实例分析
Nov 25 Javascript
JavaScript本地储存:localStorage、sessionStorage、cookie的使用
Oct 13 Javascript
vue 在服务器端直接修改请求的接口地址
Dec 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
德生BCL3000的电路分析和打磨
2021/03/02 无线电
php实现mysql数据库备份类
2008/03/20 PHP
php提示Call-time pass-by-reference has been deprecated in的解决方法[已测]
2012/05/06 PHP
解析php中heredoc的使用方法
2013/06/17 PHP
PHP has encountered a Stack overflow问题解决方法
2014/11/03 PHP
PHP实现适用于自定义的验证码类
2016/06/15 PHP
Yii2中DropDownList简单用法示例
2016/07/18 PHP
location.search在客户端获取Url参数的方法
2010/06/08 Javascript
javascript客户端解决方案 缓存提供程序
2010/07/14 Javascript
关于锚点跳转及jQuery下相关操作与插件
2012/10/01 Javascript
jQuery动画出现连续触发、滞后反复执行的解决方法
2015/01/28 Javascript
jfreechart插件将数据展示成饼状图、柱状图和折线图
2015/04/13 Javascript
jQuery实现大转盘抽奖活动仿QQ音乐代码分享
2015/08/21 Javascript
Angular2中Bootstrap界面库ng-bootstrap详解
2016/10/18 Javascript
jQuery下拉菜单的实现代码
2016/11/03 Javascript
VueJs监听window.resize方法示例
2018/01/17 Javascript
编写React组件项目实践分析
2018/03/04 Javascript
基于webpack4搭建的react项目框架的方法
2018/06/30 Javascript
vue中利用Promise封装jsonp并调取数据
2019/06/18 Javascript
Python中的进程分支fork和exec详解
2015/04/11 Python
flask中过滤器的使用详解
2018/08/01 Python
python中aioysql(异步操作MySQL)的方法
2019/04/11 Python
使用Python创建简单的HTTP服务器的方法步骤
2019/04/26 Python
对python 中re.sub,replace(),strip()的区别详解
2019/07/22 Python
python实现12306登录并保存cookie的方法示例
2019/12/17 Python
基于keras中的回调函数用法说明
2020/06/17 Python
CSS3使用多列制作瀑布流
2016/05/10 HTML / CSS
canvas三角函数模拟水波效果的示例代码
2018/07/03 HTML / CSS
HTML5添加禁止缩放功能
2017/11/03 HTML / CSS
override和overload的区别
2016/03/09 面试题
物业管理求职自荐信
2013/09/25 职场文书
法学函授自我鉴定
2014/02/06 职场文书
班级学习计划书
2014/04/27 职场文书
2015学校年度工作总结
2015/05/11 职场文书
2015年车间管理工作总结
2015/07/23 职场文书
2016年大学生就业指导课心得体会
2015/10/09 职场文书