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 相关文章推荐
用js实现的一个Flash滚动轮换显示图片代码生成器
Mar 14 Javascript
技术男用来对妹子表白的百度首页
Jul 23 Javascript
jQuery中Form相关知识汇总
Jan 06 Javascript
JS+CSS实现淡入式焦点图片幻灯切换效果的方法
Feb 26 Javascript
一道面试题引发的对javascript类型转换的思考
Mar 06 Javascript
详解node如何让一个端口同时支持https与http
Jul 04 Javascript
Easyui和zTree两种方式分别实现树形下拉框
Aug 04 Javascript
基于百度地图api清除指定覆盖物(Overlay)的方法
Jan 26 Javascript
vue history 模式打包部署在域名的二级目录的配置指南
Jul 02 Javascript
实例分析JS中的相等性判断===、 ==和Object.is()
Nov 17 Javascript
jQuery实现简易QQ聊天框
Feb 10 jQuery
openlayers实现图标拖动获取坐标
Sep 25 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 编程请选择正确的文本编辑软件
2006/12/21 PHP
生成卡号php代码
2008/04/09 PHP
AMFPHP php远程调用(RPC, Remote Procedure Call)工具 快速入门教程
2010/05/10 PHP
php实现简易计算器
2020/08/28 PHP
BootStrap table表格插件自适应固定表头(超好用)
2016/08/24 Javascript
jQuery实现花式轮播之圣诞节礼物传送效果
2016/12/25 Javascript
NodeJS链接MySql数据库的操作方法
2017/06/27 NodeJs
ES6新增的math,Number方法
2017/08/06 Javascript
使用webpack打包koa2 框架app
2018/02/02 Javascript
使vue实现jQuery调用的两种方法
2019/05/12 jQuery
JS array数组检测方式解析
2020/05/19 Javascript
python命令行参数sys.argv使用示例
2014/01/28 Python
Python守护进程(daemon)代码实例
2015/03/06 Python
解析Python中的二进制位运算符
2015/05/13 Python
简单了解Django模板的使用
2017/12/20 Python
python基础教程项目二之画幅好画
2018/04/02 Python
python中的turtle库函数简单使用教程
2018/07/23 Python
Python正则表达式匹配数字和小数的方法
2019/07/03 Python
python实现beta分布概率密度函数的方法
2019/07/08 Python
Django的models中on_delete参数详解
2019/07/16 Python
python实现的生成word文档功能示例
2019/08/23 Python
Python request使用方法及问题总结
2020/04/26 Python
python通过函数名调用函数的几种场景
2020/09/23 Python
德国滑雪和户外用品网上商店:XSPO
2019/10/30 全球购物
岗位职责定义及内容
2013/11/08 职场文书
物流管理专业应届生求职信
2013/11/21 职场文书
写自荐信要注意什么
2013/12/26 职场文书
出国留学经济担保书
2014/04/01 职场文书
企业承诺书怎么写
2014/05/24 职场文书
依法行政工作汇报
2014/10/28 职场文书
销售辞职信范文
2015/03/02 职场文书
2015年保卫科工作总结
2015/05/14 职场文书
2015年干部教育培训工作总结
2015/05/15 职场文书
人民的好儿女观后感
2015/06/18 职场文书
2019经典广告词集锦!
2019/07/02 职场文书
Javascript 解构赋值详情
2021/11/17 Javascript