Compile Template
1. template
template的配置
代码语言:javascript复制{
template: '<div>22222</div>',
replace: true, //false
link: function () {
...
}
}
对于template的处理主要集中在applyDirectivesToNode()中对于directives数组循环compile目标node,处于compile阶段
代码语言:javascript复制if (directive.template) {
hasTemplate = true;
// 不允许同级存在多个directive配置了template
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
// template可以为function,来执行返回string,最终需要一个string的字符模板
directiveValue = (isFunction(directive.template))
? directive.template($compileNode, templateAttrs)
: directive.template;
// 转化{{ or }}为自定义符号
directiveValue = denormalizeTemplate(directiveValue);
// 是否整体替换
if (directive.replace) {
replaceDirective = directive;
if (jqLiteIsTextNode(directiveValue)) {
$template = [];
} else {
$template = jqLite(wrapTemplate(directive.type, trim(directiveValue)));
}
// 当template不是element而是直接textNode的,这儿会报错!
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== 1) {
throw $compileMinErr('tplrt',
"Template for directive '{0}' must have exactly one root element. {1}",
directiveName, '');
}
// 将$compileNode替换成compileNode即模板node
replaceWith(jqCollection, $compileNode, compileNode);
var newTemplateAttrs = {$attr: {}};
// 对于替换好的模板进行directive的收集
var templateDirectives = collectDirectives(compileNode, [], newTemplateAttrs);
// 分离出还没有编译的directives
var unprocessedDirectives = directives.splice(i 1, directives.length - (i 1));
if (newIsolateScopeDirective) {
// 给每个templateDirectives做好isolateScope的标记
markDirectivesAsIsolate(templateDirectives);
}
// 替换好的node需要继续编译,而继续编译的directive由之前没有编译完的directive和新收集的directive组成
// directives = unprocessedDirectives templateDirectives
directives = directives.concat(templateDirectives).concat(unprocessedDirectives);
mergeTemplateAttributes(templateAttrs, newTemplateAttrs);
ii = directives.length;
} else {
$compileNode.html(directiveValue);
}
}
2. templateUrl
templateUrl配置
代码语言:javascript复制{
templateUrl: '/user.html',
replace: true, //false
link: function () {
...
}
}
入口方法源码如下:
代码语言:javascript复制if (directive.templateUrl) {
hasTemplate = true;
assertNoDuplicate('template', templateDirective, directive, $compileNode);
templateDirective = directive;
if (directive.replace) {
replaceDirective = directive;
}
nodeLinkFn = compileTemplateUrl(directives.splice(i, directives.length - i), $compileNode,
templateAttrs, jqCollection, hasTranscludeDirective && childTranscludeFn, preLinkFns, postLinkFns, {
controllerDirectives: controllerDirectives,
newIsolateScopeDirective: newIsolateScopeDirective,
templateDirective: templateDirective,
nonTlbTranscludeDirective: nonTlbTranscludeDirective
});
// 这里很重要!表示后续的directives都不需要执行了,改在compileTemplateUrl中异步执行
ii = directives.length;
} else if (directive.compile) {
// ...
}
主要方法compileTemplateUrl()包含所有细节
代码语言:javascript复制function compileTemplateUrl(directives, $compileNode, tAttrs,
$rootElement, childTranscludeFn, preLinkFns, postLinkFns, previousCompileContext) {
var linkQueue = [],
afterTemplateNodeLinkFn,
afterTemplateChildLinkFn,
beforeTemplateCompileNode = $compileNode[0],
origAsyncDirective = directives.shift(),
// The fact that we have to copy and patch the directive seems wrong!
derivedSyncDirective = extend({}, origAsyncDirective, {
templateUrl: null, transclude: null, replace: null, $$originalDirective: origAsyncDirective
}),
templateUrl = (isFunction(origAsyncDirective.templateUrl))
? origAsyncDirective.templateUrl($compileNode, tAttrs)
: origAsyncDirective.templateUrl,
type = origAsyncDirective.type;
// 清空$compileNode
$compileNode.empty();
// 异步请求模板
$http.get($sce.getTrustedResourceUrl(templateUrl), {cache: $templateCache}).
success(function(content) {
// ...
}).
error(function(response, code, headers, config) {
throw $compileMinErr('tpload', 'Failed to load template: {0}', config.url);
});
// 返回异步延迟方法
return function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
// ...
};
}
主要两块
- 异步请求模板然后处理模板
- 返回延迟执行的方法
从简单的开始--2.返回的延迟方法
原理很简单,就是将调用的参数用queue存起来,等请求完了再去queue里获取执行
代码语言:javascript复制 function delayedNodeLinkFn(ignoreChildLinkFn, scope, node, rootElement, boundTranscludeFn) {
var childBoundTranscludeFn = boundTranscludeFn;
if (linkQueue) {
// 保存各种参数
linkQueue.push(scope);
linkQueue.push(node);
linkQueue.push(rootElement);
linkQueue.push(childBoundTranscludeFn);
}
// 当这个模板已经请求回来并且编译(compile)过就直接执行不需要在queue里缓存等待执行
else {
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
}
// 执行nodeLinkFn
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, node, rootElement, childBoundTranscludeFn);
}
};
1. 异步请求模板然后处理模板
代码语言:javascript复制var compileNode, tempTemplateAttrs, $template, childBoundTranscludeFn;
content = denormalizeTemplate(content);
// 是否整体替换
if (origAsyncDirective.replace) {
if (jqLiteIsTextNode(content)) {
$template = [];
} else {
$template = jqLite(wrapTemplate(type, trim(content)));
}
compileNode = $template[0];
if ($template.length != 1 || compileNode.nodeType !== 1) {
throw $compileMinErr('tplrt',
"Template for directive '{0}' must have exactly one root element. {1}",
origAsyncDirective.name, templateUrl);
}
tempTemplateAttrs = {$attr: {}};
replaceWith($rootElement, $compileNode, compileNode);
var templateDirectives = collectDirectives(compileNode, [], tempTemplateAttrs);
if (isObject(origAsyncDirective.scope)) {
markDirectivesAsIsolate(templateDirectives);
}
// 在调用前已经将执行过的directive splic删除掉了,所以这边跟上稍许不同,但本质一样
directives = templateDirectives.concat(directives);
mergeTemplateAttributes(tAttrs, tempTemplateAttrs);
} else {
compileNode = beforeTemplateCompileNode;
$compileNode.html(content);
}
// ----- 到这边处理方式逻辑跟文本template的几乎一样 ------
// directives 没有包含本directive,所以需要加入
directives.unshift(derivedSyncDirective);
// 类似于compileNodes中调用applyDirectivesToNode返回nodeLinkFn
afterTemplateNodeLinkFn = applyDirectivesToNode(directives, compileNode, tAttrs,
childTranscludeFn, $compileNode, origAsyncDirective, preLinkFns, postLinkFns,
previousCompileContext);
forEach($rootElement, function(node, i) {
if (node == compileNode) {
$rootElement[i] = $compileNode[0];
}
});
// 类似于compileNodes中递归调用compileNodes处理childNodes
afterTemplateChildLinkFn = compileNodes($compileNode[0].childNodes, childTranscludeFn);
// 需要延迟执行的link阶段方法
while(linkQueue.length) {
var scope = linkQueue.shift(),
beforeTemplateLinkNode = linkQueue.shift(),
linkRootElement = linkQueue.shift(),
boundTranscludeFn = linkQueue.shift(),
linkNode = $compileNode[0];
if (beforeTemplateLinkNode !== beforeTemplateCompileNode) {
var oldClasses = beforeTemplateLinkNode.className;
if (!(previousCompileContext.hasElementTranscludeDirective &&
origAsyncDirective.replace)) {
// it was cloned therefore we have to clone as well.
linkNode = jqLiteClone(compileNode);
}
replaceWith(linkRootElement, jqLite(beforeTemplateLinkNode), linkNode);
// Copy in CSS classes from original node
safeAddClass(jqLite(linkNode), oldClasses);
}
if (afterTemplateNodeLinkFn.transcludeOnThisElement) {
childBoundTranscludeFn = createBoundTranscludeFn(scope, afterTemplateNodeLinkFn.transclude, boundTranscludeFn);
} else {
childBoundTranscludeFn = boundTranscludeFn;
}
afterTemplateNodeLinkFn(afterTemplateChildLinkFn, scope, linkNode, $rootElement,
childBoundTranscludeFn);
}
// 将queue设置为null,表示该directive已经获取成功并且compile完成,可以直接调用
linkQueue = null;
链接
angularjs源码笔记(1.1)--directive compile
angularjs源码笔记(1.2)--directive template
angularjs源码笔记(2)--inject
angularjs源码笔记(3)--scope