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代码
Dec 01 Javascript
javascript URL锚点取值方法
Feb 25 Javascript
十分钟打造AutoComplete自动完成效果代码
Dec 26 Javascript
再谈Jquery Ajax方法传递到action(补充)
May 12 Javascript
js实现回放拖拽轨迹从过程上进行分析
Jun 26 Javascript
JavaScript中反正弦函数Math.asin()的使用简介
Jun 14 Javascript
纯javascript实现自动发送邮件
Oct 21 Javascript
详解VueJs异步动态加载块
Mar 09 Javascript
vue 1.x 交互实现仿百度下拉列表示例
Oct 21 Javascript
深入理解Vue Computed计算属性原理
May 29 Javascript
我所理解的JavaScript中的this指向
Sep 04 Javascript
微信小程序实现登录注册功能
Dec 29 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
PHP读写文件的方法(生成HTML)
2006/11/27 PHP
基于PHP导出Excel的小经验 完美解决乱码问题
2013/06/10 PHP
分享PHP header函数使用教程
2013/09/05 PHP
destoon实现调用当前栏目分类及子分类和三级分类的方法
2014/08/21 PHP
php中Ctype函数用法详解
2014/12/09 PHP
PHP开发框架laravel安装与配置教程
2015/03/13 PHP
PHP PDOStatement::getColumnMeta讲解
2019/02/01 PHP
ext实现完整的登录代码
2008/08/08 Javascript
自用js开发框架小成 学习js的朋友可以看看
2010/11/16 Javascript
IE6 hack for js 集锦
2014/09/23 Javascript
JS判断浏览器是否支持某一个CSS3属性的方法
2014/10/17 Javascript
JavaScript检测上传文件大小的方法
2015/07/22 Javascript
java必学必会之static关键字
2015/12/03 Javascript
jquery实现图片上传前本地预览
2017/04/28 jQuery
Angular 2父子组件数据传递之局部变量获取子组件其他成员
2017/07/04 Javascript
微信小程序实现顶部普通选项卡效果(非swiper)
2020/06/19 Javascript
VueJs使用Amaze ui调整列表和内容页面
2017/11/30 Javascript
Vue的土著指令和自定义指令实例详解
2018/02/04 Javascript
vue实现图片上传到后台
2020/06/29 Javascript
Python 自动补全(vim)
2014/11/30 Python
Python的randrange()方法使用教程
2015/05/15 Python
在CentOS上配置Nginx+Gunicorn+Python+Flask环境的教程
2016/06/07 Python
caffe binaryproto 与 npy相互转换的实例讲解
2018/07/09 Python
Python中函数的基本定义与调用及内置函数详解
2019/05/13 Python
在pyqt5中QLineEdit里面的内容回车发送的实例
2019/06/21 Python
python内存动态分配过程详解
2019/07/15 Python
Python3之字节串bytes与字节数组bytearray的使用详解
2019/08/27 Python
python selenium操作cookie的实现
2020/03/18 Python
Python自定义聚合函数merge与transform区别详解
2020/05/26 Python
TensorFlow-gpu和opencv安装详细教程
2020/06/30 Python
css3 实现元素弧线运动的示例代码
2020/04/24 HTML / CSS
CSS3选择器新增问题的实现
2021/01/21 HTML / CSS
人事助理岗位职责
2013/11/18 职场文书
投标邀请书范文
2014/01/31 职场文书
有关骆驼祥子的读书笔记
2015/06/26 职场文书
讲解Python实例练习逆序输出字符串
2022/05/06 Python