🖥️
Log4think
  • Introduction
  • Archive
  • 2016-12-13 JavaScript 中几种不同的基于 prototype 继承方式的区别
  • 2014-09-02 Facebook的Dalvik运行期补丁
  • 2014-08-19 理解AnguarJS中的模板编译
  • 2014-08-13 在Android中使用OSGi框架(Knopflerfish)
  • 2014-08-13 在Android中使用OSGi框架(Apache Felix)
  • 2014-07-30 3rd-party apt-key list for Ubuntu
  • 2014-07-21 'Failed to clone a large git repository: The remote end hung up unexpectedly'
  • 2014-07-02 genymotion Qt error in Ubuntu
  • 2014-04-30 可自动安装依赖的Ubuntu离线包安装工具 gdebi
  • 2014-04-23 解决搜狗输入法Ubuntu 14.04下黑块状态条
  • 2014-04-08 Setup Ghost blog system on Ubuntu
  • 2014-03-28 Trace a process
  • 2014-03-25 Forecast::IO 599 Internal Exception
  • 2014-03-23 Mac Tips
  • 2014-01-17 LD_LIBRARY_PATH shouldn't contain the current directory
  • 2014-01-10 Python on vim
  • 2013-11-12 ibus-pinyin doesn't work in KUbuntu 13.10
  • 2013-11-11 phpMyAdmin login error to remote server
  • 2013-11-04 Get SNMP(v3) working on Ubuntu 12.04
  • 2013-10-30 Accessing Facebook by LWP
  • 2013-10-17 Log4perl多个Appender重复输出日志的问题解决办法
  • 2013-04-19 How to enable ORMLite internal log on Android
  • 2013-02-20 Bash Shortcuts
  • 2013-02-20 '"adb shell dumpsys" parameter list'
  • 2013-01-27 循环有序数组的二分查找
  • 2013-01-23 为Java运行环境导入根证书解决Eclipse的TFS插件的"PKIX path building failed"错误
  • 2013-01-21 ant 中通过重新定义 project.all.jars.path 在 classpath 中引入外部 jar 文件
  • 2012-09-22 Android的滚动条实现细节
  • 2012-02-05 hostname自动变成bogon的问题
  • 2011-07-09 代码段速记 gist.github.com
  • 2011-07-08 A perl Data.Dumper clone for Python
  • 2011-06-22 魔兽世界私服Trinity,从源码开始
  • 2011-06-13 魔兽世界3.3.5 13930登录数据包分析
  • 2011-06-13 魔兽世界 3.3.5 13930 Trinity 认证补丁
  • 2011-05-20 统一业务模型(UBM) in ERP5
  • 2011-03-08 制作ASCII字符动画
  • 2011-01-14 Ubuntu升级导致的udevd错误修复
  • 2011-01-09 行列有序矩阵求第K个数
  • 2011-01-09 字节按位逆序
  • 2011-01-01 编程之美 1.2 中国相帅问题的一个简洁解法
  • 2010-11-26 为Windows 7/Windows Server 2008添加IPX协议
  • 2010-11-12 How to debug with Android Logging
  • 2010-09-16 利用google-code-prettify做网页内源码的语法高亮
  • 2010-09-05 10 Ways We Hurt Our Romantic Relationships
  • 2010-08-30 利用ipkall+xlite+iptel.org开通google voice
  • 2010-08-27 避免Android开发中的ANR
  • 2010-08-27 在Eclipse中查看Android SDK的源代码
  • 2010-08-18 Git中判断一个commit是否在某个branch中
  • 2010-08-04 修正auto-excerpt产生带格式的摘要
  • 2010-03-31 Go 编程语言入门教程
  • 2010-03-13 利用外部VPS主机通过ssh隧道穿透防火墙连接内网
  • 2009-12-30 旋转矩阵
  • 2009-02-10 为什么cpio要比tar好
  • 2008-12-11 ThoughtWorks 的一道笔试题
  • 2008-11-18 长距离打车如何省钱?
  • 2007-02-08 ftp后台自动上传下载
  • 2006-06-12 vi cheatsheet
  • 2006-04-19 修正mysqlcc在MySQL 5.0上常报的 Table 'xxx' doesn't exist 错误
  • 2006-04-01 'Perl中不寻常的 ?: 运算符'
  • 2005-09-13 关于IoC、低耦合、配置文件及其本质意义的思考
  • 2005-09-13 Perl与数据库DBI快速入门
  • 2005-09-12 Perl无废话入门指南
  • 2005-07-16 Solaris 下安装Perl的DBD-mysql模块失败的原因以及解决办法
  • 2004-10-15 SharpDevelop的AddInTree View 插件
  • 2004-10-14 SharpDevelop源码分析 (完整版)
由 GitBook 提供支持
在本页
  • 反向控制(IoC)
  • 深入模板
  • Hello !
  • 编译模板
  • 总结

这有帮助吗?

2014-08-19 理解AnguarJS中的模板编译

在一开始学习AngularJS的过程中,模板的工作原理可能是最令人难解的问题之一。在其他的框架中,一般来说模板就是一个字符串,在这个字符串中使用一个特殊的表达方式可以嵌入一个传入的外部对象的数据,其本质上就是一个字符串的操作:输入字符串,输出字符串,然后通过.innerHTML()把所有搞出来的东西塞到DOM里面去。

很明显AngularJS的模板不是这样做的。它有双向数据绑定,可以完美的在视图中与最新的数据模型同步。它还会自动在ng-click上注册事件监听器,甚至可以将视图中输入控件中的数据同步回数据模型。太神奇了!

这个当然不是什么魔术,事实上将一个模板转换为一个带动态绑定和事件监听功能的动态视图,这中间的过程其实很简单。一旦搞明白了这个过程,对于AngularJS应用的理解也会上升一个台阶。

这里有一个模板

Name: # Hello {{yourName}}!

如你所见,这是个最简单的AngularJS的应用页面。

反向控制(IoC)

第一个需要了解的概念是"反向控制"(IoC)。AngularJS不需要手工启动应用,而是假定你会遵循一定的规则在代码中放一些基本的设定。

例如,AngularJS会假定HTML代码中有一个带有ng-app属性的元素,表示你的应用的"根"节点。它表示AngularJS可以接管这个节点以及之下所有子节点。这一点可以保证AngularJS可以和其他的JavaScript框架共存。当AngularJS启动应用的时候,它会遍历DOM内的节点,并查找这个属性,这个在AngularJS术语中叫"指令"(Directive)。注意这个ng-app指令可以放在任何DOM节点上,包括<html>和<body>标签。

AngularJS还会假定其他的框架不会接触到它的"根"节点下面的所有内容,否则可能会导致未知行为,或者打破双向数据绑定和事件监听器。

深入模板

<label>Name:</label>
<input type="text" ng-model="yourName" placeholder="Enter a name here">

Hello !

这一部分是模板。"根"节点下面的所有内容都会被视为模板,之后将会被编译。模板与数据模型($scope)和控制器一起构成了动态视图,用户可以在浏览器中看到并与之交互。本例中没有控制器和明确的数据模型,但AngularJS会在后台创建对应的内容。

所有的AngularJS都会有一个$rootScope,它会持有所有子scope的引用,它自身保存的数据也可以被所有的$scope共享。本例中,由于没有明确指定控制器,所以模板会被绑定到这个$rootScope上,并设置一个子$scope。

在模板中,给输入控件绑定了一个模型(由ng-model指明的yourName),AngularJS会在这个数据控件上注册一个键盘事件监听器,并且将所有输入到这个控件中的内容自动保存到$rootScope.yourName,但我们并没有也并不需要声明这个变量。scope中的变量会被初始化为undefined,就像正常的JavaScript对象一样。

模板中还用到了$interpolate指令,表示为两对大花括号 {{ yourName }}。这个指令会创建一个$watcher,监听模型的变化,并在发生变化的时候将数据更新到视图上。

接下来看看如何将一个DOMElement转换成动态视图。

编译模板

AngularJS找到ng-app指令之后,它会启动并创建一个新的$rootScope,开始编译"根"节点(带ng-app的那个节点)下面所有的子节点。

首先,Angular的$compile函数将传入的DOMElement作为输入。这一点与其它的框架很不一样,AngularJS会使用浏览器的API遍历整个DOM,而其它模板只是在做字符串替换。如果需要用字符串作为传入的模板,则先要用$angular.element函数将字符串转为DOMElement。这个函数实际上就是jQuery中的$()函数的。

$compile函数会遍历DOM,并查找"指令"(Directive),将找到的每个"指令"添加到一个列表中,整个DOM遍历完成后,再将列表中的"指令"按照"优先级"排序。之后,执行每个"指令"自己的compile函数,让"指令"有机会去修改DOM。每个指令的compile函数会返回一个"链接"函数,该函数会被拼接成一个完整的链接函数,并被返回。

接下来,AngularJS会执行返回的"链接"函数,对应的scope会被传入到这个执行过程中。这一步中,所有的子"链接"函数都会被执行,并绑定在同一个scope上,或依照"指令"的设定创建一个新的scope。所有的"链接"函数执行完毕后,每个"链接"函数都会返回一组DOMElement,这些DOMElement已经完成数据绑定和事件监听,AngularJS会将它们添加到父节点。

上述过程的伪代码可以表示如下

var $compile = ...; //注入到你的代码
var $rootScope = ...; //注入
var parent = ...; //编译过的模板内容会被添加到该DOMElement下
var template = ...; //我们的模板的DOMElement

var linkFn = $compile(template); //编译模板,返回"链接"函数

var element = linkFn(scope); //"链接",返回处理好的DOMElement

parent.appendChild(element); //将处理好的DOMElement添加到父节点

总结

基本就是这些了。如你所见,AngularJS完全不同于其他的模板系统,它约定了一些规则,并基于这些规则做了一些假定,之后你就不必将精力耗费在启动代码上,而可以放在真正的应用上。这只是AngularJS的一部分,之后会继续讲讲$watch和$digest如何保证视图刷新。

上一页2014-09-02 Facebook的Dalvik运行期补丁下一页2014-08-13 在Android中使用OSGi框架(Knopflerfish)

最后更新于5年前

这有帮助吗?

接下来发生的事情分为两步,编译和链接(这里借用了编译原理中的术语,但本质上是一样的)。更深的介绍可以参考文档。

via

AngularJS documentation on the HTML Compiler
src