angular源码分析:$compile服务——directive他妈
一、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他妈的更多相关文章
- angular源码分析:angular中脏活累活的承担者之$interpolate
一.首先抛出两个问题 问题一:在angular中我们绑定数据最基本的方式是用两个大括号将$scope的变量包裹起来,那么如果想将大括号换成其他什么符号,比如换成[{与}],可不可以呢,如果可以在哪里配 ...
- Netty服务端的启动源码分析
ServerBootstrap的构造: public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, Serve ...
- angular源码分析:angular的整个加载流程
在前面,我们讲了angular的目录结构.JQLite以及依赖注入的实现,在这一期中我们将重点分析angular的整个框架的加载流程. 一.从源代码的编译顺序开始 下面是我们在目录结构哪一期理出的an ...
- angular源码分析:angular的源代码目录结构说明
一.读源码,是选择"编译合并后"的呢还是"编译前的"呢? 有朋友说,读angular源码,直接看编译后的,多好,不用管模块间的关系,从上往下读就好了.但是在我看 ...
- angular源码分析:angular中脏活累活承担者之$parse
我们在上一期中讲 $rootscope时,看到$rootscope是依赖$prase,其实不止是$rootscope,翻看angular的源码随便翻翻就可以发现很多地方是依赖于$parse的.而$pa ...
- angular源码分析:angular中$rootscope的实现——scope的一生
在angular中,$scope是一个关键的服务,可以被注入到controller中,注入其他服务却只能是$rootscope.scope是一个概念,是一个类,而$rootscope和被注入到cont ...
- angular源码阅读,依赖注入的原理:injector,provider,module之间的关系。
最开始使用angular的时候,总是觉得它的依赖注入方式非常神奇. 如果你跳槽的时候对新公司说,我曾经使用过angular,那他们肯定会问你angular的依赖注入原理是什么? 这篇博客其实是angu ...
- angular源码阅读的起点,setupModuleLoader方法
angular源码其实结构非常清晰,划分的有条有理的,大概就是这样子: (function(window,document,jquery,undefined){ //一些工具函数 //EXPR 编译器 ...
- angular源码分析:injector.js文件分析——angular中的依赖注入式如何实现的(续)
昨天晚上写完angular源码分析:angular中jqLite的实现--你可以丢掉jQuery了,给今天定了一个题angular源码分析:injector.js文件,以及angular的加载流程,但 ...
随机推荐
- Design7:数据删除设计
在设计一个新系统的Table Schema的时候,不仅需要满足业务逻辑的复杂需求,而且需要考虑如何设计schema才能更快的更新和查询数据,减少维护成本. 模拟一个场景,有如下Table Schema ...
- 【WP8.1开发】用手机来控制电脑的多媒体播放
为了用电脑看电影时方便控制,我就突发其想,做一个手机app来通过无线网络远程调节电脑上的音量.后来进行尝试成功后,我就想,光是调音量似乎单调了些,就把播放/暂停,上一首,下一首,等多媒体控制功能也加上 ...
- ImageView设置边框的两种方式
转载:http://www.2cto.com/kf/201308/239945.html package cc.testimageviewbounds; import android.os.Bundl ...
- AngularJS之Scope及Controller(一)
前言 之前有接触过基本的AngularJS,未过多涉及,于是乎本系列我们来着重讲讲AngularJS,对AngularJS中重要的几大内容进行梳理并进行一些小的例子的书写.在之前项目过程中用到过ava ...
- 从零开始编写自己的C#框架(4)——文档编写说明
在写本系列的过程中,了解得越多越不知道从哪里做为切入点来写,几乎每个知识点展开来说都可以写成一本书.而自己在写作与文档编写方面来说,还是一个初鸟级别,所以只能从大方面说说,在本框架开发所需的范围内来讲 ...
- iOS开发之远程推送
说到远程推送,应该用的也挺多的,今天就基于SEA的云推送服务,做一个推送的小demo,来了解一下iOS中的远程推送是怎么一回事儿,首先你得有苹果的开发者账号,好咸蛋也差不多了,主要内容走起. 一.准备 ...
- Oracle层次查询
Oracle层次查询的语法如下: 下面根据两道“烧脑”的题具体来体现: 1. 根据时间先后顺序,十二星座的英文名称用逗号串起来为'Aries,Taurus,Gemini,Cancer,Leo,Virg ...
- 拨乱反正:DDD 回归具体的业务场景,Domain Model 再再重新设计
首先,把最真挚的情感送与梅西,加油! 写在前面 阅读目录: 重申业务场景 Domain Model 设计 后记 上一篇<设计窘境:来自 Repository 的一丝线索,Domain Model ...
- Vagrant入门
简单地说,Vagrant让我们可以通过代码的方式快速地.可重复地创建针对不同虚拟环境的虚拟机,包括Virtualbox.AWS.Docker等.它使得我们可以一次性地.自动创建多个环境相同的虚拟机,对 ...
- ASP.NET MVC中获取URL地址参数的两种写法
一.url地址传参的第一种写法 1.通过mvc中默认的url地址书写格式:控制器/方法名/参数 2.实例:http://localhost:39270/RequestDemo/Index/88,默认参 ...