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 相关文章推荐
JQuyer $.post 与 $.ajax 访问WCF ajax service 时的问题需要注意的地方
Sep 20 Javascript
javascript中var的重要性分析
Feb 11 Javascript
详解AngularJS 模态对话框
Apr 07 Javascript
微信小程序 Template详解及简单实例
Jan 05 Javascript
JS获得多个同name 的input输入框的值的实现方法
Jan 09 Javascript
js图片放大镜效果实现方法详解
Oct 28 Javascript
jQuery Tree Multiselect使用详解
May 02 jQuery
浅析vue中常见循环遍历指令的使用 v-for
Apr 18 Javascript
微信小程序时间选择插件使用详解
Dec 28 Javascript
js图片无缝滚动插件使用详解
May 26 Javascript
解决layui表格的表头不滚动的问题
Sep 04 Javascript
Jquery cookie插件实现原理代码解析
Aug 04 jQuery
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随机生成福彩双色球号码的2种方法
2013/02/04 PHP
使用PHP生成二维码的方法汇总
2015/07/22 PHP
php7函数,声明,返回值等新特性介绍
2018/05/25 PHP
JavaScript在IE中“意外地调用了方法或属性访问”
2008/11/19 Javascript
推荐30个新鲜出炉的精美 jQuery 效果
2012/03/26 Javascript
Jquery ajaxStart()与ajaxStop()方法(实例讲解)
2013/12/18 Javascript
jQuery操作select下拉框的text值和value值的方法
2014/05/31 Javascript
jQuery实现输入框下拉列表树插件特效代码分享
2015/08/27 Javascript
谈谈JavaScript自定义回调函数
2015/10/18 Javascript
通过Jquery.cookie.js实现展示浏览网页的历史记录超管用
2015/10/23 Javascript
一次$.getJSON不执行的简单记录
2016/07/19 Javascript
浅谈移动端之js touch事件 手势滑动事件
2016/11/07 Javascript
Angular.js中定时器循环的3种方法总结
2017/04/27 Javascript
深入理解Vue2.x的虚拟DOM diff原理
2017/09/27 Javascript
详解vue+css3做交互特效的方法
2017/11/20 Javascript
nodejs实现大文件(在线视频)的读取
2020/10/16 NodeJs
vue-cli脚手架-bulid下的配置文件
2018/03/27 Javascript
一步一步的了解webpack4的splitChunk插件(小结)
2018/09/17 Javascript
webpack中如何使用雪碧图的示例代码
2018/11/11 Javascript
javascript简单实现深浅拷贝过程详解
2019/10/08 Javascript
记录微信小程序 height: calc(xx - xx);无效问题
2019/12/30 Javascript
Python列表append和+的区别浅析
2015/02/02 Python
Python简单实现安全开关文件的两种方式
2016/09/19 Python
python 基本数据类型占用内存空间大小的实例
2018/06/12 Python
使用pandas read_table读取csv文件的方法
2018/07/04 Python
pytorch多进程加速及代码优化方法
2019/08/19 Python
Python numpy线性代数用法实例解析
2019/11/15 Python
Python 3 使用Pillow生成漂亮的分形树图片
2019/12/24 Python
利用HTML5实现使用按钮控制背景音乐开关
2015/09/21 HTML / CSS
Oroton中国官网:澳洲知名奢侈配饰品牌
2017/03/26 全球购物
Travelstart沙特阿拉伯:廉价航班、豪华酒店和实惠的汽车租赁优惠
2019/04/06 全球购物
领先的荷兰线上超市:荷兰之家Holland at Home(支持中文)
2021/01/21 全球购物
创建文明城市标语
2014/06/16 职场文书
如何写股份合作协议书
2014/09/11 职场文书
《日月潭》教学反思
2016/02/20 职场文书
Python可视化学习之seaborn绘制矩阵图详解
2022/02/24 Python