當前版本:0.0.1-alpha.0
重構
Renderer
因為舊版本中執行時編譯的時候嚴重耦合DOM api,導致寫服務端渲染的時候必須重新實現一套相同的編譯邏輯。於是便下決心剔除DOM api,抽象出一個通用的編譯邏輯。
想了很久參考了現有的三大框架的一些方法,於是便通過暴露出一個基類Renderer
來封裝一些渲染方法,隔離平臺api。再通過已經實現的虛擬DOM,每次執行時編譯的時候都對虛擬DOM進行更改diff,最後通過實現Renderer
的例項的方法進行渲染。
export abstract class Renderer {
public abstract nativeElementToVnode(nativeElement: any, parseVnodeOptions?: ParseOptions): Vnode[];
public abstract getElementsByTagName(name: string): any;
public abstract hasChildNodes(nativeElement: any): boolean;
public abstract getChildNodes(nativeElement: any): any[];
public abstract removeChild(parent: any, child: any): void;
public abstract appendChild(parent: any, child: any): void;
public abstract insertBefore(parent: any, child: any, index: number): void;
public abstract isContainted(parent: any, child: any): boolean;
public abstract creatElement(tagName: string): any;
public abstract creatTextElement(value: string): any;
public abstract getAttribute(element: any, name: string): any;
public abstract setAttribute(element: any, name: string, value: any): void;
public abstract setNvAttribute(element: any, name: string, value: any): void;
public abstract removeAttribute(element: any, name: string, value?: any): void;
public abstract removeNvAttribute(element: any, name: string, value?: any): void;
public abstract setNodeValue(element: any, nodeValue: any): void;
public abstract setValue(element: any, value: any): void;
public abstract removeEventListener(element: any, eventType: string, handler: any): void;
public abstract addEventListener(element: any, eventType: string, handler: any): void;
public abstract setStyle(element: any, name: string, value: any): void;
public abstract removeStyle(element: any, name: string): void;
public abstract getStyle(element: any, name: string): void;
}複製程式碼
最後,通過平臺的外掛實現該類就可以做到跨平臺渲染。(現在只實現了platform-browser..服務端進行中)。
虛擬DOM
因為模板是字串模板,所以第一版的虛擬DOM實際上是利用DOM的innerHTML完成的,為了做跨平臺渲染所以使用在正則匹配模板來找出tag及屬性。
packages/core/vnode/parse.ts
const tagRegex: RegExp = /<
(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">
])+>
/g;
複製程式碼
packages/core/vnode/parse-tag.ts
const tagRegex: RegExp = /<
(?:"[^"]*"['"]*|'[^']*'['"]*|[^'">
])+>
/g;
複製程式碼
利用這兩個正則匹配出對應模板的標籤及其屬性後,就可以不適用innerHTML來獲得一個類似DOM結構的Vnode了。
packages/core/vnode/vnode.ts
class Vnode {
public tagName?: string;
public nativeElement?: any;
public parentVnode?: Vnode;
public attributes?: TAttributes[];
public nodeValue?: string | null;
public childNodes?: Vnode[];
public type?: string;
public value?: string | number;
public repeatData?: any;
public eventTypes?: TEventType[] = [];
public key?: any;
public checked?: boolean;
public voidElement?: boolean = false;
public template?: string;
public index?: number;
}複製程式碼
最後在compile階段對Vnode進行操作獲得相應的最新Vnode及對diff之後的差異進行patch就可以對頁面結構進行更新。
此外順便把之前的一些遺留repeat
指令的問題修復了。
更新
- 移除
state
屬性,現在可以直接使用類的所有成員屬性和成員方法。在Oninit生命週期之前會把模板解析到的成員屬性和通過@Watch()
註解過的成員屬性都加入監聽中,屬性更改觸發render
- 移除
props
屬性,現在可以直接通過@Input(name?: string)
直接指定將元件的輸入對映到哪個屬性或方法上 - 在
JavaScript
可以直接在靜態屬性中使用類當做token,順序對應建構函式的引數 - 生命週期
nvDoCheck
代替nvWatchState
,刪除引數。(其實每次更改狀態都是已知的,所以上次狀態的意義不大) - 生命週期
nvReceiveProps
更名為nvReceiveInputs
- 優化渲染,通過代理指令上的事件。當事件觸發時將無法觸發渲染,事件handler結束之後將觸發
render
與watcher
。當一個事件中有多次屬性更改,將此次修改合併成一次修改推倒渲染佇列中 @NvModule
現在懶載入模組和根模組強制指定bootstrap
後設資料- 增加
ElementRef
和Renderer
,可以直接在元件或指令注入,分別獲得元件或指令掛載的nativeElement
和全域性的薰染例項。因此並不推薦直接使用瀏覽器api去操作DOM,而是推薦使用ElementRef
和Renderer
最後
emmmm….最後看了下跟ng越來越像了。
現在編譯渲染全部基於JIT,未來會支援AOT和管道,在此算我立了個flag。