Angular发展

1

前端黎明

HTML是一种标记语言,属于声明式编程范式,然而,声明式的缺点——复用性差,往往会制约着开发者对应用进行扩展。

早期的网页应用往往体量较小,加之用户体验不强,很多服务器直接将HTML作为数据处理,形成MVC的中介模式(脱藕)实现,model和view是两种不同的数据系统,中介controller则负责将不同的model输入映射到响应的view输出。

img

中介模式
使用一个中介对象来封装两个系统之间的交互,使得各对象不用显示地相互引用

但是随着前端交互越来越多,静态的HTML服务端处理根本不能满足交互需求,因此有实现将数据与视图的映射(MVC)迁移到前端,例如AngularJS,Backbone等。

混乱大时代

但是随着应用体量的不断变大,前端交互逻辑的不断复杂化,这类框架的Controller越来越复杂难以管理,加上浏览器的事件驱动模型(事件循环),使得前端开发步入了困境。

首先解决这一困境的是React,采用函数式编程语言中广泛使用的单向数据流,控制耦合数据的产生和事件的作用范围。

img

单向数据流
函数式编程(λ演算),例如LISP,采用的是不可变数据+态射变换的方式进行计算,最早由阿隆索·丘奇(Alonzo Church)提出,想象一个函数(函子)的参数是函数,返回值也是函数。(不可变数据本身也是函数,总是返回自身参数,即单位元,单位态射)
参考:
https://github.com/justinyhuang/Functional-Programming-For-The-Rest-of-Us-Cn
https://plato.stanford.edu/entries/category-theory/
维持态射连续性,即形成unidirectional network(单向网络),输入单位元总使从计算系统的输入流向输出(想象一个工业生产线)
这样的做法的目的,也是函数式编程的优点之一(参考傻瓜函数式):
因为FP中的每个符号都是final的,于是没有什么函数会有副作用。谁也不能在运行时修改任何东西,也没有函数可以修改在它的作用域之外修改什么值给其他函数继续使用(在指令式编程中可以用类成员或是全局变量做到)。这意味着决定函数执行结果的唯一因素就是它的返回值,而影响其返回值的唯一因素就是它的参数。
当然函数式的这一优势不仅仅用在前端领域,包括密集计算(模型能够运行则要求运行不出错,例如Haskell),军用(导弹巡航控制),核电站中控(安全要求高),气象等等领域。

但是React夹杂的私货,却也被混入了前端开发中,这便是MVVM

img

每一个设计模式,都有他的应用领域,MVVM是马丁·富勒的Presentation Modal(表示模型)的变体,由约翰·高斯曼提出,优点是不依赖于特定用户界面平台

缺点呢?创造者约翰·高斯曼自己就提出:

实现MVVM的开销对于简单的UI操作是“过度的”,对于更大的应用来说,推广ViewModel变得更加困难。而且,非常大的应用程序中的数据绑定会导致相当大的内存消耗。

不仅如此,浏览器在实现视图绑定的阶段,往往伴随着性能开销,而采用VM到V的映射,往往伴随着大量的视图绑定和解绑过程,会造成相当的性能瓶颈。

React可以通过很多优化控制重绘的数量(diff)和时间(requestAnimationFrame,setTimeout),但是当应用体量越来越庞大的时候,它所提到的“Web UI中跨层级移动操作特别少”这个前置条件慢慢失效,而维护一个庞大的数据池带来的内存消耗和数据持久化问题也越来越突出

但是React为什么还要采用MVVM而放弃掉MVC呢?

Facebook成立于2004年,而2005年WWDC大会上,苹果不仅发布了iPod nano这一音乐领域的飓风,还联合各家浏览器厂商,联合发布了围堵IE的WebKit,浏览器领域迎来了新的一波风暴。

img

当然,刚刚成立的facebook直到当年九月份才将高中版Facebook向大众开放,自然错过了浏览器之争的关键时机。但是作为以互联网产品为盈利主力的facebook,肯定不希望自己的产品被浏览器钳制,不可能因为浏览器的更新迭代甚至是恶意竞争而妨碍到自己的利益。并且在当时浏览器兼容问题严重的情况下,MVVM的平台无关也更加符合Facebook甚至是用户的切身利益。

另外,由于没有考虑到前端的复杂度会到达今天这样的地步,网景在设计JavaScript的时候,考虑利用函数式编程中“直观”的优势,以及异步带来的交互友好,将JS设计成了协程异步系统,这毫无疑问将编程要求提高到一个较高的层次,使得一线开发人员叫苦不迭。而MVVM又能有效地将异步的问题同步化(针对数据操作),因此,选择MVVM这一模式几乎是必然的。

因此React在2011年开始用于facebook的newsfeed,而这一年正是浏览器竞争最为激烈的一年,不管是从开发效率还是从运行稳定性上来看,React都是最好并且最符合Facebook利益的选择,于是快马加鞭,于2013年正式向公众开放。

img

React的单向数据流思想,让组件化,工程化的进程不再受到阻碍,整个前端领域也迎来了大的发展期。

本来故事到这里就应该结束了的,不过,谁也没有想到会有另外一个公司出手,将前端领域带入了另外一个世界。


移动端大时代

08年大家都印象深刻,乔布斯将手中的iphone4举向空中的那一刻,相信每一个人都心潮澎湃。而此时iOS的竞争对手Android加入了Google,开始了与iOS长达多年的拉锯战。面对发布会上乔布斯自豪的网页浏览功能,Android团队建议放弃KHTML,加入WebKit开发项目。

但是不知是不是早有预谋还是其他原因(毕竟08年Google已经有了v8),08年加入WebKit的Google从09年开始就成为了WebKit最大的代码提供者,而提交贡献相对较少的苹果却拥有最终决断权。

于是当2013年blink爆炸性发布时,苹果WebKit团队领头人Maciej Stachowiak说:

我们在写任何WebKit2代码前就问了Google的人,他们愿不愿意将多进程架构的支持整合到WebKit中,他们的答案是否定的。在这种情况下,我们面临的选择是做一个怀有敌意的Chromium分支,或者写我们自己的多进程架构,或者继续使用单进程架构。我们选择了写自己的多进程架构。

而凭借着多进程,v8,沙箱的三板斧,Chrome迅速侵占浏览器市场,终于在15年底,完成了对浏览器领域的垄断。

img

这时浏览器巨人再回过头看,看到的是“平台无关”的Happy Hacking代码肆意运行在它的浏览器上

Don’t be evil?事实应该是Don‘t let anybody say I’m evil。涉及到利益之争,不可能不重视。早在2012年,当市场上风闻Facebook新项目时,Google就将其假象竞争对手AngularJS收入囊中,几乎是与blink同时布局。

开源世界的主导权竞争就是人才的竞争,谷歌早就深谙这一套玩法,为了更广大的互联网市场,Google开始了新一轮的降维打击,而这一布局的关键一环,就是——Angular。


只用一个平台的野心

Hack的思想,是如何在系统框架内部,实现对自己有利的功能,Facebook是一家富有极客精神的公司,因此,React的实现也颇有些无耐,没有浏览器,即便再Hack,都只是别人既定框架下的小聪明。

而这时,Angular在紧锣密鼓地计划中:

stage中的代码阻塞如何处理?工程界早有成熟的方式,可以采用多线程的方式,主线程处理不了的任务转化成事件,转交给从线程运行,保障高IO的进行——chrome 17支持了web worker。

前端开发调试,离线浏览,消息推送?没问题,chrome service worker为你提供保障。

提供了完整的性能监视,每个区域的重绘,fps,layer边界,都给你直观地展示,堪称世界上最完善的浏览器调试工具。

甚至是微软,经历了IE的失败,比其他人都更加意识到开源社区的重要,作为事件驱动windows系统中的长期经验总结,Rx的js版本Rxjs,顺理成章地被运用到同样处于事件驱动环境的浏览器中。

接下来,就是等待能够彻底压榨浏览器性能,用户友好,集成化的前端开发框架了。

2

上回说到Google需要一款前端技术栈将它的浏览器性能完全发挥,并于其他浏览器拉开身位,阻击平台无关(MVVM)的各类框架。
如何能够让Google在浏览器制霸之后,将魔抓伸向应用层?
如何在渲染中发挥出浏览器最大性能呢?

最近房价波动比较厉害,作为一个高薪程序员,新房大多地段不好,而购入一套离公司近的二手房,自然需要一个好的房地产中介。

需要他联系买卖双方

但是这时却有两种中介商:

  1. 所有买方跟我联系,我同意把你们愿意出的价格和需求报给卖方,最后把结果给你们双份分开展示(MVVM)
  2. 你要买房?我单独联系卖方和你,你说啥,我就原封不动地传达给卖方,反之亦然(MVC)

大家别急着选 2,想象一个场景,如果卖方那群人天天在变,一会儿这个看不上,一会儿那个瞧不起,房子也不停在变,早上 50 平晚上 100 平的,你会不会觉得第一种房地产中介要好一点呢?

大哥我跟你说,这个卖家真的有毒,上次那房看的时候在 3 环,卖的时候带我们去 5 环,您看看这个,这个靠谱,他家房子是真的大。

你也别急着调过头选 1,就算卖家不靠谱,1 中的中介每天下午 5 点才能将所有卖家信息汇总给你看,你婚礼在上午 8 点,未婚妻说:

不干!什么时候买房,什么时候结婚!

这个时候你又怎么办?另外即便未婚妻能等到下午 6 点,中介从 5 点开始翻记录就翻到了 6 点半,你又怎么办?

对的,浏览器也遇到这个问题,js 中的 dom 操作是买方,而页面渲染则是卖方。


React 作为一个出色的前端框架,通过 MVVM 做到了平台无关,facebook 相关产品在任何浏览器上打开都能有不错的性能,甚至能够进军原生终端应用开发,这都是 MVVM 架构的功劳。

但是 vm 这一全局的 view 副本,始终在消耗着浏览器资源,项目小的时候看不出来,当项目无限膨胀的时候,弊病开始显现。

书接上回,Google 需要一个前端框架,这个技术栈不一定需要 Google 产品在其他浏览器上的体验与 Chrome 一样,但一定要在 Chrome 上有极致的性能。用纯数据的方式保留一个 view 副本?不可能也不需要这么做。

Angular 框架采用 TypeScript 开发(工程化会另有文章讨论),其中一个优点便是代码易读,打开源码angular/packages/core/src/linker,里面有许多抽象类,方便了解大致的 Angular 渲染机制。

  1. templateRef: 用户声明模板
  2. viewRef: 自定义视图
  3. elementRef: 原生 Dom 元素
  4. viewContainerRef:自定义挂载点
  5. componentFactory: 组件工厂函数
  6. moduleFactory: 模块工厂函数

举例说明两个重要类:ComponentRef 和 viewContainerRef。

ComponentRef 需要确定和视图的关系,以及相关的变更检测,依赖注入等(变更检测在 M 中进行而非 vm 中):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
//https://github.com/angular/angular/blob/master/packages/core/src/linker/component_factory.ts
// 组件引用
export abstract class ComponentRef<C> {
// 宿主元素
abstract get location(): ElementRef;
// 依赖
abstract get injector(): Injector;
// 组件实例
abstract get instance(): C;
// 宿主视图
abstract get hostView(): ViewRef;
// 检测树
abstract get changeDetectorRef(): ChangeDetectorRef;
// 获取组件类型
abstract get componentType(): Type<any>;
// 组件销毁
abstract destroy(): void;
// 组件销毁回调
abstract onDestroy(callback: Function): void;
}

viewContanerRef 需要实现对 viewRef 的挂载和删除,因此他的抽象类声明如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// angular/packages/core/src/linker/view_container_ref.ts
export abstract class ViewContainerRef {
// 针对每一个container 的锚点 element
abstract get element(): ElementRef;
// 清除内容
abstract clear(): void;
// 获取view
abstract get(index: number): ViewRef | null;
// 获取view 长度
abstract get length(): number;
// 创建嵌入式view(偷懒专用)
abstract createEmbeddedView<C>(
templateRef: TemplateRef<C>,
context?: C,
index?: number
): EmbeddedViewRef<C>;
// 插入view
abstract insert(viewRef: ViewRef, index?: number): ViewRef;
// 移动view
abstract move(viewRef: ViewRef, currentIndex: number): ViewRef;
// 获取view 的 index
abstract indexOf(viewRef: ViewRef): number;
// 删除view
abstract remove(index?: number): void;
// 解绑但是不删除view
abstract detach(index?: number): ViewRef | null;
}

注意那个 move 移动方法,稍后会有讨论许多其他框架中“跨层级移动不会发生”的前置条件是否存在。

注:变更检测的简单声明在 angular/packages/core/src/change_detection/change_detector_ref.ts

如果组件绑定的 ChangeDetectionRef 发现了变更,标记为脏,则重新运行 Component 到 View 的渲染,官方称作渲染引擎

这便是Angular的MVC架构:

img

直接操作DOM,极大地减少了中间数据操作,提升了性能。


Angular 4.0 更新了渲染引擎 Renderer2,将渲染操作封装成函数,方便大家进行渲染(直接使用有违背组件化的开发方式)。 而现在,官方已经放出了 Renderer3,也就是ivy 渲染引擎,将摇树优化,依赖,国际化等等操作融入到渲染层级,并且以“装饰器即编译”的理念作为中心思想。

img

更加恐怖的是,能够在 6.2kb 的大小下实现一个渲染的最小功能集。


DOM 中有一个幽灵——

良好用户体验的保证是 60 帧的刷新率,最好是120帧!

然而在这 8ms 左右的帧时间中,一次 DOM 结构变更(appendChild,removeChild)会占用平均 4ms 左右的时间,因此——

你得代码必须在 4ms 内执行完毕!

而且 DOM 结构变更是浏览器过程,浏览器会保证你单纯的 dom 变更操作绝对会有稳定的用户界面变更,但这个过程在 js 代码中不可优化!

React 的解决办法,是利用浏览器 RquestAnimationFrame 或者自己统计时间(RequestAnimationFrameWithSetTimeout),将需要执行的reconsiliation代码和提交的DOM变更依次安排在浏览器渲染结束的空隙里

但这有没有真正解决问题,大家都有自己的判断。

Angular 采用了另外的方式:

(一)MVC 的变更是响应式的,并且脏检查只针对数据(onPush)。

渲染引擎在处理数据变更时,采用单向绑定(数据变更即更新):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
// angular/packages/core/src/view/util.ts
// 检测view是否变更
export function checkAndUpdateBinding(
view: ViewData, def: NodeDef, bindingIdx: number, value: any): boolean {
if (checkBinding(view, def, bindingIdx, value)) {
view.oldValues[def.bindingIndex + bindingIdx] = value;
return true;
}
return false;
}

// angular/packages/core/src/view/text.ts
// 以textNode为例,还有内联性能优化
export function checkAndUpdateTextInline(
view: ViewData, def: NodeDef, v0: any, v1: any, v2: any, v3: any, v4: any, v5: any, v6: any,
v7: any, v8: any, v9: any): boolean {
let changed = false;
const bindings = def.bindings;
const bindLen = bindings.length;
if (bindLen > 0 && checkAndUpdateBinding(view, def, 0, v0)) changed = true;
// ...10个内联
if (changed) {
let value = def.text !.prefix;
if (bindLen > 0) value += _addInterpolationPart(v0, bindings[0]);
// ... 10个内联
const renderNode = asTextData(view, def.nodeIndex).renderText;
view.renderer.setValue(renderNode, value);
}
return changed;
}

也就是说,单纯的数据的改变无法引起DOM的结构变更。

并且,DOM操作是会发生节点间的移动的!拖拽,弹框等等,如果设计大量复杂组建节点,一次DOM的移动操作伴随大量重绘。

这些问题,在Angular中也只是调用一个move方法,名正言顺地replaceChild。

但是当NgIf等发生时,还是会有结构变更怎么办,如果这个时候大量代码逻辑导致4ms内代码运行超时,卡顿还是会发生。

(二)我不与你在这 4ms 中争抢性能,使用 webWorker 在另一个线程处理逻辑,最后通过指令告诉浏览器视图更新,在这 8ms 中尽情地完成你那平均 4ms 的渲染~

这方法就是Angular在渲染性能领域的杀手锏,也是充分利用浏览器做到的极致性能,官网描述,源代码在angular/packages/platform-webworker-dynamic/,官网描述为:

Achieve the maximum speed possible on the Web Platform today

汪老师在翻译时,加了一个‘以及未来’:

通过Web Worker和服务端渲染,达到在如今(以及未来)的Web平台上所能达到的最高速度

为什么有个括号里的以及未来?因为webworker与浏览器强关联,技术平台只有在浏览器垄断的庇护下才能肆无忌惮地使用web worker,其技术架构甚至比绝大部分客户端程序的io性能还要高(因为webWorker是系统线程,不是用户态)

这一点是如何实现的呢?

img

组件逻辑代码完全与主线程相分离,在另一个线程处理数据,而在UI中只进行渲染有关操作。(不然你得Angular师傅叫你把代码写在service里面只是为了好看么?)

img

通过ServiceMessageBroker提交变更,渲染引擎在客户端获取变更数据,进行视图渲染。

正如ason Teplitz演讲所言,所有如下的操作:

img

都不会对你的视图渲染造成任何影响,V与M在前端实现了真正的分离


是的,Angular实现的(设计目标更严谨),是浏览器中的最强渲染性能,强大的浏览器后盾允许他这么做。

但是采用了MVC的方式,会不会导致AngularJS一代那样的异步错乱和数据意外耦合变更的问题呢?

3

Angular物语(三)ReactiveX(1)

动机

如果你觉得你没有必要用Rxjs,那你就不要用

这个说法很万金油,可以避免很多不必要的争论,但是并没有什么建设意义——什么时候会觉得有必要用呢?

先给一个结论,大家可以参考参考:

在一个事件驱动系统中处理复杂异步必须使用ReactiveX或者类似工具

必须?是的,必须!

就是这么绝对,毕竟先抛出一个绝对的概念,大家讨论特殊情况的时候才能言之有物不是?

这个武断中有一下几点需要特殊说明——

  1. 事件驱动
  2. 复杂异步
  3. 必要性

我们一个个说明吧~


事件驱动

XXX驱动,一般指的是你代码逻辑运行的原因。

如果你代码运行是因为某种事件或者消息——事件驱动。

如果是因为数据的改变——数据驱动。

首先声明这不是一个编程概念,因为你很容易在非编程领域看到这些概念——比如市场分析可以采用事件驱动和数据驱动的方法——但是他却是在编程概念之上更高层级的思想方法,很多种模式的不同实现就是来源于这两种不同的思想(例如MVC,MVVM)。

举例说明一个随时间变化的数据系统:

img

你如果注重数据,注重代码实现(代码的目的就是将事件消息翻译成数据变更)而不在意时间方向上的变化——数据驱动:

img

你如果注重事件,注重服务而不在意单一时刻的数据变化——事件驱动:img

有什么区别呢?

我们先来看数据驱动:

  1. 每一时刻只需要考虑数据的变更,而不需要考虑数据为什么变更,又会引起怎样的变更——耦合性低,不可回溯
  2. 状态的迁移需要由编程人员或是系统发出的消息驱动——即时性差
  3. 总是由一个状态迁移到另一个状态,方便时间旅行——可回退

惊喜的是,事件驱动就像它的另一面一样,优缺点完全互补:

  1. 需要考虑特定事件会引起怎样的变更,多个事件极容易出现耦合,但是容易定位数据变更的原因——耦合性大,可回溯
  2. 状态的迁移是由事件决定的,事件逻辑不受阻挡——即时性强
  3. 单一时刻数据不确定,很难进行时间旅行——不可回退

因此两种思想方法的口号也就容易理解了——

数据驱动——人类不善于处理逻辑,但善于处理数据
事件驱动——一切以最大化服务为优先

数据库事务,浏览器DOM,游戏帧画面渲染都是数据驱动,他们之间有一定的共性——

  1. 对数据准确定有要求
  2. 状态迁移没有额外开销或者开销很小(文件存储读写,或者直接渲染一帧即释放)

服务器IO,浏览器接口,游戏逻辑都是事件驱动,他们之间也有一定的共性——

  1. 需要对用户操作或者请求及时响应
  2. 全部状态迁移几乎不可能或者开销极大,只能通过消息进行

所谓的前端层面有名的伪概念之一——数据驱动适合前端开发——完完全全就是错误的!

并且,浏览器为了快速响应用户操作可是采用的事件驱动模型,为此还在单线程中实现了协程,这一方向在可见的未来不会改变。

那怎么处理这个复杂的,即时性要求如此之高的系统呢?如何优化其耦合性大,不可回退的缺点?实现高可用呢?

数据驱动模式一般如何解决不可回溯的缺点?

将事件作为数据存储——flux,redux模式

点到为止,应该很多前端都很熟悉这样的概念,其实这样的概念和redis缓存,sql语句操作数据库没有什么本质区别,都是消息——事务——数据变更的模式。

同理可得,如何处理事件驱动中的耦合性问题呢?

将数据也作为事件发出——stream模式

对的,这就是ReactiveX定义中的“数据流”。


复杂异步

浏览器给了你异步的代码能力,但是怎样实现异步,怎样处理异步,却没有告诉你行之有效的方法。

异步怎么可能复杂,不就是async await么?

好吧,那你坐好了,我给你看看异步到底有多复杂:

性能问题

大部分所谓的异步代码比同步代码还慢(需要调度时间):

1
2
3
4
5
// bad
await A; await B; awaitC;

// good
await Promise.all([A,B,C])

竞态问题

发出多个请求,以最先返回的数据为准?(别急着打产品经理)

1
await Promise.race([A,B])

嗯,这些好像Promise也能做,不急,还有其他问题呢。

组合问题

需要一次异步紧跟上一次异步,即时处理:

1
2
3
const result_1 = await Promise.All([A,B,C])
const result_2 = await D(result_1[0])
// 异步耦合,也是耦合

并发问题

需要进行节流和去抖。

撤销问题

需要必要的时候撤销回调,防止空置错误。

1
2
3
4
await A;
if(componentDestroied){
return
}

异常问题

需要统一处理异步异常,捕获异步异常以便让异步顺利进行,处处try catch?

1
2
3
4
5
6
try{
await A;
await B;
}catch(err){
// error handler
}

调度问题

这个promise只有micro一种调度模式,你如果需要用其他调度方式,恭喜你扑街:

img

micro和macro都有可能导致页面渲染阻塞(前者推迟,后者导致逻辑帧挤压物理帧),想要更加流畅的效果?试试animationFrame?

更或者,你是希望用到webworker多线程渲染(主线程只提交patch),那就得自己实现调度器了~

这些还只是冰山一角,还有循环变更,事件过滤,事件侦听,多播等等问题,信息交换会遇到什么问题,异步能把他们都走一遭~

别紧张,这些问题有没有简单的可以解决的办法呢?哈哈,你算是找对人了!


必要性

不过很多有后端经验的朋友肯定会反驳我——解决异步耦合问题不是可以用面向对象么?

是的,类和对象的思想就是封装数据和事件(行为),理论上数据驱动的问题和事件驱动的短板都可以用面向对象来弥补。

但是,面向对象需要复杂的结构信息来声明依赖,当异步过于复杂的时候,你需要维持一个异常庞大的结构,这样就回到了该用库还是该自己实现的问题上——不过以事件驱动系统中遇到的异步问题复杂度,自己用面向对象实现很不可靠。

不信问问用NodeJs的小朋友,最多做做可读可写流的连接,用人处理过race,combine,mergeAll?

当然,如果你觉得完全没有使用Rxjs的必要,那么可能存在以下几种情况——

  1. 处于一个完全数据驱动系统下(React),状态迁移是由固定消息驱动的。如setState,将所有数据流订阅之后再打平成数据,然后setState?这有意义?那自动订阅然后setState呢?那就请小心处理并发问题(diff可是个性能无底洞)。
  2. 不需要处理复杂的异步问题。比如博客,官网等产品,数据来源确定,前端往往只有展示的需求。

Rxjs和Angular

Angular是数据驱动么?

这么说某种程度也对,毕竟class的变量和elementRef的绑定是数据驱动的(数据改变会引发响应逻辑),但是也仅仅停留在一个Component声明内部,并且是响应式实现(getter,setter),本质不是变量改变,而是触发了新的事件。

另外提一句,vue也是如此的原理,但是getter,setter没有暴露给你,用watch和computed配合Rxjs说不定是个很好的思路(但是它是diff实现,同样也要考虑性能问题,虽然diff只是局限于component内部)。

全局来看,Angular触发视图变更的原因,有且只有zone拦截的事件!,并且是响应式变更至template,是一个不折不扣,200%的事件驱动系统,和浏览器实现高度吻合!

数据驱动有什么问题?毕竟都流行那么多年了?

因为前端的大环境改变了,我也希望大家能清楚地明白这一趋势。

数年前前端的主题是跨平台,因此数据驱动的MVVM大行其道可以理解,但是DOM diff消耗性能阻塞协程,状态手动迁移导致异步性能极差,是无论如何无法被忽视的。

而浏览器平均patch时间是4ms左右,现代浏览器普遍60-120hz的刷新能每个事件循环你有10ms左右的时间处理逻辑。

好像时间挺宽裕的,别忘了,历史的洪流在不断向前——

5g时代如果VR大行其道,低于240hz产生的栅栏效应将严重影响用户体验,这种情况下——你已经完全没有了在渲染线程处理业务逻辑的时间
再加上万物互联和1ms延迟导致的呈几何倍数增长的异步问题,届时如何为用户提供服务?

platform-webworker提供了多线程渲染的新的解决办法,服务将渐渐凌驾于实现之上,面对着新的产业升级,这波车,你上不上?


明白为什么要使用Rxjs了么?

更快(快速无阻塞响应服务),更稳定(数据流约束耦合),更方便(函数式避免复杂结构)

  • Copyrights © 2020-2022 Henry
  • Visitors: | Views:

请我喝杯咖啡吧~

支付宝
微信