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.The.Good.Parts阅读笔记(二)作用域&amp;闭包&amp;减缓全局空间污染
Nov 16 Javascript
js中单引号与双引号冲突问题解决方法
Oct 04 Javascript
javascript结合ajax读取txt文件内容
Dec 05 Javascript
针对BootStrap中tabs控件的美化和完善(推荐)
Jul 06 Javascript
AngularJs directive详解及示例代码
Sep 01 Javascript
用jQuery旋转插件jqueryrotate制作转盘抽奖
Feb 10 Javascript
Angular.js通过自定义指令directive实现滑块滑动效果
Oct 13 Javascript
Angular2仿照微信UI实现9张图片上传和预览的示例代码
Oct 19 Javascript
vue弹窗组件使用方法
Apr 28 Javascript
使用webpack构建应用的方法步骤
Mar 04 Javascript
jQuery实现异步上传一个或多个文件
Aug 17 jQuery
解决Antd Table表头加Icon和气泡提示的坑
Nov 17 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的FTP学习(四)
2006/10/09 PHP
Godaddy空间Zend Optimizer升级方法
2010/05/10 PHP
PHP生成指定长度随机数最简洁的方法
2014/07/14 PHP
PHP Ajax JavaScript Json获取天气信息实现代码
2016/08/17 PHP
ThinkPHP整合datatables实现服务端分页的示例代码
2018/02/10 PHP
Javascript写了一个清除“logo1_.exe”的杀毒工具(可扫描目录)
2007/02/09 Javascript
javascript判断单选框或复选框是否选中方法集锦
2007/04/04 Javascript
javascript一个无懈可击的实例化XMLHttpRequest的方法
2010/10/13 Javascript
一起来写段JS drag拖动代码
2010/12/09 Javascript
瀑布流布局代码一例
2014/04/11 Javascript
node.js中使用socket.io的方法
2014/12/15 Javascript
JavaScript框架是什么?怎样才能叫做框架?
2015/07/01 Javascript
jQuery定义插件的方法
2015/12/18 Javascript
bootstrap实现弹窗和拖动效果
2016/01/03 Javascript
Angularjs中使用Filters详解
2016/03/11 Javascript
jquery注册文本框获取焦点清空,失去焦点赋值的简单实例
2016/09/08 Javascript
jQuery EasyUI Accordion可伸缩面板组件使用详解
2017/02/28 Javascript
js弹出窗口简单实现代码
2017/03/22 Javascript
vue-cli 组件的导入与使用教程详解
2018/04/11 Javascript
jQuery基于闭包实现的显示与隐藏div功能示例
2018/06/09 jQuery
vue中rem的配置的方法示例
2018/08/30 Javascript
vue在路由中验证token是否存在的简单实现
2019/11/11 Javascript
three.js利用gpu选取物体并计算交点位置的方法示例
2019/11/25 Javascript
[02:13] 完美世界DOTA2联赛PWL DAY5集锦
2020/11/03 DOTA
python实现简单日期工具类
2019/04/24 Python
python3.7简单的爬虫实例详解
2019/07/08 Python
使用pip安装python库的多种方式
2019/07/31 Python
Anaconda的安装与虚拟环境建立
2020/11/18 Python
CSS3 实现的缩略图悬停效果
2020/12/09 HTML / CSS
美国战术品牌:5.11 Tactical
2019/05/01 全球购物
可口可乐唇膏:Lip Smackers
2019/08/27 全球购物
煤矿机修工岗位职责
2014/02/07 职场文书
学生实习证明范文
2014/09/28 职场文书
高中开学感言
2015/08/01 职场文书
浅谈JS的二进制家族
2021/05/09 Javascript
前端使用svg图片改色实现示例
2022/07/23 HTML / CSS