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学习笔记6 prototype的提出
Jan 11 Javascript
基于jquery的表头固定的若干方法
Jan 27 Javascript
jQuery中:not选择器用法实例
Dec 30 Javascript
jquery 构造函数在表单提交过程中修改数据
May 25 Javascript
jquery分割字符串的方法
Jun 24 Javascript
JavaScript学习笔记之检测客户端类型是(引擎、浏览器、平台、操作系统、移动设备)
Dec 03 Javascript
浅谈jQuery为哪般去掉了浏览器检测
Aug 29 Javascript
微信小程序 教程之事件
Oct 18 Javascript
[原创]JavaScript语法高亮插件highlight.js用法详解【附highlight.js本站下载】
Nov 01 Javascript
详解如何实现一个简单的 vuex
Feb 10 Javascript
jQuery实现动画、消失、显现、渐出、渐入效果示例
Sep 06 jQuery
Nuxt使用Vuex的方法示例
Sep 06 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/10/09 PHP
PHP使用CURL实现对带有验证码的网站进行模拟登录的方法
2014/07/23 PHP
php数组转成json格式的方法
2015/03/09 PHP
CodeIgniter视图使用注意事项
2016/01/20 PHP
php实现的数组转xml案例分析
2019/09/28 PHP
在 Laravel 6 中缓存数据库查询结果的方法
2019/12/11 PHP
js 加载时自动调整图片大小
2008/05/28 Javascript
自己动手制作jquery插件之自动添加删除行的实现
2011/10/13 Javascript
js中更短的 Array 类型转换
2011/10/30 Javascript
鼠标焦点离开文本框时验证的js代码
2013/07/19 Javascript
原生JavaScript+LESS实现瀑布流
2014/12/12 Javascript
jquery获取及设置outerhtml的方法
2015/03/09 Javascript
微信小程序使用第三方库Underscore.js步骤详解
2016/09/27 Javascript
基于JavaScript实现拖动滑块效果
2017/02/16 Javascript
JS中cookie的使用及缺点讲解
2017/05/13 Javascript
Vue.js基础指令实例讲解(各种数据绑定、表单渲染大总结)
2017/07/03 Javascript
angularjs数组判断是否含有某个元素的实例
2018/02/27 Javascript
jQuery实现文字超过1行、2行或规定的行数时自动加省略号的方法
2018/03/28 jQuery
jQuery实现轮播图源码
2019/10/23 jQuery
[01:53]2016完美“圣”典风云人物:Maybe专访
2016/12/05 DOTA
[04:00]黄浦江畔,再会英雄——完美世界DOTA2 TI9应援视频
2019/07/31 DOTA
python cx_Oracle的基础使用方法(连接和增删改查)
2017/11/19 Python
有趣的python小程序分享
2017/12/05 Python
TensorFlow saver指定变量的存取
2018/03/10 Python
Python线程下使用锁的技巧分享
2018/09/13 Python
python实现比较类的两个instance(对象)是否相等的方法分析
2019/06/26 Python
Python是怎样处理json模块的
2020/07/16 Python
澳大利亚潮流尖端的快时尚品牌:Cotton On
2016/09/26 全球购物
Columbia美国官网:美国著名的户外服装品牌
2016/11/24 全球购物
英国健身仓库:Bodybuilding Warehouse
2019/03/06 全球购物
旅游个人求职信范文
2014/01/30 职场文书
大一新生学期自我评价
2014/04/09 职场文书
班长演讲稿范文
2014/04/24 职场文书
西安导游词
2015/02/12 职场文书
入党后的感想
2015/08/10 职场文书
mybatis3中@SelectProvider传递参数方式
2021/08/04 Java/Android