Compile Template
1. template
template的配置
{ template: '<div>22222</div>',replace: true,//false link: function () { ... } }
对于template的处理主要集中在applyDirectivesToNode()中对于directives数组循环compile目标node,处于compile阶段
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配置
{ templateUrl: '/user.html',//false link: function () { ... } }
入口方法源码如下:
if (directive.templateUrl) { hasTemplate = true; assertNoDuplicate('template',$compileNode); templateDirective = directive; if (directive.replace) { replaceDirective = directive; } nodeLinkFn = compileTemplateUrl(directives.splice(i,directives.length - i),templateAttrs,jqCollection,hasTranscludeDirective && childTranscludeFn,preLinkFns,postLinkFns,{ controllerDirectives: controllerDirectives,newIsolateScopeDirective: newIsolateScopeDirective,templateDirective: templateDirective,nonTlbTranscludeDirective: nonTlbTranscludeDirective }); // 这里很重要!表示后续的directives都不需要执行了,改在compileTemplateUrl中异步执行 ii = directives.length; } else if (directive.compile) { // ... }
主要方法compileTemplateUrl()包含所有细节
function compileTemplateUrl(directives,tAttrs,$rootElement,childTranscludeFn,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里获取执行
function delayedNodeLinkFn(ignoreChildLinkFn,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,childBoundTranscludeFn); } };
1.异步请求模板然后处理模板
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',origAsyncDirective.name,templateUrl); } tempTemplateAttrs = {$attr: {}}; replaceWith($rootElement,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,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,boundTranscludeFn); } else { childBoundTranscludeFn = boundTranscludeFn; } afterTemplateNodeLinkFn(afterTemplateChildLinkFn,linkNode,childBoundTranscludeFn); } // 将queue设置为null,表示该directive已经获取成功并且compile完成,可以直接调用 linkQueue = null;
链接
angularjs源码笔记(1.1)--directive compile
angularjs源码笔记(1.2)--directive template
angularjs源码笔记(2)--inject
angularjs源码笔记(3)--scope
原文链接:https://www.f2er.com/angularjs/149044.html