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 相关文章推荐
toString()一个会自动调用的方法
Feb 08 Javascript
javascript 面向对象的JavaScript类
May 04 Javascript
jquery与prototype框架的详细对比
Nov 21 Javascript
JS函数定义方式的区别介绍
Mar 22 Javascript
jQuery ajax中使用confirm,确认是否删除的简单实例
Jun 17 Javascript
AngularJS过滤器filter用法实例分析
Nov 04 Javascript
JavaScript实现精美个性导航栏筋斗云效果
Oct 29 Javascript
浅谈vue2 单页面如何设置网页title
Nov 08 Javascript
node puppeteer(headless chrome)实现网站登录
May 09 Javascript
JS动画实现回调地狱promise的实例代码详解
Nov 08 Javascript
vue实现路由懒加载的3种方法示例
Sep 01 Javascript
JavaScript parseInt0.0000005打印5原理解析
Jul 23 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 array数组的教程详解
2013/06/05 PHP
PHP中error_log()函数的使用方法
2015/01/20 PHP
php检查函数必传参数是否存在的实例详解
2017/08/28 PHP
鼠标图片振动代码
2006/07/06 Javascript
使用JavaScript switch case 另类写法
2010/03/14 Javascript
轻松创建nodejs服务器(10):处理POST请求
2014/12/18 NodeJs
javascript仿京东导航左侧分类导航下拉菜单效果
2020/11/25 Javascript
Javascript之面向对象--封装
2016/12/02 Javascript
jquery实时获取时间的简单实例
2017/01/26 Javascript
jquery mobile实现可折叠的导航按钮
2017/03/11 Javascript
微信小程序开发之map地图实现教程
2017/06/08 Javascript
图片懒加载imgLazyLoading.js使用详解
2020/09/15 Javascript
js实现控制文件拖拽并获取拖拽内容功能
2018/02/17 Javascript
详解Vue Elememt-UI构建管理后台
2018/02/27 Javascript
mpvue开发音频类小程序踩坑和建议详解
2019/03/12 Javascript
利用JS如何获取form表单数据
2019/12/19 Javascript
vue监听dom大小改变案例
2020/07/29 Javascript
通过实例了解Render Props回调地狱解决方案
2020/11/04 Javascript
Nodejs 数组的队列以及forEach的应用详解
2021/02/25 NodeJs
python文件写入实例分析
2015/04/08 Python
Django的信号机制详解
2017/05/05 Python
python操作xlsx文件的包openpyxl实例
2018/05/03 Python
对pycharm 修改程序运行所需内存详解
2018/12/03 Python
Python魔法方法 容器部方法详解
2020/01/02 Python
django实现将后台model对象转换成json对象并传递给前端jquery
2020/03/16 Python
解决django接口无法通过ip进行访问的问题
2020/03/27 Python
重构Python代码的六个实例
2020/11/25 Python
Linux系统下升级pip的完整步骤
2021/01/31 Python
html5 input元素新特性_动力节点Java学院整理
2017/07/06 HTML / CSS
编写strcpy函数
2014/06/24 面试题
态度决定一切演讲稿
2014/05/20 职场文书
军训拉歌口号
2014/06/13 职场文书
幼儿教师自我剖析材料
2014/09/29 职场文书
出国留学导师推荐信
2015/03/26 职场文书
教您怎么制定西餐厅运营方案 ?
2019/07/05 职场文书
Android移动应用开发指南之六种布局详解
2022/09/23 Java/Android