又双叒叕造新轮子:重构了自己写的mvvm库

承接上文

当前版本:0.0.1-alpha.0

项目地址
文档
npm

重构

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就可以对页面结构进行更新。

详情参考虚拟DOM跨平台渲染这两章。

此外顺便把之前的一些遗留repeat指令的问题修复了。

更新

  • 移除state属性,现在可以直接使用类的所有成员属性和成员方法。在Oninit生命周期之前会把模板解析到的成员属性和通过@Watch()注解过的成员属性都加入监听中,属性更改触发render
  • 移除props属性,现在可以直接通过@Input(name?: string)直接指定将组件的输入映射到哪个属性或方法上
  • JavaScript可以直接在静态属性中使用类当做token,顺序对应构造函数的参数
  • 生命周期nvDoCheck代替nvWatchState,删除参数。(其实每次更改状态都是已知的,所以上次状态的意义不大)
  • 生命周期nvReceiveProps更名为nvReceiveInputs
  • 优化渲染,通过代理指令上的事件。当事件触发时将无法触发渲染,事件handler结束之后将触发renderwatcher。当一个事件中有多次属性更改,将此次修改合并成一次修改推倒渲染队列中
  • @NvModule现在懒加载模块和根模块强制指定bootstrap元数据
  • 增加ElementRefRenderer,可以直接在组件或指令注入,分别获得组件或指令挂载的nativeElement和全局的熏染实例。因此并不推荐直接使用浏览器api去操作DOM,而是推荐使用ElementRefRenderer

最后

emmmm....最后看了下跟ng越来越像了。

现在编译渲染全部基于JIT,未来会支持AOT和管道,在此算我立了个flag。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值