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 相关文章推荐
jQuery 锚点跳转滚动条平滑滚动一句话代码
Apr 30 Javascript
设为首页加入收藏兼容360/火狐/谷歌/IE等主流浏览器的代码
Mar 26 Javascript
JavaScript中的字符串操作详解
Nov 12 Javascript
js 针对html DOM元素操作等经验累积
Mar 11 Javascript
AngularJS 模块详解及简单实例
Jul 28 Javascript
浅谈jquery中next与siblings的区别
Oct 27 Javascript
jQuery使用正则验证15/18身份证的方法示例
Apr 27 jQuery
详解vue-cli中的ESlint配置文件eslintrc.js
Sep 25 Javascript
详解http访问解析流程原理
Oct 18 Javascript
vue计算属性和监听器实例解析
May 10 Javascript
vue改变对象或数组时的刷新机制的方法总结
Apr 24 Javascript
微信小程序实现watch监听
Jun 04 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查询数据库中满足条件的记录条数(两种实现方法)
2013/01/29 PHP
php解压文件代码实现php在线解压
2014/02/13 PHP
php实现两表合并成新表并且有序排列的方法
2014/12/05 PHP
php上传图片生成缩略图(GD库)
2016/01/06 PHP
PHP实现的限制IP投票程序IP来源分析
2016/05/04 PHP
php apache开启跨域模式过程详解
2019/07/08 PHP
JavaScript实现拼音排序的方法
2012/11/20 Javascript
js控制input框只读实现示例
2014/01/20 Javascript
分享十五款 jQuery 社交网络分享插件
2015/05/16 Javascript
Nginx上传文件全部缓存解决方案
2015/08/17 Javascript
javascript实现的上下无缝滚动效果
2016/09/19 Javascript
Bootstrap Table使用方法解析
2016/10/19 Javascript
如何使用Bootstrap创建表单
2017/03/29 Javascript
分享ES6的7个实用技巧
2018/01/18 Javascript
vue debug 二种方法
2018/09/16 Javascript
JS中使用cavas截图网页并解决跨域及模糊问题
2018/11/13 Javascript
Koa 中的错误处理解析
2019/04/09 Javascript
ES2020 已定稿,真实场景案例分析
2020/05/25 Javascript
vue 全局封装loading加载教程(全局监听)
2020/11/05 Javascript
详解Vue.js3.0 组件是如何渲染为DOM的
2020/11/10 Javascript
Python使用matplotlib实现基础绘图功能示例
2018/07/03 Python
用Pycharm实现鼠标滚轮控制字体大小的方法
2019/01/15 Python
如何在VSCode上轻松舒适的配置Python的方法步骤
2019/10/28 Python
Python多线程threading join和守护线程setDeamon原理详解
2020/03/18 Python
Python定时任务APScheduler原理及实例解析
2020/05/30 Python
HTML5的文档结构和新增标签完全解析
2017/04/21 HTML / CSS
法国房车租赁网站:Yescapa
2019/08/26 全球购物
Omio英国:搜索并比较便宜的巴士、火车和飞机
2019/08/27 全球购物
客服文员岗位职责
2013/11/29 职场文书
运动会开幕式解说词
2014/02/05 职场文书
计划生育证明格式范本
2014/09/12 职场文书
导游词之峨眉山
2019/12/16 职场文书
python获取对象信息的实例详解
2021/07/07 Python
Python标准库pathlib操作目录和文件
2021/11/20 Python
Apache Linkis 中间件架构及快速安装步骤
2022/03/16 Servers
ipad隐藏软件app图标方法
2022/04/19 数码科技