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 4(三)【指令】AngularJS
- Angularjs學習筆記指令AngularJS筆記
- vue.js響應式原理解析與實現—實現v-model與{{}}指令Vue.js
- Angularjs——初識AngularJSAngularJS
- AT指令框架的實現框架
- git原理學習記錄:從基本指令到背後原理,實現一個簡單的gitGit
- block實現原理BloC
- ReentrantLock實現原理ReentrantLock
- synchronized實現原理synchronized
- AsyncTask實現原理
- jQuery實現原理jQuery
- Synchronized 實現原理synchronized
- Condition實現原理
- AQS實現原理AQS
- LinkedList實現原理
- ETL指令碼的實現指令碼
- AOP如何實現及實現原理
- Locust 程式碼指令碼實現指令碼
- 【Linux】【Shell】主控指令碼實現Linux指令碼
- MySQL——索引實現原理MySql索引
- Greys主要實現原理
- Spring AOP實現原理Spring
- (201)Atomic*實現原理
- 水波圖實現原理
- Category的實現原理Go
- Vitepress 的實現原理Vite
- 【Spring】AOP實現原理Spring
- golang reflect 實現原理Golang
- Python 字典實現原理Python
- synchronized 的實現原理synchronized
- RunTime實現原理剖析
- 理解 Block 實現原理BloC
- JAVA AQS 實現原理JavaAQS
- 前端路由實現原理前端路由
- Lombok 原理與實現Lombok
- MySQL MVCC實現原理MySqlMVC
- ACID的實現原理
- LinkedList 的實現原理