一、directive的注册

1.我们知道,我们可以通过类似下面的代码定义一个指令(directive)。

var myModule = angular.module(...);

  myModule.directive('directiveName', function factory(injectables) {
var directiveDefinitionObject = {
priority: 0,
template: '<div></div>', // or // function(tElement, tAttrs) { ... },
// or
// templateUrl: 'directive.html', // or // function(tElement, tAttrs) { ... },
transclude: false,
restrict: 'A',
scope: false,
controller: function($scope, $element, $attrs, $transclude, otherInjectables) { ... },
controllerAs: 'stringAlias',
require: 'siblingDirectiveName', // or // ['^parentDirectiveName', '?optionalDirectiveName', '?^optionalParent'],
compile: function compile(tElement, tAttrs, transclude) {
return {
pre: function preLink(scope, iElement, iAttrs, controller) { ... },
post: function postLink(scope, iElement, iAttrs, controller) { ... }
}
// or
// return function postLink( ... ) { ... }
},
// or
// link: {
// pre: function preLink(scope, iElement, iAttrs, controller) { ... },
// post: function postLink(scope, iElement, iAttrs, controller) { ... }
// }
// or
// link: function postLink( ... ) { ... }
};
return directiveDefinitionObject;
});

通过前面的分析(directive: invokeLater('$compileProvider', 'directive')),我们可以知道上面的代码会最终调用$compileProvider.directive

2.$compileProvider.directive


var hasDirectives = {},//定义用于存储指令的对象
Suffix = 'Directive',
COMMENT_DIRECTIVE_REGEXP = /^\s*directive\:\s*([\w\-]+)\s+(.*)$/,
CLASS_DIRECTIVE_REGEXP = /(([\w\-]+)(?:\:([^;]+))?;?)/,
ALL_OR_NOTHING_ATTRS = makeMap('ngSrc,ngSrcset,src,srcset'),
REQUIRE_PREFIX_REGEXP = /^(?:(\^\^?)?(\?)?(\^\^?)?)?/;
/**
* @ngdoc method
* @name $compileProvider#directive
* @kind function
*
* @description
* Register a new directive with the compiler.
*
* @param {string|Object} name Name of the directive in camel-case (i.e. <code>ngBind</code> which
* will match as <code>ng-bind</code>), or an object map of directives where the keys are the
* names and the values are the factories.
* @param {Function|Array} directiveFactory An injectable directive factory function. See
* {@link guide/directive} for more info.
* @returns {ng.$compileProvider} Self for chaining.
*/
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',//这里在定义指令的服务,比如注册了`test`这样的一个指令,这里就会定义个`testDirective`的服务.
function($injector, $exceptionHandler) {//注意这个匿名函数,将在需要获取指令的是否被执行,所以当这个函数执行时,所有的指令的定义都放入了hasDirectives数组
var directives = [];
forEach(hasDirectives[name], function(directiveFactory, index) {//注意这里的directiveFactory是从数组中取出的元素而不是前面函数的参数
try {
var directive = $injector.invoke(directiveFactory);
if (isFunction(directive)) {
directive = { compile: valueFn(directive) };
} 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);//这个parseDirectiveBindings需要分析
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;
};

请注意代码的执行数序。

a.在第一注册某个执行时(比如现在注册了两个test执行),那么第一次调用这个函数注册指令时,会定一个testDirective的服务,且将该指令的工厂函数压入hasDirectives['test']

b.当再次注册一个与test同名的另一个指令时,仅是将其工厂函数压入hasDirectives['test']

c.当指令需要时,框架会调用testDirectiveProvider.$get(也就是testDirective的工厂方法)制造一个directives数组

3.parseDirectiveBindings

  function parseIsolateBindings(scope, directiveName, isController) {
var LOCAL_REGEXP = /^\s*([@&]|=(\*?))(\??)\s*(\w*)\s*$/; var bindings = {}; forEach(scope, function(definition, scopeName) {
var match = definition.match(LOCAL_REGEXP); if (!match) {
throw $compileMinErr('iscp',
"Invalid {3} for directive '{0}'." +
" Definition: {... {1}: '{2}' ...}",
directiveName, scopeName, definition,
(isController ? "controller bindings definition" :
"isolate scope definition"));
} bindings[scopeName] = {
mode: match[1][0],
collection: match[2] === '*',
optional: match[3] === '?',
attrName: match[4] || scopeName
};
}); return bindings;
} function parseDirectiveBindings(directive, directiveName) {
var bindings = {
isolateScope: null,
bindToController: null
};
if (isObject(directive.scope)) {//指令对象中scope==true 或者 scope是一个对象时
if (directive.bindToController === true) {
bindings.bindToController = parseIsolateBindings(directive.scope, //解析scope对象中的表达式
directiveName, true);
bindings.isolateScope = {};
} else {
bindings.isolateScope = parseIsolateBindings(directive.scope,
directiveName, false);
}
}
if (isObject(directive.bindToController)) {
bindings.bindToController =
parseIsolateBindings(directive.bindToController, directiveName, true);
}
if (isObject(bindings.bindToController)) {
var controller = directive.controller;
var controllerAs = directive.controllerAs;
if (!controller) {
// There is no controller, there may or may not be a controllerAs property
throw $compileMinErr('noctrl',
"Cannot bind to controller without directive '{0}'s controller.",
directiveName);
} else if (!identifierForController(controller, controllerAs)) {
// There is a controller, but no identifier or controllerAs property
throw $compileMinErr('noident',
"Cannot bind to controller without identifier for directive '{0}'.",
directiveName);
}
}
return bindings;
}

二、给出一幅图说明angular的"编译原理"


1.在定义或者注册指令,最终是以延迟调用$compileProvider.Directive来完成
2.编译阶段,主要工作是收集dom元素上引用到的指令,编译函数将返回一个"链接函数"用户完成和$scope的链接.
3.链接过程,将$scope与dom建立联系,指令指令中定义的link函数

由于$compile这部分的代码过于复杂,本期暂且讲到这里,下期继续

上一期:angular源码分析:angular中脏活累活的承担者之$interpolate

下一期:angular源码分析:$compile服务——指令的编写

angular源码分析:$compile服务——directive他妈的更多相关文章

  1. angular源码分析:angular中脏活累活的承担者之$interpolate

    一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...

  2. Netty服务端的启动源码分析

    ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...

  3. angular源码分析:angular的整个加载流程

    在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...

  4. angular源码分析:angular的源代码目录结构说明

    一.读源码,是选择"编译合并后"的呢还是"编译前的"呢? 有朋友说,读angular源码,直接看编译后的,多好,不用管模块间的关系,从上往下读就好了.但是在我看 ...

  5. angular源码分析:angular中脏活累活承担者之$parse

    我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...

  6. angular源码分析:angular中$rootscope的实现——scope的一生

    在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...

  7. angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。

    最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇. 如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么? 这篇博客其实是angu ...

  8. angular源码阅读的起点,setupModuleLoader方法

    angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 ...

  9. angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)

    昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...

随机推荐

  1. sublime text3同时编辑多行

    sublime text3同时编辑多行 下面的才是正确的姿势,之前一直是shift + 右键 拖啊. http://stackoverflow.com/questions/32127604/how-t ...

  2. 利用免费cdn加速webpack单页应用

    回顾现状 在之前的学习过程中,react单页应用经过webpack打包之后会输出大概如下的目录结构,它就是站点的所有前端组成了:   1 2 3 4 5 6 MacBook-Pro:output ba ...

  3. Segmentation fault (core dumped)

    问题: 在Linux中实现快速排序时,没有加入终止条件,结果出现Segmentation fault (core dumped)这个问题,Segmentation fault (core dumped ...

  4. 【原创】开源Math.NET基础数学类库使用(12)C#随机数扩展方法

                   本博客所有文章分类的总目录:[总目录]本博客博文总目录-实时更新  开源Math.NET基础数学类库使用总目录:[目录]开源Math.NET基础数学类库使用总目录 前言 ...

  5. ES6 - Note4:Class类

    1.Class类的介绍 在ES6中新增了Class类的概念,让语法看起来更像是面向对象编程,其实这可以说是一个语法糖,ES5可以做到Class绝大部分功能,但也有一些不同.在ES6以前,可以通过构造函 ...

  6. position:fixed和scroll实现div浮动【示例】

    前言 在自己建站的过程中,要实现一个div随滚动条浮动的效果,网上找了些示例不太好用,还是自己动手,丰衣足食,写的不好请大家谅解,毕竟我不是搞前端的,因为自己建站毕竟每一步都必须自己来,这边只是做个记 ...

  7. ModelState.IsValid总为false原因

    总结在开发中遇到的一个问题 ModelState.IsValid 一直是false 且在局部变量中,没有发现有问题啊,Model非常正常有木有,可是为什么 ModelState.IsValid 总是f ...

  8. 为SharePoint Server 2013 建立搜索爬行

    当搭建好SharePoint Server 2013 之后系统内的爬行信息是空白的,因此需要进行搜索爬行行为的开启. 确认系统上的服务 首先进入到系统的服务中services.msc 确保如下的Sha ...

  9. 通过SQL Server自定义数据类型实现导入数据

    写在前面 在看同事写的代码时看到了SQL Server中可以自定义数据类型,而且定义的是DataTable类型的数据类型. 后我想起了以前我们导入数据时要么是循环insert写入,要么是SqlBulk ...

  10. Centos 7 开启端口

    CentOS 7 默认没有使用iptables,所以通过编辑iptables的配置文件来开启80端口是不可以的 CentOS 7 采用了 firewalld 防火墙 如要查询是否开启80端口则: 1 ...