【问题标题】:What is the difference between compile and link function in angularjsangularjs中的编译和链接函数有什么区别
【发布时间】:2012-08-23 05:58:32
【问题描述】:

谁能简单解释一下?

文档似乎有点迟钝。我没有了解何时使用其中一个的本质和全局。一个对比两者的例子会很棒。

【问题讨论】:

标签: javascript angularjs


【解决方案1】:
  • 编译函数 - 用于 template DOM 操作(即,tElement = 模板元素的操作),因此操作适用于与指令关联的模板的所有 DOM 克隆。 p>

  • link 函数 - 用于注册 DOM 侦听器(即实例范围上的 $watch 表达式)以及 instance DOM 操作(即 iElement = 单个实例元素的操作)。
    模板被克隆后执行。例如,在

  • 中,链接函数在
  • 模板 (tElement) 被克隆(到 iElement)用于特定
  • 元素之后执行。
    $watch () 允许将实例范围属性更改通知指令(实例范围与每个实例相关联),这允许指令通过将内容从实例范围复制到 DOM 来向 DOM 呈现更新的实例值。

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

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

一种帮助确定使用哪个的方法:考虑编译函数没有收到scope 参数。 (我故意忽略了 transclude 链接函数参数,它接收一个被包含的范围——这是 rarely 使用的。)所以编译函数不能做任何你想做的需要(实例)范围的事情——你不能 $watch 任何模型/实例作用域属性,你不能使用实例作用域信息操作 DOM,你不能调用实例作用域上定义的函数,等等。

但是,编译函数(如链接函数)确实可以访问属性。因此,如果您的 DOM 操作不需要实例范围,则可以使用 compile 函数。由于这些原因,这是一个仅使用编译函数的指令的an example。它检查属性,但不需要实例范围来完成其工作。

这是一个指令的an example,它也只使用编译函数。该指令只需要转换模板DOM,因此可以使用编译函数。

另一种帮助确定使用哪个方法:如果您不使用链接函数中的“元素”参数,那么您可能不需要链接函数。

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

请注意,如果您需要一个编译函数和一个链接函数(或前后链接函数),编译函数必须返回链接函数,因为如果 'compile' 属性为已定义。

另见

【讨论】:

  • 编译与链接的最佳解释。
  • 当您说if you don't use the "element" parameter in the link function, then you probably don't need a link function. 时,您的意思是“范围”而不是“元素”?
【解决方案2】:

我为此苦苦挣扎了几天,我觉得有必要再解释一下。

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

出于我们的目的,我将强调术语,否则会令人困惑:

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

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

关于compile FUNCTION的一些注意事项:

  1. 您不能修改 ROOT 元素(您的指令影响的那个),因为它已经从 DOM 的外层编译(编译服务已经扫描了该元素上的指令)。

  2. 如果您想向(嵌套)元素添加其他指令,您可以:

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

    2. 必须将编译服务注入链接阶段并手动编译元素。但是,请注意不要编译两次!

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

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

<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 post-link FUNCTION 在第三个指令 (up) 上显式调用 compile SERVICE,该指令在最后完成所有三个步骤。

现在,我想通过几个场景来展示如何使用编译和链接来做各种事情:

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

  1. 编译SERVICE找到my-field
  2. 它在指令上调用 compile FUNCTION,从而更新 DOM。
  3. 编译服务然后进入生成的 DOM,并编译(递归)
  4. 编译SERVICE然后调用pre-link top-down
  5. compile SERVICE 然后调用 post-link BOTTOM UP,所以 my-field 的链接函数在内部节点被链接后被调用。

在上面的例子中,不需要链接,因为所有指令的工作都是在 compile 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>
...

在这种情况下,您肯定需要访问范围(因为这是存储验证的地方),并且必须手动编译添加的内容,再次注意不要重复编译。 (作为旁注,您需要在包含表单标签上设置一个名称(我假设这里是 theForm),并且可以通过 iElement.parent().controller('form').$name 链接访问它.

在这种情况下,编写编译函数是没有意义的。链接真的是你想要的。步骤是:

  1. 定义一个完全没有角度指令的模板。
  2. 定义添加各种属性的链接函数
  3. 删除您可能在顶级元素上允许的任何角度指令(my-field 指令)。它们已经被处理过,这是一种防止它们被双重处理的方法。
  4. 通过在您的顶级元素上调用 compile 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:通过链接进行双向数据绑定

当然,link 最常见的用法是通过 watch/apply 简单地连接双向数据绑定。大多数指令都属于这一类,因此在其他地方已经充分涵盖。

【讨论】:

  • 超酷的答案!
  • 如何在不重复编译的情况下添加嵌套元素?
【解决方案3】:

From the docs:

编译器

Compiler 是一个 Angular 服务,它遍历 DOM 寻找属性。编译过程分为两个阶段。

  1. 编译: 遍历 DOM 并收集所有指令。结果是一个链接函数。

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

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

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


From @UmurKontacı:

如果你要进行 DOM 转换,它应该是compile。如果你想添加一些行为改变的特性,应该在link

【讨论】:

  • 如果你要做DOM的转换,应该是compile,如果你想添加一些特性是行为改变,它应该在link
  • +1 以上评论;这是迄今为止我找到的最简洁的描述。它与我找到的教程相匹配 here.
【解决方案4】:

这是来自 Misko 关于指令的演讲。 http://youtu.be/WqmeI5fZcho?t=16m23s

把编译器函数想象成 在模板上工作的东西和允许的东西 更改模板本身,例如,向其中添加一个类或 任何类似的东西。但实际上是链接功能 将两者绑定在一起的工作,因为链接功能具有 访问范围,它是执行一次的链接函数 对于特定模板的每个实例化。所以只有一种 你可以放在编译函数里面的东西是 在所有实例中都是通用的。

【讨论】:

    【解决方案5】:

    话题有点晚了。但是,为了未来读者的利益:

    我看到了以下视频,它以非常棒的方式解释了 Angular JS 中的编译和链接:

    https://www.youtube.com/watch?v=bjFqSyddCeA

    在此处复制/输入所有内容并不令人愉快。我从视频中截取了几张截图,解释了编译和链接阶段的每个阶段:

    第二张截图有点混乱。但是,如果我们按照步骤编号,就很简单了。

    第一个循环:首先对所有指令执行“编译”。
    第二个周期:执行“控制器”和“预链接”(一个接一个) 第三个循环:“Post-Link”以相反的顺序执行(从最里面开始)

    下面是代码,演示了上面的内容:

    var app = angular.module('app', []); app.controller('msg', ['$scope', function($scope){ }]); app.directive('消息',函数($interpolate){ 返回{ 编译:函数(tElement,tAttributes){ console.log(tAttributes.text + "-在编译中.."); 返回 { 前:功能(范围,iElement,iAttributes,控制器){ console.log(iAttributes.text + "-在 pre.."); }, 发布:功能(范围,iElement,iAttributes,控制器){ console.log(iAttributes.text + "-In Post.."); } } }, 控制器:函数($scope,$element,$attrs){ console.log($attrs.text + " -In controller.."); }, } });
    <body ng-app="app">
    <div ng-controller="msg">
        <div message text="first">
            <div message text="..second">
                <div message text="....third">
    
                </div>              
            </div>  
        </div>
    </div>
    

    更新:

    同一视频的第 2 部分可在此处观看:https://www.youtube.com/watch?v=1M3LZ1cu7rw 该视频通过一个简单的示例详细说明了如何在 Angular JS 的编译和链接过程中修改 DOM 和处理事件。

    【讨论】:

    • 使用 compilepost 修改 DOM,然后再修改供应商指令中的 template 部分。
    【解决方案6】:

    两个阶段:编译和链接

    编译:

    遍历 DOM 树寻找指令(元素/属性/类/cmets)。指令的每次编译都可能修改其模板,或修改尚未编译的内容。一旦一个指令被匹配,它就会返回一个链接函数,该函数在稍后阶段用于将元素链接在一起。在编译阶段结束时,我们有一个已编译指令及其对应链接函数的列表。

    链接:

    当一个元素被链接时,DOM 树在其在 DOM 树中的分支点被破坏,内容被模板的已编译(和链接)实例替换。原始置换的内容要么被丢弃,要么在嵌入的情况下重新链接回模板。通过嵌入,这两个部分重新链接在一起(有点像一条链,模板部分位于中间)。当调用链接函数时,模板已经被绑定到一个范围,并被添加为元素的子元素。链接功能是您进一步操作 DOM 并设置更改侦听器的机会。

    【讨论】:

      【解决方案7】:

      这个问题很老了,我想做一个简短的总结,可能会有所帮助:

      • 为所有指令实例调用一次编译
      • 编译的主要目的是返回/创建链接(可能还有前/后)函数/对象。您还可以初始化指令实例之间共享的内容。
      • 在我看来,“链接”是这个功能的一个令人困惑的名称。我更喜欢“预渲染”。
      • 为每个指令实例调用链接,其目的是准备在 DOM 中呈现指令。

      【讨论】:

      • 建议名称加一个:“pre-render”
      猜你喜欢
      • 2011-04-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-02-11
      • 2014-11-26
      • 2014-08-10
      • 2018-07-11
      • 2010-09-16
      相关资源
      最近更新 更多