Javascript angularjs中编译和链接功能有什么区别

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/12164138/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 07:16:54  来源:igfitidea点击:

What is the difference between compile and link function in angularjs

javascriptangularjs

提问by numan salati

Can someone explain in simple terms?

有人可以用简单的术语解释一下吗?

The docs seems a bit obtuse. I am not getting the essence and the big picture of when to use one over the other. An example contrasting the two would be awesome.

文档似乎有点迟钝。我没有得到什么时候使用一个而不是另一个的本质和大局。对比两者的例子会很棒。

回答by Mark Rajcok

  • compile function - use for templateDOM manipulation (i.e., manipulation of tElement = template element), hence manipulations that apply to all DOM clones of the template associated with the directive.

  • link function - use for registering DOM listeners (i.e., $watch expressions on the instance scope) as well as instanceDOM manipulation (i.e., manipulation of iElement = individual instance element).
    It is executed after the template has been cloned. E.g., inside an <li ng-repeat...>, the link function is executed after the <li> template (tElement) has been cloned (into an iElement) for that particular <li> element.
    A $watch() allows a directive to be notified of instance scope property changes (an instance scope is associated with each instance), which allows the directive to render an updated instance value to the DOM -- by copying content from the instance scope into the DOM.

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

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

Note that DOM transformations can be done in the compile function and/or the link function.

请注意,DOM 转换可以在 compile 函数和/或 link 函数中完成。

Most directives only need a link function, since most directives only deal with a specific DOM element instance (and its instance scope).

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

One way to help determine which to use: consider that the compile function does not receive a scopeargument. (I'm purposely ignoring the transclude linking function argument, which receives a transcluded scope -- this is rarelyused.) So the compile function can't do anything you would want to do that requires an (instance) scope -- you can't $watch any model/instance scope properties, you can't manipulate the DOM using instance scope information, you can't call functions defined on the instance scope, etc.

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

However, the compile function (like the link function) does have access to the attributes. So if your DOM manipulations don't require the instance scope, you can use a compile function. Here's an exampleof a directive that only uses a compile function, for those reasons. It examines the attributes, but it doesn't need an instance scope to do its job.

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

Here's an exampleof a directive that also only uses a compile function. The directive only needs to transform the template DOM, so a compile function can be used.

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

Another way to help determine which to use: if you don't use the "element" parameter in the link function, then you probably don't need a link function.

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

Since most directives have a link function, I'm not going to provide any examples -- they should be very easy to find.

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

Note that if you need a compile function and a link function (or pre and post link functions), the compile function must return the link function(s) because the 'link' attribute is ignored if the 'compile' attribute is defined.

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

See also

也可以看看

回答by Tony K.

I beat my head against the wall on this for a couple of days, and I feel that a bit more explanation is in order.

我在这件事上用头撞墙了几天,我觉得有必要再解释一下。

Basically, the docs mention that the separation is largely a performance enhancement. I would reiterate that the compile phase is mainly used when you need to modify the DOM BEFORE the sub-elements themselves are compiled.

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

For our purposes, I'm going to stress terminology, which is otherwise confusing:

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

The compiler SERVICE ($compile) is the angular mechanism that processes the DOM and runs the various bits of code in directives.

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

The compile FUNCTION is one bit of code within a directive, which is run at a particular time BY the compiler SERVICE ($compile).

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

Some notes about the compile FUNCTION:

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

  1. You cannot modify the ROOT element (the one your directive affects), since it is already being compiled from the outer level of DOM (the compile SERVICE has already scanned for directives on that element).

  2. If you want to add other directives to (nested) elements, you either:

    1. Have to add them during the compile phase.

    2. Have to inject the compile service into the linking phase and compile the elements manually. BUT, beware of compiling something twice!

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

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

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

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

It is also helpful to see how the nesting and explicit calls to $compile work, so I've created a playground for viewing that at http://jsbin.com/imUPAMoV/1/edit. Basically, it just logs the steps to console.log.

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

I'll state the results of what you'd see in that bin here. For a DOM of custom directives tp and sp nested as follows:

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

<tp>
   <sp>
   </sp>
</tp>

Angular compile SERVICE will call:

Angular compile SERVICE 将调用:

tp compile
sp compile
tp pre-link
sp pre-link
sp post-link
tp post-link

The jsbin code also has the tp post-link FUNCTION explicitly call the compile SERVICE on a third directive (up), which does all three steps at the end.

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

Now, I want to walk through a couple of scenarios to show how one might go about using the compile and link to do various things:

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

SCENARIO 1: Directive as a MACRO

情景 1:指令作为宏观

You want to add a directive (say ng-show) dynamically to something in your template that you can derive from an attribute.

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

Say you have a templateUrl that points to:

假设您有一个 templateUrl 指向:

<div><span><input type="text"></span><div>

and you want a custom directive:

并且你想要一个自定义指令:

<my-field model="state" name="address"></my-field>

that turns the DOM into this:

将 DOM 变成这样:

<div><span ng-show="state.visible.address"><input ng-model="state.fields.address" ...>

basically, you want to reduce boilerplate by having some consistent model structure that your directive can interpret. In other words: you want a macro.

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

This is a great use for the compile phase, since you can base all of the DOM manipulations on things you know just from the attributes. Simply use jQuery to add the attributes:

这是编译阶段的一个很好的用途,因为您可以将所有 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() {}
   };
}

The sequence of operations will be (you can see this via the jsbin mentioned earlier):

操作顺序将是(您可以通过前面提到的 jsbin 看到这一点):

  1. The compile SERVICE finds my-field
  2. It calls the compile FUNCTION on the directive, which updates the DOM.
  3. The compile SERVICE then walks into the resulting DOM, and COMPILES (recursively)
  4. The compile SERVICE then calls pre-link top-down
  5. The compile SERVICE then calls post-link BOTTOM UP, so my-field's link function is called AFTER interior nodes have been linked.
  1. compile SERVICE 找到 my-field
  2. 它在指令上调用 compile FUNCTION 来更新 DOM。
  3. 然后 compile SERVICE 进入生成的 DOM,并编译(递归)
  4. compile SERVICE 然后自顶向下调用 pre-link
  5. compile SERVICE 然后调用 post-link BOTTOM UP,所以 my-field 的链接函数在内部节点被链接之后被调用。

In the above example, no linking is needed, since all of the directive's work was done in compile FUNCTION.

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

At any point, the code in a directive can ask for the compiler SERVICE to run on additional elements.

在任何时候,指令中的代码都可以要求编译器 SERVICE 在其他元素上运行。

This means that we can do exactly the same thing in a link function if you inject the compile 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);
    }
});

If you're sure that the elements you are passing to $compile SERVICE originally were directive-free (e.g. they came from a template you defined, or you just created them with angular.element()), then the end result is pretty much the same as before (though you may be repeating some work). However, if the element had other directives on it, you just caused those to be processed again, which can cause all sorts of erratic behavior (e.g. double-registration of events and watches).

如果您确定传递给 $compile SERVICE 的元素最初是无指令的(例如它们来自您定义的模板,或者您只是使用 angular.element() 创建它们),那么最终结果几乎是和以前一样(虽然你可能会重复一些工作)。但是,如果元素上有其他指令,您只会导致再次处理这些指令,这可能会导致各种不稳定的行为(例如,事件和监视的双重注册)。

Thus, the compile phase is a much better choice for macro-style work.

因此,编译阶段是宏风格工作更好的选择。

SCENARIO 2: DOM configuration via scope data

场景 2:通过范围数据配置 DOM

This one follows from the example above. Suppose you need access to the scope while manipulating the DOM. Well, in that case, the compile section is useless to you, since it happens before a scope is available.

这是从上面的例子中得出的。假设您在操作 DOM 时需要访问作用域。那么,在这种情况下,编译部分对您来说是无用的,因为它发生在作用域可用之前。

So, let's say you want to pimp out an input with validations, but you want to export your validations from a server-side ORM class (DRY), and have them auto-apply and generate the proper client-side UI for those validations.

因此,假设您想通过验证输出输入,但您想从服务器端 ORM 类 (DRY) 导出验证,并让它们自动应用并为这些验证生成正确的客户端 UI。

Your model might push:

您的模型可能会推送:

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'
};

and you might want a directive:

你可能想要一个指令:

<form name="theForm">
  <my-field model="state" metadata="metadata" name="address">
</form>

to auto-include the proper directives and divs to show the various validation errors:

自动包含正确的指令和 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>
...

In this case you definitely need access to the scope (since that is where your validations are stored), and are going to have to compile the additions manually, again being careful not to double-compile things. (as a side note, you would need to set a name on the containing form tag (I'm assuming theForm here), and could access it in link with iElement.parent().controller('form').$name).

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

In this case there is no point in writing a compile function. Link is really what you want. The steps would be:

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

  1. Define a template that is completely devoid of angular directives.
  2. Define a link function that adds the various attributes
  3. REMOVE any angular directives that you might allow on your top-level element (the my-field directive). They have already been processed and this is a way to keep them from being double-processed.
  4. Finish by calling the compile SERVICE on your top-level element
  1. 定义一个完全没有角度指令的模板。
  2. 定义添加各种属性的链接函数
  3. 删除您可能允许在顶级元素上使用的任何角度指令(my-field 指令)。它们已经被处理过,这是一种防止它们被双重处理的方法。
  4. 通过在顶级元素上调用 compile SERVICE 来完成

Like so:

像这样:

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
    }
  };
});

You could, of course, compile the nested elements one-by-one to avoid having to worry about the duplicate processing of ng directives when you compile the top-level element again.

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

One final note on this scenario: I implied you'd be pushing the definition of the validations from a server, and in my example I've shown them as data already in the scope. I leave it as an exercise for the reader to figure out how one might deal with needing to pull that data from a REST API (hint: deferred compile).

关于此场景的最后一个说明:我暗示您将从服务器推送验证的定义,在我的示例中,我已将它们显示为范围内已有的数据。我把它留作练习,让读者弄清楚如何处理需要从 REST API 中提取数据的问题(提示:延迟编译)。

SCENARIO 3: two-way data binding via link

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

Of course the most common use of link is to simply hook up the two-way data binding via watch/apply. Most directives fall into this category, so it is adequately covered elsewhere.

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

回答by Matt Ball

From the docs:

从文档:

Compiler

Compiler is an angular service which traverses the DOM looking for attributes. The compilation process happens into two phases.

  1. Compile:traverse the DOM and collect all of the directives. The result is a linking function.

  2. Link:combine the directives with a scope and produce a live view. Any changes in the scope model are reflected in the view, and any user interactions with the view are reflected in the scope model. Making the scope model a single source of truth.

Some directives such ng-repeatclone DOM elements once for each item in collection. Having a compile and link phase improves performance since the cloned template only needs to be compiled once, and then linked once for each clone instance.

编译器

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

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

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

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

So at least in some cases, the two phases exist separately as an optimization.

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



From @UmurKontac?:

来自@UmurKontac?

If you are going to make DOM transformations, it should be compile. If you want to add some features that are behavior changes, it should be in link.

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

回答by SunnyShah

This is from Misko 's talk on directives. http://youtu.be/WqmeI5fZcho?t=16m23s

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

Think of the compiler function as the thing that works on a template and the thing that is allowed to change the template itself by, for example, adding a class to it or anything like that. But it's the linking function that actually does the work of binding the two together because the linking function has access to the scope and it's the linking function that executes once for each instantiation of the particular template. So the only kind of things you can placed inside of the compile functions are things that are common across all of the instances.

将编译器函数视为在模板上工作的东西,以及允许通过例如向模板添加类或类似的东西来更改模板本身的东西。但实际上是链接函数完成了将两者绑定在一起的工作,因为链接函数可以访问作用域,并且链接函数为特定模板的每个实例化执行一次。因此,您可以放置​​在 compile 函数中的唯一东西是所有实例中通用的东西。

回答by user203687

Little late to the thread. But, for the benefit of future readers:

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

I came across the following video which explains Compile and Link in Angular JS in a very great fashion:

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

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

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

It would not be pleasing to copy/type in all of the content here. I took a couple of screenshots from the video, which explain every stage of Compile and Link phases:

在这里复制/输入所有内容是不愉快的。我从视频中截取了几个屏幕截图,其中解释了编译和链接阶段的每个阶段:

Compile and Link in Angular JS

在 Angular JS 中编译和链接

Compile and Link in Angular JS - Nested Directives

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

The second screenshot is little bit confusing. But, if we follow the step numbering, it is quite straight forward.

第二个屏幕截图有点令人困惑。但是,如果我们按照步骤编号,则非常简单。

First cycle: "Compile" gets performed on all of the directives first.
Second cycle: "Controller" and "Pre-Link" gets performed (just one after another) Third cycle: "Post-Link" gets performed in reverse order (starting from innermost)

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

Following is the code, which demonstrates the above:

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

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>

UPDATE:

更新:

Part 2 of the same video is available here: https://www.youtube.com/watch?v=1M3LZ1cu7rwThe video explains more about how to modify DOM and handle events during Compile and Link process of Angular JS, in a simple example.

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

回答by pixelbits

Two Phases: Compile and Link

两个阶段:编译和链接

Compile:

编译:

Traverse the DOM tree looking for directives (elements / attributes / classes / comments). Each compilation of a directive may modify its template, or modify its contents which has not been compiled yet. Once a directive is matched, it returns a linking function, which is used in a later phase to link elements together. At the end of the compile phase, we have a list of compiled directives and their corresponding linking functions.

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

Link:

关联:

When an element is linked, the DOM tree is broken at its branch point in the DOM tree, and the contents are replaced by the compiled (and linked) instance of the template. The original displaced content is either discarded, or in the case of transclusion, re-linked back into the template. With transclusion, the two pieces are linked back together (kind of like a chain, with the template piece being in the middle). When the link function is called, the template has already been bound to a scope, and added as a child of the element. The link function is your opportunity to manipulate the DOM further and setup change listeners.

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

回答by Kfir Erez

This question is old by I would like to make short summary which may help:

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

  • Compile called once for all directive instance
  • Compile main purpose is to return/create the link (and possibly pre/post) function/object. You can also init stuff that are shared between instances of the directive.
  • In my opinion, "link" is a confusing name for this feature. I would prefer "pre-render".
  • link is called for each directive instance and its purpose is to prepare the rendering of the directive in the DOM.
  • 为所有指令实例调用一次编译
  • 编译的主要目的是返回/创建链接(可能还有前/后)函数/对象。您还可以初始化在指令实例之间共享的内容。
  • 在我看来,“链接”是此功能的一个令人困惑的名称。我更喜欢“预渲染”。
  • link 为每个指令实例调用,其目的是准备在 DOM 中呈现指令。