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 相关文章推荐
jquery tools 系列 scrollable(2)
Sep 06 Javascript
jQuery中ajax的使用与缓存问题的解决方法
Dec 19 Javascript
常用的JQuery函数及功能小结
Mar 24 Javascript
全国省市二级联动下拉菜单 js版
May 10 Javascript
AngularJS  $modal弹出框实例代码
Aug 24 Javascript
详解js中call与apply关键字的作用
Nov 21 Javascript
JavaScript实现按键精灵的原理分析
Feb 21 Javascript
从零开始学习Node.js系列教程之设置HTTP头的方法示例
Apr 13 Javascript
JS常用正则表达式总结【经典】
May 12 Javascript
JS控制鼠标拒绝点击某一按钮的实例
Dec 29 Javascript
Vue函数式组件的应用实例详解
Aug 30 Javascript
vue Treeselect下拉树只能选择第N级元素实现代码
Aug 31 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
YB217、YB235、YB400浅听
2021/03/02 无线电
php设计模式之职责链模式实例分析【星际争霸游戏案例】
2020/03/27 PHP
PHP实现倒计时功能
2020/11/16 PHP
[原创]图片分页查看
2006/08/28 Javascript
JavaScript 事件冒泡简介及应用
2010/01/11 Javascript
基于jquery的图片轮播 tab切换组件
2012/07/19 Javascript
禁止选中文字兼容IE、Chrome、FF等
2013/09/04 Javascript
jQuery通过控制节点实现仅在前台通过get方法完成参数传递
2015/02/02 Javascript
JQuery中DOM事件合成用法实例分析
2015/06/13 Javascript
用JavaScript来美化HTML的select标签的下拉列表效果
2015/11/17 Javascript
js与applet相互调用的方法
2016/06/22 Javascript
jQuery元素选择器实例代码
2017/02/06 Javascript
bootstrap轮播图示例代码分享
2017/05/17 Javascript
详解vue过滤器在v2.0版本用法
2017/06/01 Javascript
JS实现遍历不规则多维数组的方法
2018/03/21 Javascript
详解webpack模块加载器兼打包工具
2018/09/11 Javascript
JavaScript 中 JSON.parse 函数 和 JSON.stringify 函数
2018/12/05 Javascript
微信小程序textarea层级过高(盖住其他元素)问题的解决办法
2019/03/04 Javascript
Vue CLI3中使用compass normalize的方法
2019/05/30 Javascript
用JS实现一个简单的打砖块游戏
2019/12/11 Javascript
Python实现爬取逐浪小说的方法
2015/07/07 Python
python僵尸进程产生的原因
2017/07/21 Python
python-opencv在有噪音的情况下提取图像的轮廓实例
2017/08/30 Python
深入浅析Python的类
2018/06/22 Python
浅谈pandas用groupby后对层级索引levels的处理方法
2018/11/06 Python
Python常见数据结构之栈与队列用法示例
2019/01/14 Python
tensorflow查看ckpt各节点名称实例
2020/01/21 Python
python实现梯度下降算法的实例详解
2020/08/17 Python
美国女性卫生用品公司:Thinx
2017/06/30 全球购物
Champs Sports加拿大:北美最大的以商场为基础的专业运动鞋和服装零售商之一
2018/05/01 全球购物
英国家庭、花园、汽车和移动解决方案:Easylife Group
2018/05/23 全球购物
党员干部四风问题整改措施思想汇报
2014/10/12 职场文书
2014年个人技术工作总结
2014/12/08 职场文书
工作年限证明范本
2015/06/15 职场文书
教师见习总结范文
2015/06/23 职场文书
MySQL中dd::columns表结构转table过程及应用详解
2022/09/23 MySQL