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

有人可以简单的解释吗?

该文件似乎有点迟钝。 我没有得到什么时候使用一个的本质和大局。 一个例子对比两个将是真棒。

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

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

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

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

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

但是,编译函数(如链接函数)可以访问属性。 所以如果你的DOM操作不需要实例范围,你可以使用一个编译函数。 以下是一个仅使用编译函数的指令的例子 。 它检查属性,但不需要实例范围来完成其工作。

这是一个也只使用编译函数的指令的例子 。 该指令只需要转换模板的DOM,所以可以使用编译function。

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

由于大多数指令都有链接function,我不打算提供任何示例 – 它们应该很容易find。

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

也可以看看

  • 定义指令时,“控制器”,“链接”和“编译”function之间的区别
  • 戴夫·史密斯(Dave Smith) 对指令的优秀的ng-conf 2104谈话 (链接到video的讨论编译和链接的部分)

我在这一两天的时间里把我的头靠在了墙上,我觉得更多的解释是为了。

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

就我们的目的而言,我将强调术语,这是令人困惑的:

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

编译function是一个指令中的一个代码,它在编译器SERVICE($ compile)的特定时间运行。

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

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

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

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

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

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

我会陈述你在这里看到的结果。 对于自定义指令tp和sp的DOM嵌套如下:

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

Angular编译SERVICE会调用:

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

jsbin代码也有tp post-link FUNCTION在第三个指令(up)上明确地调用了编译SERVICE,它在最后完成了所有的三个步骤。

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

情景1:作为一个MACRO指令

你想dynamic地添加一个指令(比如说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" ...> 

基本上,你想通过你的指令可以解释一些一致的模型结构来减less样板。 换句话说:你想要一个macros。

这对于编译阶段非常有用,因为您可以将所有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. 编译SERVICEfind我的领域
  2. 它调用指令的编译function,更新DOM。
  3. 编译SERVICE然后走进生成的DOM,并且COMPILES(recursion地)
  4. 编译SERVICE然后调用预先链接自上而下
  5. 编译SERVICE然后调用post-link BOTTOM UP,所以my-field的链接函数被称为AFTER内部节点已被链接。

在上面的例子中,不需要链接,因为指令的所有工作都是在编译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()创build它们),那么最终结果几乎是和以前一样(虽然你可能会重复一些工作)。 但是,如果元素上有其他指令,则只会导致这些元素被重新处理,这会导致各种不规则的行为(例如事件和手表的双重注册)。

因此,编译阶段是macros观工作的一个更好的select。

情景2:通过范围数据进行DOMconfiguration

这个从上面的例子来看。 假设您在操作DOM时需要访问范围。 那么在这种情况下,编译部分对你来说是无用的,因为它在一个范围可用之前就发生了。

因此,假设您想要validation一个input,但是您希望从服务器端ORM类(DRY)导出validation,并让它们自动应用并为这些validation生成适当的客户端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来显示各种validation错误:

 <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> ... 

在这种情况下,您肯定需要访问范围(因为这是您的validation存储的地方),并且将不得不手动编译添加,再次注意不要进行双重编译。 (作为一个附注,你需要在包含的表单标签上设置一个名字(我假设这里是表单),并且可以通过iElement.parent().control('form')。$ name) 。

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

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

关于这个场景的最后一个注意:我暗示你会推动从服务器validation的定义,在我的例子中,我已经把它们显示为已经在范围内的数据。 我将其作为练习,让读者了解如何处理需要从REST API中提取数据(提示:延迟编译)。

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

当然,链接最常见的用途是通过watch / apply简单地连接双向数据绑定。 大多数指令都属于这个类别,所以在其他地方已经有了充分的说明。

从文档:

编译器

编译器是一个遍历DOM查找属性的angular度服务。 编辑过程分为两个阶段。

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

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

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

所以至less在某些情况下, 这两个阶段是分开存在的。


来自@UmurKontacı :

如果你打算做DOM转换,它应该被compile 。 如果你想添加一些行为改变的function,它应该在link

在心脏Angular框架是一个parsing器。 parsingAngular指令和render的HTML输出的parsing器。

有angular度的parsing器有3个步骤:

  • 第1步: – HTML浏览器分析HTML并创build一个DOM(文档对象模型)。

  • 第2步: – 在这个DOM上运行的angular度框架查看Angular指令并相应地操纵DOM。

  • 第3步: – 这个操纵,然后在浏览器呈现为HTML。

在这里输入图像描述

现在上面的angular度parsing并不像看起来那么简单。 它发生在两个阶段“编译”和“链接” 。 首先是编译阶段,然后是链接阶段。

在这里输入图像描述

编译阶段 ,angular度parsing器开始parsingDOM,每当parsing器遇到一个指令,就会创build一个函数。 这些函数被称为模板或编译函数。 在这个阶段,我们不能访问$ scope数据。

链接阶段 ,数据ie($ scope)被附加到模板函数并被执行以获得最终的HTML输出。

在这里输入图像描述

这是米斯科关于指令的谈话。 http://youtu.be/WqmeI5fZcho?t=16m23s

把编译器的function看作是在模板上工作的东西,也就是允许通过添加一个类或类似的东西来改变模板本身的东西。 但是链接函数实际上是将两者绑定在一起的链接函数,因为链接函数可以访问范围,并且链接函数对于特定模板的每个实例都执行一次。 所以在编译函数中可以放置的唯一东西就是所有实例中通用的东西。

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

我遇到了以下非常棒的video,它解释了Angular JS中的编译和链接:

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

在这里复制/input所有的内容并不令人满意。 我从video中截取了几个截图,解释了编译和链接阶段的每个阶段:

编译和链接在角JS

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

第二个截图有点混乱。 但是,如果我们按照步骤编号,这是非常简单的。

第一个循环:首先在所有指令上执行“编译”。
第二个循环:“Controller”和“Pre-Link”被执行(只是一个接一个)第三个循环:“Post-Link”以相反的顺序执行(从最里面开始)

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

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

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

 }]);

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

        编译:函数(tElement,tAttributes){ 
             console.log(tAttributes.text +“-In compile ..”);
            返回{

                 pre:函数(范围,iElement,iAttributes,控制器){
                     console.log(iAttributes.text +“-In pre ..”);
                 },

                 post:function(scope,iElement,iAttributes,controller){
                     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> 

更新:

同一个video的第2部分可以在这里find: https : //www.youtube.com/watch?v=1M3LZ1cu7rw这个video解释了更多关于在Angular JS的Compile and Link过程中如何修改DOM和处理事件的一个简单例子。

两个阶段:编译和链接

编译:

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

链接:

当一个元素被链接时,DOM树在DOM树中的分支点处被破坏,并且内容被模板的编译(和链接)实例取代。 原始的stream离失所的内容要么被丢弃,要么在被包含的情况下被重新链接回模板。 通过跨越,两个部分链接在一起(有点像一个链,模板部分在中间)。 当链接函数被调用时,模板已经被绑定到一个作用域,并作为元素的一个子对象被添加。 链接function是您进一步操作DOM并设置更改侦听器的机会。

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

  • 为所有指令实例编译一次
  • 编译的主要目的是返回/创build链接(也可能是前/后)函数/对象。 你也可以初始化指令实例之间共享的东西。
  • 在我看来,“链接”是这个function的一个令人困惑的名字。 我宁愿“预渲染”。
  • 链接是为每个指令实例调用的,其目的是在DOM中准备指令的呈现。