说到Angularjs directive即指令,可以这么说Angularjs的灵魂就是指令,学会Angularjs指令那么你的Angularjs的武功就修炼了一半了,当然这只是鄙人的一点点独到见解,废话不多说,以下是Angularjs 指令的运用及源码实现解读。

学习Angularjs directive那么我们要带3个问题来学习:

1. 什么是angular 指令?
2. 指令如何定义存储的?
3. 指令如何编译运行的?

1.首先第一点解读什么是指令:

At a high level, directives are markers on a DOM element (such as an attribute, element name, comment or CSS class) that tell AngularJS's HTML compiler ($compile) to attach a specified behavior to that DOM element or even transform the DOM element and its children.(官网是这么说的)

指令就是一些附加在HTML元素上的自定义标记(例如:属性,元素,或css类),它告诉AngularJS的HTML编译器 ($compile) 在元素上附加某些指定的行为,甚至操作DOM、改变DOM元素,以及它的各级子节点。(这是鄙人通过解读官网定义的个人见解)

从中可以看出指令就是自定义的html标签用来解决反复的的CRUD(创建 读取 更改 删除)操作来修改DOM,这里小伙伴们如果想深入学习Angularjs操作DOM,那么小伙伴们首先需要了解D1、D2、以及D3实现了什么以及结构的优化有什么。再来学习Angularjs就会一步冲天!

好第一点我们知道了概念那么我们看看具体怎么使用Angularjs 指令吧:

指令实例:

<div ng-controller="Controller">
  Hello <input ng-model='name'> <hr/>
  <span self-app></span> <br/>
  <span self:app></span> <br/>
  <span self_app></span> <br/>
  <span data-self-app></span> <br/>
  <span x-self-app></span> <br/>
</div>

在这里可以看出如果指令注册为Attribute(属性)那么有5中合法的调用方式:

第一种以_来调用指

第二种以:来调用指令

第三种以-来调用指令

第四种以data-XXX-XXX来调用指令

第五种以x-XXX-XXX来调用指令

2.第二点解读指令如何定义存储的:

指令通过

angular.module('selfApp', [])
.directive('myDirective', function() {
// 返回一个对象(暂且称之为指令对象)
  return {
    restrict: 'A',//E A M C
    replace: true,
    scope: true,
    template: '<span>hello world</span>',
    compile: function (tElement) {
      console.log('complile: ', tElement);
      return function (scope, elem) {
        console.log('1');
      }

}

}

});

这里我们可以看出来通过我们事先注册的module来第一了一个指令,我们如果想了解指令如何存储的 那么就要看看源代码了,

directive: invokeLaterAndSetModuleName('$compileProvider', 'directive')//angular.js 2172行

这是angularjs module的内部方法,

//延迟调用并设置模块名称

function invokeLaterAndSetModuleName(provider, method) {
  return function(recipeName, factoryFunction) {

//给工厂函数的module变量命名即给模块命名
    if (factoryFunction && isFunction(factoryFunction)) factoryFunction.$$moduleName = name;

//把我们注册的指令存储到调用队列中
      invokeQueue.push([provider, method, arguments]);

//返回我们的模块实例
    return moduleInstance;
  };
}

这里我们可以知道其实我们注册一个指令其实只是把我们的指令存储到了调用队列中等待调用。

3. 第三点解读指令如何编译运行的?

其实调用的时候就是编译Angularjs directive的时候也就是angularjs自己的编译机制,会使用$CompileProvider的directive方法来编译指令,compileProvider方法如下:

//这里其实是吧$CompileProvide注册为一个服务来调用

$CompileProvider.$inject = ['$provide', '$$sanitizeUriProvider'];//angular.js 6919行
function $CompileProvider($provide, $$sanitizeUriProvider) {

好了重头戏来了

// 指令存储容器,一个指令可以有多个指令工厂函数(即多个指令对象)

this.directive = function registerDirective(name, directiveFactory) {

assertNotHasOwnProperty(name, 'directive');

//这里不用说了吧检验指令名称是否为字符串类型
  if (isString(name)) {

//还是检验指令名称是否合法
    assertValidDirectiveName(name);

//构造一个指令工厂
    assertArg(directiveFactory, 'directiveFactory');

// 检验该指令是否有指令工厂 如果没有就继续
    if (!hasDirectives.hasOwnProperty(name)) {
      hasDirectives[name] = [];

$provide.factory(name + Suffix, ['$injector', '$exceptionHandler',
        function($injector, $exceptionHandler) {

// 定义一个指令工厂集合
          var directives = [];

// 循环遍历指令工厂集合,并收集每个工厂函数返回的指令对象
          forEach(hasDirectives[name], function(directiveFactory, index) {
            try {

// 调用工厂函数,这里用的是$injector,所以工厂函数也可以是一个拥有依赖注入的函数或数组
              var directive = $injector.invoke(directiveFactory);

//检查指令是否是一个函数 即我们之前定义的指令其实是一个函数
              if (isFunction(directive)) {
                directive = { compile: valueFn(directive) };

//如果指令中存在compile函数就不运行link函数
              } else if (!directive.compile && directive.link) {
                directive.compile = valueFn(directive.link);
              }
              directive.priority = directive.priority || 0;
              directive.index = index;
              directive.name = directive.name || name;
              directive.require = directive.require || (directive.controller && directive.name);
              directive.restrict = directive.restrict || 'EA';
              var bindings = directive.$$bindings =
              parseDirectiveBindings(directive, directive.name);
              if (isObject(bindings.isolateScope)) {
                directive.$$isolateBindings = bindings.isolateScope;
              }
              directive.$$moduleName = directiveFactory.$$moduleName;
              directives.push(directive);
            } catch (e) {
            $exceptionHandler(e);
          }
        });
        return directives;
      }]);
    }

// 存储当前指令工厂
  hasDirectives[name].push(directiveFactory);
} else {
  forEach(name, reverseParams(registerDirective));
  }

// 最后提供链式调用
  return this;
};

好了源代码解读完成,大家是不是对指令有了一个系统的概念,而且也知道angularjs directive是怎么运行下来的了,那么总结一下:

1. 注册指令时,注册的是工厂函数(支持依赖注入),它负责返回指令对象
2. 一个指令可以注册多个工厂函数,就意味着将对应多个指令对象(即指令对象集合),其实多个指令对象之间是有一些冲突的,比如只能拥有有一个模板,拥有一个孤立作用域等
3. 一个指令对应的指令对象集合是通过注册为服务的方式被外界获取的

好,下面介绍定义一个指令时内部参数的解读:(作为一名专业的it工作者,做戏就要做全套!)

name -  当前scope的名称,注册时可以使用默认值(不填)。

priority - (优先级)- 当有多个directive定义在同一个DOM元素时,有时需要明确它们的执行顺序。这属性用于在directive的compile function调用之前进行排序。如果优先级相同,则执行顺序是                不确定的(经初步试验,优先级高的先执行,同级时按照类似栈的“后绑定先执行”。另外,测试时有点不小心,在定义directive的时候,两次定义了一个相同名称的directive,但执行结                  果发现,两个compile或者link function都会执行)。

terminal - (最后一组起作用)

- 如果设置为”true”,则表示当前的priority将会成为最后一组执行的directive。任何directive与当前的优先级相同的话,他们依然会执行,但顺序是不确定的(虽然顺序不确定,但基本上                   与priority的顺序一致。当前优先级执行完毕后,更低优先级的将不会再执行)。

scope -  true - 将为这个directive创建一个新的scope。如果在同一个元素中有多个directive需要新的scope的话,它还是只会创建一个scope。新的作用域规则不适用于根模版(root of the                           template),因此根模版往往会获得一个新的scope。这个scope是一个新的、独立(isolate)的scope。”isolate” scope与一般的scope的区别在于它不是通过原型继承于父scope的。这对                 于创建可复用的组件是很有帮助的,可以有效防止读取或者修改父级scope的数据。这个独立的scope会创建一个拥有一组来源于父scope的本地scope属性(local scope properties)的                     object hash。这些local properties对于为模版创建值的别名很有帮助(useful for aliasing values for templates!)。本地的定义是对其来源的一组本地scope property的hash映射(Locals                  definition is a hash of local scope property to its source))():

@或@attr - 建立一个local scope property到DOM属性的绑定。因为属性值总是String类型,所以这个值总是返回一个字符串。如果没有通过@attr指定属性名称,那么本地名称将与                                          DOM属性的名称一直。例如<widget my-attr='hello {{name}}'>,widget的scope定义为:{localName:’@myAttr’}。那么,widget scope property的localName会映射                                                 出”hello {{name}}"转换后的真实值。name属性值改变后,widget scope的localName属性也会相应地改变(仅仅单向,与下面的”=”不同)。name属性是在父scope读取                                         的(不是组件scope)

=或=expression(这里也许是attr) - 在本地scope属性与parent scope属性之间设置双向的绑定。如果没有指定attr名称,那么本地名称将与属性名称一致。例如<widget my-                                                   attr=”parentModel”>,widget定义的scope为:{localModel:’=myAttr’},那么widget scope property “localName”将会映射父scope的“parentModel”。如果parentModel发生                                       任何改变,localModel也会发生改变,反之亦然。(双向绑定)

&或&attr - 提供一个在父scope上下文中执行一个表达式的途径。如果没有指定attr的名称,那么local name将与属性名称一致。例如<widget my-attr=”count = count + value”>,                                             widget的scope定义为:{localFn:’increment()’},那么isolate scope property “localFn”会指向一个包裹着increment()表达式的function。一般来说,我们希望通过一个表达                                         式,将数据从isolate scope传到parent scope中。这可以通过传送一个本地变量键值的映射到表达式的wrapper函数中来完成。例如,如果表达式是increment(amount),那                                     么我们可以通过localFn({amount:22})的方式调用localFn以指定amount的值。

controller - controller 构造函数。controller会在pre-linking步骤之前进行初始化,并允许其他directive通过指定名称的require进行共享(看下面的require属性)。这将允许directive之间相互沟通,增强相互之间的行为。controller默认注入了以下本地对象到link:

$scope - 与当前元素结合的scope
    $element - 当前的元素
    $attrs - 当前元素的属性对象
    $transclude - 一个预先绑定到当前转置scope的转置linking function :function(cloneLinkingFn)。(A transclude;linking function pre-bound to the correct transclusion scope)

require - 请求另外的controller,传入当前directive的linking function中。require需要传入一个directive controller的名称。如果找不到这个名称对应的controller,那么将会抛出一个error。名称可以                加入以下前缀:

? - 不要抛出异常。这使这个依赖变为一个可选项。<br />
                                  ^ - 允许查找父元素的controller<br /><br />

restrict - EACM的子集的字符串,它限制directive为指定的声明方式。如果省略的话,directive将仅仅允许通过属性声明:E - 元素名称 A - 属性名 C - class名 M - 注释

template - 如果replace 为true,则将模版内容替换当前的HTML元素,并将原来元素的属性、class一并迁移;如果为false,则将模版元素当作当前元素的子元素处理。

templateUrl - 与template基本一致,但模版通过指定的url进行加载。因为模版加载是异步的,所以compilation、linking都会暂停,等待加载完毕后再执行。

replace - 如果设置为true,那么模版将会替换当前元素,而不是作为子元素添加到当前元素中。(注:为true时,模版必须有一个根节点)

transclude - 编译元素的内容,使它能够被directive所用。需要(在模版中)配合ngTransclude使用(引用)。transclusion的优点是linking function能够得到一个预先与当前scope绑定的transclusion                        function。一般地,建立一个widget,创建isolate scope,transclusion不是子级的,而是isolate scope的兄弟。这将使得widget拥有私有的状态,transclusion会被绑定到父级                                (preisolate)scope中。

true - 转换这个directive的内容。

                  ‘element’ - 转换整个元素,包括其他优先级较低的directive。(像将整体内容编译后,当作一个整体(外面再包裹p),插入到指定地方)

compile - compile function

link - link function,这个属性仅仅是在compile属性没有定义的情况下使用。

好了,以上内容就是鄙人的一些见解,望大家能够更好的了解angularjs directive的使用及原理。转载请注明出处!谢谢

转载于:https://www.cnblogs.com/houMing/p/5132655.html

Directive全面分析相关推荐

  1. Debug 集子[更新中]

    文章目录 一.错误:'start' 的存储大小未知 二.warning: braces around scalar initializer 三.excess elements in array ini ...

  2. Redis源码分析:服务器端处理过程

    服务器端处理过程 在前面我们大致分析了Redis的服务器端的启动流程,Redis服务端主要就是依据单线程的反应器模式来设计的,并且在处理的事件过程中主要分为时间事件和连接响应事件.本文就根据客户端发送 ...

  3. Petshop3.0学习笔记(二)Global.asax文档分析

    Global.asax文档和asp中的Global.asa文件的功能是一样的,都是用来宿主Application对象的事件的处理处理程序,然asp.net中的这个文件在内容上却和asp中的差别巨大,不 ...

  4. Vue.js 源码分析(二十三) 指令篇 v-show指令详解

    v-show的作用是将表达式值转换为布尔值,根据该布尔值的真假来显示/隐藏切换元素,它是通过切换元素的display这个css属性值来实现的,例如: <!DOCTYPE html> < ...

  5. 代码分析:NASM源码阅读笔记

    NASM源码阅读笔记 NASM(Netwide Assembler)的使用文档和代码间的注释相当齐全,这给阅读源码 提供了很大的方便.按作者的说法,这是一个模块化的,可重用的x86汇编器, 而且能够被 ...

  6. Redis源码简要分析

    在文章的开头我们把所有服务端文件列出来,并且标示出其作用: adlist.c //双向链表 ae.c //事件驱动 ae_epoll.c //epoll接口, linux用 ae_kqueue.c / ...

  7. Linux/Centos7系统管理之深入理解Linux文件系统与日志分析

    前言:inode(文件节点)与block(数据块)硬链接与软连接恢复误删除的文件 (即rm-rf 的操作,可以先进行备份的操作,然后可以进行恢复ext4和xfs文件系统皆可)日志文件的分类用户日志与程 ...

  8. 复工后一次百万长连接压测Nginx与OOM的问题排查分析,我裂开了!

    在最近的一次百万长连接压测中,32C 128G 的四台 Nginx 频繁出现 OOM,出现问题时的内存监控如下所示. 排查的过程记录如下. 现象描述 这是一个 websocket 百万长连接收发消息的 ...

  9. Nginx 源码分析-- 模块module 解析执行 nginx.conf 配置文件流程分析 一

    搭建nginx服务器时,主要的配置文件 nginx.conf 是部署和维护服务器人员经常要使用到的文件, 里面进行了许多服务器参数的设置.那么nginx 以模块 module为骨架的设计下是如何运用模 ...

最新文章

  1. 解决:【异常】Caused by: java.lang.IllegalStateException: Zip64 archives are not supported
  2. Haunt - Youzan 服务发现 概述
  3. erlang精要(4)-等于与不等于
  4. Uuntu16.04重装后u盘不识别问题解决
  5. 牛腩新闻发布系统(一):SQLHelper重构(一)
  6. 右下角出现测试模式 win7 内部版本7601如何去掉
  7. 最近在整理和准备发布
  8. 解决RStudio(非conda安装)在使用Anaconda中的R环境时,缺失“ libbz2-1.dll ”而不能正常启动问题
  9. 田彩蝶(帮别人名字作诗)
  10. Go 原生插件使用问题全解析
  11. 微信开发流程总结(基于微信平台)
  12. Win8 RP微软原版光盘镜像下载大全(含中文版)
  13. T细胞培养方法进展及方法学对比
  14. ElasticSearch.bat 文件闪退 解决
  15. Linux svn 版本回滚的方法
  16. 安装pytorch报错torch.cuda.is_available()=false的解决方法
  17. (翻译)表单中应使用文本域输入地址的原因
  18. marquee标签_html滚动文字
  19. Prolific PL2303 usb 转串口Win8 Win8.1驱动
  20. C语言---指针笔试题

热门文章

  1. mysql存储引擎6_Mysql各种存储引擎对比总结
  2. jenkins部署web项目
  3. 推荐一个提供干货的java公众号
  4. array用法 numpy_关于Numpy Array的使用技巧整理
  5. iOS多线程中performSelector: 和dispatch_time的不同
  6. 从数据类型 nvarchar 转换为 bigint 时出错_JavaScript数据类型的一些细节点
  7. 软件测试之功能测试详细过程
  8. 计算机 时代,计算机时代,你准备好了吗
  9. php+tcpdf+表格,PHP中使用TCPDF生成PDF文档实例
  10. 运维企业专题(10)RHCS集群工具——FENCE搭建、高可用服务配置详解