当前版本: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。