angularjs中的编译和链接函数有什么区别
有人可以用简单的术语解释吗?
文档似乎有点迟钝。我没有得到何时使用一个的本质和大局。对比两者的例子会很棒。
有人可以用简单的术语解释吗?
文档似乎有点迟钝。我没有得到何时使用一个的本质和大局。对比两者的例子会很棒。
编译函数 - 用于模板 DOM 操作(即,tElement = 模板元素的操作),因此操作适用于与指令关联的模板的所有 DOM 克隆。
link函数 - 用于注册 DOM 侦听器(即,实例作用域上的$watch表达式)以及实例 DOM 操作(即,iElement 的操作 = 单个实例元素)。
它在克隆模板后执行。例如,在<li ng-repeat...>,在为该特定<li>元素克隆(到 iElement 中)<li>模板 (tElement) 后,将执行 link 函数。
$watch() 允许指令在实例范围属性更改时收到通知(实例范围与每个实例相关联),这允许指令通过将内容从实例范围复制到 DOM 中来向 DOM 呈现更新的实例值。
请注意,DOM 转换可以在编译函数和/或链接函数中完成。
大多数指令只需要一个链接函数,因为大多数指令只处理特定的DOM元素实例(及其实例范围)。
帮助确定要使用的一种方法:考虑编译函数不接收参数。(我故意忽略了 transclude 链接函数参数,它接收一个 transcluded 作用域 - 这很少使用。因此,编译函数无法执行任何需要(实例)作用域的操作 - 您无法$watch任何模型/实例作用域属性,不能使用实例作用域信息操作 DOM,不能调用在实例作用域上定义的函数,等等。scope
但是,编译函数(如链接函数)确实可以访问属性。因此,如果您的 DOM 操作不需要实例作用域,则可以使用编译函数。下面是一个仅使用编译函数的指令示例,出于这些原因。它会检查属性,但不需要实例范围来完成其工作。
下面是一个也只使用编译函数的指令示例。该指令只需要转换模板 DOM,因此可以使用编译函数。
另一种帮助确定使用哪种方法的方法:如果您不在链接函数中使用“element”参数,则可能不需要链接函数。
由于大多数指令都有链接函数,因此我不打算提供任何示例 - 它们应该很容易找到。
请注意,如果需要编译函数和链接函数(或链接前和链接后函数),编译函数必须返回链接函数,因为如果定义了“编译”属性,则会忽略“链接”属性。
另请参见
我在这个问题上用头撞了几天,我觉得需要更多的解释。
基本上,文档提到分离在很大程度上是一种性能增强。我想重申的是,编译阶段主要用于在编译子元素本身之前需要修改 DOM 的情况。
出于我们的目的,我将强调术语,否则会令人困惑:
编译器 SERVICE($compile)是处理 DOM 并在指令中运行各种代码位的角度机制。
编译函数是指令中的一段代码,由编译器 SERVICE ($compile) 在特定时间运行。
关于编译函数的一些说明:
您无法修改 ROOT 元素(指令影响的元素),因为它已经从 DOM 的外部级别进行编译(编译服务已经扫描了该元素上的指令)。
如果要向(嵌套)元素添加其他指令,请执行下列操作之一:
必须在编译阶段添加它们。
必须将编译服务注入链接阶段并手动编译元素。但是,当心编译两次东西!
了解嵌套和对$compile显式调用的工作原理也很有帮助,因此我创建了一个游乐场,用于在 http://jsbin.com/imUPAMoV/1/edit 查看它。基本上,它只是将步骤记录到控制台.log。
我将在这里陈述您在该箱中看到的结果。对于嵌套的自定义指令 tp 和 sp 的 DOM,如下所示:
<tp>
<sp>
</sp>
</tp>
Angular compile SERVICE 将调用:
tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link
jsbin 代码还具有 tp 后链接函数,它显式调用第三个指令 (up) 上的编译 SERVICE,该指令在最后执行所有三个步骤。
现在,我想演练几个场景,以展示如何使用编译和链接来执行各种操作:
方案 1:作为宏的指令
您希望将指令(例如 ng-show)动态添加到模板中可从属性派生的内容。
假设您有一个指向以下内容的模板 Url:
<div><span><input type="text"></span><div>
并且你想要一个自定义指令:
<my-field model="state" name="address"></my-field>
将 DOM 变成这样:
<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>
基本上,您希望通过具有一些一致的模型结构来减少样板,您的指令可以解释。换句话说:你想要一个宏。
这对于编译阶段来说是一个很大的用途,因为您可以将所有 DOM 操作基于您仅从属性中知道的内容。只需使用 jQuery 添加属性:
compile: function(tele, tattr) {
var span = jQuery(tele).find('span').first();
span.attr('ng-show', tattr.model + ".visible." + tattr.name);
...
return {
pre: function() { },
post: function() {}
};
}
操作的顺序将是(您可以通过前面提到的jsbin看到这一点):
在上面的示例中,不需要链接,因为指令的所有工作都是在编译函数中完成的。
在任何时候,指令中的代码都可以要求编译器 SERVICE 在其他元素上运行。
这意味着,如果您注入编译服务,我们可以在链接函数中执行完全相同的操作:
directive('d', function($compile) {
return {
// REMEMBER, link is called AFTER nested elements have been compiled and linked!
link: function(scope, iele, iattr) {
var span = jQuery(iele).find('span').first();
span.attr('ng-show', iattr.model + ".visible." + iattr.name);
// CAREFUL! If span had directives on it before
// you will cause them to be processed again:
$compile(span)(scope);
}
});
如果您确定要传递给$compile SERVICE 的元素最初是无指令的(例如,它们来自您定义的模板,或者您只是使用angular.element()创建了它们),那么最终结果与以前几乎相同(尽管您可能正在重复一些工作)。但是,如果元素上有其他指令,您只是导致再次处理这些指令,这可能会导致各种不稳定的行为(例如,事件和监视的双重注册)。
因此,编译阶段是宏样式工作的更好选择。
方案 2:通过作用域数据进行 DOM 配置
这个是从上面的例子中得出的。假设在操作 DOM 时需要访问作用域。好吧,在这种情况下,编译部分对您毫无用处,因为它发生在作用域可用之前。
因此,假设您希望使用验证来拉皮条化输入,但您希望从服务器端 ORM 类 (DRY) 导出验证,并让它们自动应用并为这些验证生成正确的客户端 UI。
您的模型可能会推动:
scope.metadata = {
validations: {
address: [ {
pattern: '^[0-9]',
message: "Address must begin with a number"
},
{ maxlength: 100,
message: "Address too long"
} ]
}
};
scope.state = {
address: '123 Fern Dr'
};
你可能想要一个指令:
<form name="theForm">
<my-field model="state" metadata="metadata" name="address">
</form>
以自动包含正确的指令和 div 以显示各种验证错误:
<form name="theForm">
<div>
<input ng-model="state.address" type="text">
<div ng-show="theForm.address.$error.pattern">Address must begin with a number</input>
...
在这种情况下,您肯定需要访问范围(因为这是存储验证的地方),并且必须手动编译添加的内容,再次注意不要重复编译。(作为旁注,您需要在包含表单标记上设置一个名称(我在这里假设是Form),并且可以与iElement.parent().controller('form').$name链接访问它)。
在这种情况下,编写编译函数是没有意义的。链接真的是你想要的。步骤是:
这样:
angular.module('app', []).
directive('my-field', function($compile) {
return {
link: function(scope, iele, iattr) {
// jquery additions via attr()
// remove ng attr from top-level iele (to avoid duplicate processing)
$compile(iele)(scope); // will pick up additions
}
};
});
当然,您可以逐个编译嵌套元素,以避免在再次编译顶级元素时担心ng指令的重复处理。
关于此方案的最后一点说明:我暗示您将从服务器推送验证的定义,在我的示例中,我已将它们显示为范围内已有的数据。我把它留给读者作为一个练习,以弄清楚如何处理从REST API中提取该数据的需求(提示:延迟编译)。
场景三:通过链路进行双向数据绑定
当然,链接最常见的用途是通过监视/应用简单地挂接双向数据绑定。大多数指令都属于这一类,因此在其他地方已充分涵盖。