为了账号安全,请及时绑定邮箱和手机立即绑定

angularjs中的compile和link函数有什么区别

/ 猿问

angularjs中的compile和link函数有什么区别

PIPIONE 2019-10-04 15:14:54

有人可以用简单的方式解释吗?

该文档似乎有点钝。我没有掌握何时使用另一种方法的本质和全局。对比两者的示例将是很棒的。


查看完整描述

3 回答

?
Cats萌萌

编译功能-用于模板 DOM操纵(即,tElement =模板元素的操纵),因此适用于与指令关联的模板的所有DOM克隆的操纵。


链接功能-用于注册DOM侦听器(即实例范围内的$ watch表达式)以及实例 DOM操作(即iElement =单个实例元素的操作)。

克隆模板后执行。例如,在<li ng-repeat ...>内部,已经为特定的<li>元素克隆了<li>模板(tElement)(将其插入到iElement中)之后,将执行链接功能。

$ watch()允许指令通知实例范围属性更改(每个实例都与一个实例范围相关联),这允许该指令将更新后的实例值呈现给DOM-通过将内容从实例范围复制到DOM。


注意,可以在编译功能和/或链接功能中完成DOM转换。


大多数指令仅需要链接函数,因为大多数指令仅处理特定的DOM元素实例(及其实例范围)。


帮助确定使用哪个方法的一种方法:考虑编译函数未接收  scope参数。(我故意忽略了transclude链接函数参数,该参数接收到一个transcluded范围- 很少使用。)因此,compile函数无法执行需要(实例)范围的任何操作-您可以$不会监视任何模型/实例范围属性,您不能使用实例范围信息来操作DOM,不能调用在实例范围内定义的函数,等等。


但是,编译功能(如链接功能)确实可以访问属性。因此,如果您的DOM操作不需要实例范围,则可以使用编译函数。这里有一个例子,只有使用编译功能,对于那些原因指令。它检查属性,但不需要实例作用域即可完成其工作。


这是仅使用编译功能的指令示例。该指令仅需要转换模板DOM,因此可以使用编译功能。


帮助确定要使用哪种方法的另一种方法:如果您不使用链接功能中的“ element”参数,则可能不需要链接功能。


由于大多数指令都具有链接功能,因此我将不提供任何示例-应该很容易找到它们。


请注意,如果需要编译函数和链接函数(或链接前和链接后的函数),则编译函数必须返回链接函数,因为如果定义了“编译”属性,则将忽略“链接”属性。


也可以看看


定义指令时“控制器”,“链接”和“编译”函数之间的区别

戴夫·史密斯(Dave Smith)关于指令的出色ng-conf 2104讨论(链接转到视频中有关编译和链接的部分)


查看完整回答
反对 回复 2019-10-04
?
慕盖茨4494581

我将头撞到墙上这几天,感觉还需要更多说明。


基本上,文档提到分离在很大程度上是性能的提高。我要重申的是,编译阶段主要用于需要在子元素本身被编译之前修改DOM的情况。


为了我们的目的,我要强调术语,否则会造成混淆:


编译器SERVICE($ compile)是处理DOM并运行指令中各种代码位的角度机制。


编译功能是指令中的一部分代码,指令在特定时间由编译器SERVICE($ compile)运行。


有关编译功能的一些注意事项:


您不能修改ROOT元素(您的指令所影响的元素),因为它已经从DOM的外部层进行了编译(编译SERVICE已经在该元素上扫描了指令)。


如果要将其他指令添加到(嵌套的)元素,则可以:


必须在编译阶段添加它们。


必须将编译服务注入链接阶段并手动编译元素。但是,提防编译两次!


查看嵌套和显式调用$ compile的工作方式也很有帮助,因此我在http://jsbin.com/imUPAMoV/1/edit创建了一个游乐场,供您查看。基本上,它只是将步骤记录到console.log。


我将在此处说明您在该bin中看到的结果。对于嵌套自定义指令tp和sp的DOM,如下所示:


<tp>

   <sp>

   </sp>

</tp>

角编译SERVICE将调用:


tp compile

sp compile

tp pre-link

sp pre-link

sp post-link

tp post-link

jsbin代码还具有tp后链接功能,在第三个指令(向上)上显式调用编译服务,该指令最后执行所有三个步骤。


现在,我想通过几个场景来展示如何使用编译和链接来执行各种操作:


场景1:作为宏指令


您想将指令(例如ng-show)动态添加到可以从属性派生的模板中的某些内容。


假设您有一个templateUrl指向:


<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看到它):


编译服务找到我的领域

它在指令上调用编译FUNCTION,从而更新DOM。

然后,编译SERVICE进入生成的DOM和COMPILES(递归)

然后,编译服务会自顶向下调用预链接

然后,编译服务将调用后链接底部向上,因此在链接内部节点之后,称为my-field的链接功能。

在上面的示例中,不需要链接,因为所有指令的工作都是在编译FUNCTION中完成的。


在任何时候,指令中的代码都可以要求编译器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的链接来访问它) 。


在这种情况下,编写编译函数毫无意义。链接确实是您想要的。步骤将是:


定义一个完全没有角度指令的模板。

定义添加各种属性的链接函数

删除可能在顶级元素上允许的所有角度指令(my-field指令)。它们已经过处理,这是防止它们被双重处理的一种方法。

通过在顶层元素上调用编译SERVICE来完成

像这样:


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中提取数据的问题(提示:延迟编译)。


场景3:通过链接进行双向数据绑定


当然,链接最常见的用法是通过监视/应用简单地连接双向数据绑定。大多数指令都属于此类,因此在其他地方也有足够的介绍。


查看完整回答
反对 回复 2019-10-04
?
慕桂英4014372

从文档:


编译器

编译器是一种有角度的服务,它遍历DOM寻找属性。编译过程分为两个阶段。


编译:遍历DOM并收集所有指令。结果是链接功能。


链接:将指令与作用域结合在一起并产生实时视图。范围模型中的任何更改都将反映在视图中,并且用户与视图的任何交互都将反映在范围模型中。使作用域模型成为事实的唯一来源。


有些指令(例如)ng-repeat会对集合中的每个项目一次克隆DOM元素。编译和链接阶段可以提高性能,因为克隆的模板只需要编译一次,然后为每个克隆实例链接一次。


因此,至少在某些情况下,这两个阶段是作为优化单独存在的。


来自@UmurKontacı:


如果要进行DOM转换,则应为compile。如果要添加一些行为更改的功能,则应在中link。


查看完整回答
反对 回复 2019-10-04

添加回答

回复

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信