AngularJS 指令實現原理
瀏覽器會解析渲染 HTML 元素的樣式和行為,這個能力是 Web 強大功能的基礎之一。指令本質上是 AngularJS 對 HTML 元素的擴充套件,給 HTML 元素增加自定義功能,語義化 HTML 標籤。AngularJS 編譯 DOM 時,會執行與指令關聯的 JS 程式碼,即找到指令物件,執行指令物件的相關方法。
本文的程式碼使用 AngularJS 的原始碼,為了把重心放在主要流程上而略去了很多程式碼,這裡說明一下,有需要可以直接看原始碼。
指令註冊
module 物件的 directive 方法 directive: invokeLaterAndSetModuleName('$compileProvider', 'directive')
只是指令的儲存,儲存了一個['$compileProvider', 'directive', ['myDirective', function () {}]]
單元到一個陣列invokeQueue
中。
指令的註冊是在應用啟動後,在執行var injector = createInjector(modules, config.strictDi);
時通過呼叫loadModules
這個方法,來載入所有的 module 物件(這裡就包括了 myApp 模組物件),模組載入的實質其中就包括遍歷當前模組物件上的 invokeQueue 佇列這一項,取出每一個資料元單位,然後執行對應服務的對應方法,所以這裡的指令註冊便會 link 到 $compileProvider 服務的 directive 方法(定義一個 hasDirectives 陣列儲存指令的 factory 函式)
function $CompileProvider($provide, $$sanitizeUriProvider) {
var hasDirectives = {},
Suffix = 'Directive';
//......
this.directive = function registerDirective(name, directiveFactory) {
if (!hasDirectives.hasOwnProperty(name)) {
hasDirectives[name] = [];
$provide.factory(name + Suffix, ['$injector', function($injector) {
var directives = [];
forEach(hasDirectives[name], function(directiveFactory, index) {
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 = getDirectiveRequire(directive);
directive.restrict = getDirectiveRestrict(directive.restrict, name);
directive.$$moduleName = directiveFactory.$$moduleName;
directives.push(directive);
});
return directives;
}]);
}
hasDirectives[name].push(directiveFactory);
return this;
};
//......
}
$provider.factory
會把 'myDirective' + 'Directive'
註冊為服務,註冊的具體邏輯見上面程式碼,即指令的指令物件是通過註冊為服務的方式來被外界獲取的(依賴注入)。
指令編譯
先看一下 AngularJS 執行的主流程,在建立一個 injector 物件(實現依賴注入的主要物件)後例項化主要的 4 個服務後開始 DOM 編譯和啟動 $digest。
var injector = createInjector(modules, config.strictDi);
injector.invoke(['$rootScope', '$rootElement', '$compile', '$injector',
function bootstrapApply(scope, element, compile, injector) {
scope.$apply(function() {
element.data('$injector', injector);
compile(element)(scope);
});
}]);
$compile 服務是一個方法,內部首先呼叫 compileNodes
使用深度優先的方式遍歷 DOM 樹,找到 DOM 樹上的指令名,通過依賴注入獲取到指令物件例項,執行指令物件的 compile 方法,compile 會返回 link 函式,通過linkFns.push(i, nodeLinkFn, childLinkFn);
把 link 函式 push 到一個 linkFns 陣列中,compileNodes
函式最終返回另一個函式 compositeLinkFn
,而 $compile 返回的 publicLinkFn
會呼叫 compositeLinkFn
在呼叫時把 scope 已經傳遞進compositeLinkFn
中,然後去迴圈執行所有的 link 函式。
function compileNodes(nodeList) {
var linkFns = [],
for (var i = 0; i < nodeList.length; i++) {
attrs = new Attributes();
directives = collectDirectives(nodeList[i], [], attrs, i === 0 ?
maxPriority : undefined,ignoreDirective);
nodeLinkFn = (directives.length) ?
applyDirectivesToNode(directives, nodeList[i]) : null;
childLinkFn = (nodeLinkFn && nodeLinkFn.terminal ||
!(childNodes = nodeList[i].childNodes) ||
!childNodes.length)
? null
: compileNodes(childNodes, ......);
if (nodeLinkFn || childLinkFn) {
linkFns.push(i, nodeLinkFn, childLinkFn);
linkFnFound = true;
nodeLinkFnFound = nodeLinkFnFound || nodeLinkFn;
}
}
return linkFnFound ? compositeLinkFn : null;
function compositeLinkFn(scope, nodeList, $rootElement, parentBoundTranscludeFn) {
for (i = 0, ii = linkFns.length; i < ii;) {
nodeLinkFn = linkFns[i++];
if (nodeLinkFn) {
if (nodeLinkFn.scope) {
childScope = scope.$new();
} else {
childScope = scope;
}
nodeLinkFn(childLinkFn, childScope, node, $rootElement, childBoundTranscludeFn);
} else if (childLinkFn) {
childLinkFn(scope, node.childNodes, undefined, parentBoundTranscludeFn);
}
}
}
}
nodeLinkFn =...
賦值語句是執行指令的 compile 方法返回 link 方法,childLinkFn =...
這句是深度優先遍歷 DOM 樹,compositeLinkFn
在執行時 scope 已經定義好並傳遞進來,該方法迴圈呼叫nodeLinkFn(...
執行指令的 link 函式。
相關文章
- Vue指令實現原理Vue
- AngularJS 指令實踐AngularJS
- AngularJS 指令實踐指南(二)AngularJS
- AngularJS 指令實踐指南(一)AngularJS
- 指令<AngularJs>AngularJS
- AngularJS 4(三)【指令】AngularJS
- angularJS 系列(七)---指令AngularJS
- AngularJS - 自定義指令AngularJS
- angularjs之$timeout指令AngularJS
- Angularjs學習筆記指令AngularJS筆記
- 學習AngularJs:Directive指令用法AngularJS
- AngularJS教程二——內部指令AngularJS
- AngularJS學習筆記3——AngularJS的工作原理AngularJS筆記
- vue.js響應式原理解析與實現—實現v-model與{{}}指令Vue.js
- AngularJS 實現簡單購物車AngularJS
- 如何使用angularjs實現按鈕事件AngularJS事件
- angularjs 實現圖片上傳實時預覽AngularJS
- AngularJS中Directive間互動實現合成AngularJS
- 如何使用angularjs實現表單驗證AngularJS
- 走進AngularJs(五)自定義指令----(下)AngularJS
- 走進AngularJs(三)自定義指令-----(上)AngularJS
- 走進AngularJs(四)自定義指令----(中)AngularJS
- AngularJS教程二十三—— 通用下拉指令AngularJS
- Synchronized 實現原理synchronized
- synchronized實現原理synchronized
- block實現原理BloC
- AQS實現原理AQS
- jQuery實現原理jQuery
- HashMap實現原理HashMap
- AsyncTask實現原理
- weak實現原理
- Session實現原理Session
- AT指令框架的實現框架
- AOP如何實現及實現原理
- AngularJS實現的檔案檔案上傳AngularJS
- 如何使用angularjs實現抓取頁面內容AngularJS
- 如何使用angularjs實現文字框設定值AngularJS
- 如何使用angularjs實現ajax非同步請求AngularJS非同步