指令是我们用来扩展浏览器能力的技术之一。在DOM编译期间,和HTML元素关联着的指令会被检测到,并且被执行。这使得指令可以为DOM指定行为,或者改变它。
AngularJS有一套完整的、可扩展的、用来帮助web应用开发的指令集,它使得HTML可以转变成“特定领域语言(DSL)”。
指令可以做为HTML中的元素名,属性名,类名,或者注释。下面是一些等效调用myDir指令的例子:
angular在编译期间,编译器会用$interpolate服务去检查文本中是否嵌入了表达式。这个表达式会被当成一个监视器一样注册,并且作为$digest循环中的一部分,它会自动更新。
HTML的编译分为三个阶段:
你可能会疑惑为什么编译过程和链接过程要分离。要明白其中的原因,你可以先看下面这个带有“重复指令”的例子:
当上面的例子被编译后,编译器会遍历所有节点来寻找指令。例如{{user}}是一个替换式指令,ngRepeat是另一个指令。但是ngRepeat有一个难题。他需要为user.actions中的每一个action 构造一个li。这意味着它先要保存一个“干净”的li元素来用作克隆,然后等新的action插入进来时,克隆这个干净的li元素,把克隆出来的li元素插入到ul中。但是仅仅克隆li的话工作还没完。他还需要编译这个li才能把其中的{{action.descriptions}}的替换式替换成相应作用域下的值。我们可以用一个简单的方法来克隆和插入li元素,然后编译它。但是要编译每一个li的话,速度会很慢, 因为编译的工程需要我们遍历DOM树。如果我们在一个需要循环100次循环体内执行编译的话,性能问题就会马上凸现出来。
而我们的解决方案就是将编译工程分为两个阶段。编译阶段将指令识别出来并按优先级排序,链接阶段将作用域中的实例和li进行链接。
ngRepeat 会阻止li子元素{{action.description}}的编译,取而代之的是 ngRepeat指令会单独对li进行编译,编译时,会生成多个li元素组成的模板。这个编译结束后会生成一个链接函数,这个函数在执行时,会对整个模板进行编译,然后为每一个li元素创建一个新的作用域,并把它和对应的作用域链接上。这里,我们只需要编译一次(对模板进行一次统一的编译就行了),只是在链接的时候,需要链接多次,而链接操作并不消耗性能。
大部分情况下你不需要控制这么多细节,要简化上面的代码,我们首先需要依赖基本选项的默认值。如果使用默认值的话,上面的代码可以简化成:
由于大部分的指令只关心实例,并不需要将模板进行变形,所以我们还可以简化成:
上面代码中的factory函数,我们叫工厂函数,它是用来创建指令的。它只会被调用一次:就是当编译器第一次匹配到相应指令的时候,你可以在其中进行任何初始化的工作。调用它时使用的是?$injector.invoke?, 所以它遵循所有注入器的规则。
指令定义对象,也就是上面代码中的directiveDefinitionObject对象,给编译器提供了生成指令需要的细节。这个对象的属性有:
编译函数是用来处理需要修改模板DOM的情况的。因为大部分指令都不需要修改模板,所以这个函数也不常用。需要用到的例子有ngTrepeat,这个是需要修改模板的,还有ngView这个是需要异步载入内容的。编译函数接受以下参数。
注意:在编译函数里面不要进行任何DOM变形之外的操作。 更重要的,DOM监听事件的注册应该在链接函数中做,而不是编译函数中。
编译函数可以返回一个对象或者函数。
链接函数负责注册DOM事件和更新DOM。它是在模板被克隆之后执行的,它也是大部分指令逻辑代码编写的地方。
Pre-linking function?在子元素被链接前执行。不能用来进行DOM的变形,以防链接函数找不到正确的元素来链接。
Post-linking function?所有元素都被链接后执行。
The Attributes object属性对象 - 作为参数传递给链接函数和编译函数。这使得下列资源可以被使用。
通常需要使用更复杂的DOM结构替换单个指令。这允许指令成为一个可以生成应用程序可重用组件的短标志。
?
希望你喜欢,并分享我的工作~带你走近AngularJS系列: