angularjs中的编译和链接函数有什么区别

num*_*ati 209 javascript angularjs

有人可以简单解释一下吗?

文档似乎有点迟钝.我没有得到何时使用一个而不是另一个的本质和全局.两个对比的例子很棒.

Mar*_*cok 217

  • 编译函数 - 用于模板 DOM操作(即操作tElement = template元素),因此适用于与指令关联的模板的所有DOM克隆的操作.

  • link函数 - 用于注册DOM侦听器(即实例范围上的$ watch表达式)以及实例 DOM操作(即,操作iElement =单个实例元素).
    它在克隆模板后执行.例如,在<li ng-repeat ...>中,在将<li>模板(tElement)克隆到(iElement)特定的<li>元素之后执行链接功能.
    $ watch()允许指令通知实例范围属性更改(实例范围与每个实例关联),这允许指令将更新的实例值呈现给DOM - 通过将实例范围中的内容复制到DOM.

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

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

帮助确定使用哪种方法的一种方法:考虑编译函数不接收 scope参数.(我故意忽略了transclude链接函数参数,它接收一个被转换的范围 - 这很少使用.)因此编译函数不能做任何你想做的事情,需要一个(实例)范围 - 你可以't $监视任何模型/实例范围属性,您不能使用实例范围信息操纵DOM,也不能调用在实例范围上定义的函数等.

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

这是一个只使用编译函数的指令示例.该指令只需要转换模板DOM,因此可以使用编译功能.

另一种帮助确定使用哪种方法:如果你没有在链接函数中使用"element"参数,那么你可能不需要链接函数.

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

请注意,如果您需要编译函数和链接函数(或前后链接函数),则编译函数必须返回链接函数,因为如果定义了'compile'属性,则会忽略'link'属性.

也可以看看

  • 关于编译与链接的最佳解释. (5认同)

Ton*_* K. 68

我把头撞在墙上几天,我觉得有点解释.

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

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

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

编译FUNCTION是指令中的一位代码,它在特定时间由编译器SERVICE($ compile)运行.

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

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

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

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

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

查看对$ compile的嵌套和显式调用是如何工作的也很有帮助,所以我在http://jsbin.com/imUPAMoV/1/edit上创建了一个可以查看的操场.基本上,它只是将步骤记录到console.log.

我会在这里说明你在垃圾箱里看到的结果.对于自定义指令的DOM,tp和sp嵌套如下:

<tp>
   <sp>
   </sp>
</tp>
Run Code Online (Sandbox Code Playgroud)

Angular compile SERVICE将调用:

tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link
Run Code Online (Sandbox Code Playgroud)

jsbin代码还有tp post-link FUNCTION在第三个指令(up)上显式调用compile SERVICE,它在最后执行所有三个步骤.

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

场景1:作为MACRO的指令

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

假设您有一个指向的templateUrl:

<div><span><input type="text"></span><div>
Run Code Online (Sandbox Code Playgroud)

你想要一个自定义指令:

<my-field model="state" name="address"></my-field>
Run Code Online (Sandbox Code Playgroud)

将DOM转化为:

<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>
Run Code Online (Sandbox Code Playgroud)

基本上,您希望通过使用指令可以解释的一致模型结构来减少样板.换句话说:你想要一个宏.

这对于编译阶段非常有用,因为您可以根据属性将所有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() {}
   };
}
Run Code Online (Sandbox Code Playgroud)

操作顺序是(你可以通过前面提到的jsbin看到这个):

  1. 编译SERVICE找到my-field
  2. 它调用指令上的编译FUNCTION,它更新DOM.
  3. 然后编译SERVICE进入生成的DOM和COMPILES(递归)
  4. 然后编译SERVICE调用自上而下的前连接
  5. 然后编译SERVICE调用后链接BOTTOM UP,因此在链接内部节点之后调用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);
    }
});
Run Code Online (Sandbox Code Playgroud)

如果您确定传递给$ 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'
};
Run Code Online (Sandbox Code Playgroud)

你可能想要一个指令:

<form name="theForm">
  <my-field model="state" metadata="metadata" name="address">
</form>
Run Code Online (Sandbox Code Playgroud)

自动包含正确的指令和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>
...
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您肯定需要访问范围(因为这是存储验证的地方),并且必须手动编译添加内容,再次注意不要进行双重编译.(作为附注,你需要在包含的表单标签上设置一个名称(我在这里假设这个表格),并且可以通过链接访问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
    }
  };
});
Run Code Online (Sandbox Code Playgroud)

当然,您可以逐个编译嵌套元素,以避免在再次编译顶级元素时担心ng指令的重复处理.

关于这种情况的最后一点说明:我暗示你要从服务器推送验证的定义,在我的例子中,我已经将它们显示为范围内的数据.我把它作为练习让读者弄清楚如何处理需要从REST API中提取数据的方法(提示:延迟编译).

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

当然,链接最常见的用途是通过watch/apply简单地连接双向数据绑定.大多数指令都属于这一类,所以它在其他地方得到了充分的涵盖.

  • +1我喜欢你的答案.它提供更多信息. (2认同)
  • Awsome和酷答案! (2认同)

Mat*_*all 50

来自文档:

编译器

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

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

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

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

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


来自@UmurKontacı:

如果要进行DOM转换,那应该是compile.如果要添加一些行为更改的功能,它应该在link.

  • 如果要进行`DOM`转换,那么应该是`compile`如果要添加一些功能是行为更改,它应该在`link`中. (46认同)
  • +1以上评论; 这是我到目前为止发现的最简洁的描述.它与我在[here](http://blog.angularjs.org/2012/05/custom-components-part-2.html)中找到的教程相匹配. (4认同)

Sun*_*hah 18

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

将编译器函数视为适用于模板的事物,以及允许通过向其添加类或类似内容来更改模板本身的事物.但是链接函数实际上是将两者绑定在一起的工作,因为链接函数可以访问范围,而且它是为特定模板的每个实例化执行一次的链接函数.因此,您可以在编译函数中放置的唯一事物是在所有实例中都很常见的事物.


use*_*687 10

线程迟到了.但是,为了未来读者的利益:

我看到了以下视频,它以非常好的方式解释了Angular JS中的Compile和Link:

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

在这里复制/输入所有内容并不令人满意.我从视频中截取了几个屏幕截图,它解释了编译和链接阶段的每个阶段:

在Angular JS中编译和链接

在Angular JS中编译和链接 - 嵌套指令

第二个截图有点令人困惑.但是,如果我们按照步骤编号,它是非常直接的.

第一个循环:首先对所有指令执行"编译".
第二个周期:执行"控制器"和"预链接"(仅一个接一个)第三个周期:"后链接"按相反顺序执行(从最内层开始)

以下是代码,它演示了以上内容:

var app = angular.module('app', []);

app.controller('msg', ['$scope', function($scope){

}]);

app.directive('message', function($interpolate){
    return{

        compile: function(tElement, tAttributes){ 
            console.log(tAttributes.text + " -In compile..");
            return {

                pre: function(scope, iElement, iAttributes, controller){
                    console.log(iAttributes.text + " -In pre..");
                },

                post: function(scope, iElement, iAttributes, controller){
                    console.log(iAttributes.text + " -In Post..");
                }

            }
        },

        controller: function($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>
Run Code Online (Sandbox Code Playgroud)

更新:

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


pix*_*its 6

两个阶段:编译和链接

编译:

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

链接:

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