Angular如何由模板生成DOM樹的方法
Angular等現(xiàn)代Web框架極大的提高了開發(fā)效率,比如我們經常會在開發(fā)過程中寫出類似下面的代碼:
<div> {{title}} </div> export class AppComponent { title = 'angular'; }
這種模板寫法并不是HTML原生支持的,那么Angular又是如何轉換這些代碼,并顯示成我們期望的界面呢? 首先我們來看看Angular把上述代碼編譯成什么樣子:
...省略了其他代碼 i0.ɵɵelementStart(0, "div"); i0.ɵɵtext(1, " hello angular\n"); i0.ɵɵelementEnd() ...省略了其他代碼
可以看到,Angular把我們寫的模板編譯成指令的方式,然后通過這些指令生成對應的HTML.這個過程包含兩個步驟:
- 把模板編譯成上面的產物
- 執(zhí)行產物代碼生成HTML
本文主要圍繞步驟二進行展開,步驟一的話可能會在后續(xù)另寫一篇進行闡述。
觀察上面的產物代碼,我們不難發(fā)現(xiàn)有三個主要方法:elementStart、text、elementEnd.從它們的命名不難推測,這三個方法的作用分別是開始生成標簽、內容賦值、閉合標簽。下面我們來嘗試自己實現(xiàn)這幾個方法,最簡單的基礎版本大概會是這樣:
let currentNode: Node | null = null; let currentParent: Node | null = null; function patch(host: Node | DocumentFragment, render: () => void): void { currentNode = host; render(); } function elementOpen(tagName: string): void { currentParent = currentNode; const element = document.createElement(tagName); currentParent!.appendChild(element); currentNode = element; } function text(textContent: string): void { currentNode!.textContent = textContent; } function elementEnd(tagName: string): void { currentNode = currentParent; currentParent = currentNode!.parentNode; }
然后在HTML中可以這樣使用:
<div id="container"></div> <script> function render() { elementOpen('div'); text('div content'); elementOpen('p'); text('p content'); elementEnd('p'); elementEnd('div'); } patch(document.getElementById('container'), render); </script>
上述代碼中,text方法參數(shù)都被寫固定了,實際生成的代碼可能類似于text(Comp.title)這種形式。那么既然是以變量的形式賦值,當用戶進行操作的時候,更新這個變量的值,豈不是又要完全重新執(zhí)行一遍patch函數(shù)么?我們知道DOM操作是耗時的,當我們的項目較大時,如果不采取優(yōu)化措施,勢必會影響框架性能。為此我們很容易想到的一個優(yōu)化思路,在再次執(zhí)行patch函數(shù)時,如果DOM節(jié)點已經存在我們就重復利用,不再去重新創(chuàng)建并插入DOM樹?;谶@個思路,我們來更新一下代碼:
let currentNode: Node | null = null; let currentParent: Node | null = null; function patch(host: Node | DocumentFragment, render: () => void): void { currentNode = host; render(); } function elementOpen(tagName: string): void { currentParent = currentNode; const firstChild = (currentParent as Element).firstElementChild; if (firstChild && firstChild.tagName.toLowerCase() === tagName) { currentParent = firstChild; return; } const element = document.createElement(tagName); currentParent!.appendChild(element); currentNode = element; } function text(textContent: string): void { if (currentNode!.textContent !== textContent) { currentNode!.textContent = textContent; } } function elementEnd(tagName: string): void { currentNode = currentParent; currentParent = currentNode!.parentNode; }
本文所述代碼,只是表述Angular由模板生成dom樹的大致思路。具體的Angular做了許多優(yōu)化,而且它實現(xiàn)細節(jié)也和本文有區(qū)別。不同于現(xiàn)今較為流行的virtual DOM實現(xiàn)方式,Angular這種實現(xiàn)思路不需要單獨創(chuàng)建中間DOM對象,減少了內存分配。對此感興趣的讀者可以自行去看Angular的實現(xiàn)。
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
對angularjs框架下controller間的傳值方法詳解
今天小編就為大家分享一篇對angularjs框架下controller間的傳值方法詳解,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2018-10-10理解Angular的providers給Http添加默認headers
本篇文章主要介紹了理解Angular的providers給Http添加默認headers,具有一定的參考價值,有興趣的同學可以了解一下2017-07-07angularjs指令中的compile與link函數(shù)詳解
這篇文章主要介紹了angularjs指令中的compile與link函數(shù)詳解,本文同時訴大家complie,pre-link,post-link的用法與區(qū)別等內容,需要的朋友可以參考下2014-12-12Angular ui.bootstrap.pagination分頁
這篇文章主要為大家詳細介紹了Angular ui.bootstrap.pagination 分頁的相關資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01AngularJs ng-repeat 嵌套如何獲取外層$index
這篇文章主要介紹了AngularJs ng-repeat 嵌套如何獲取外層$index的相關資料,需要的朋友可以參考下2016-09-09AngularJS 驗證碼60秒倒計時功能的實現(xiàn)
最近在做AngularJS 項目,這是寫的一個60秒倒計時功能,下面小編給大家介紹AngularJS 驗證碼60秒倒計時功能的實現(xiàn),需要的朋友參考下吧2017-06-06