【openInula茶话会】第一期:前端框架openInula整体介绍

本期作者:陈超涛 openInula架构师 / openInula社区TAC成员

《openInula茶话会》专栏每周不定期分享业务知识、实战经验与开发技巧,帮助社区成员快速提升技能,开启技术探索之旅! 本期介绍《前端框架openInula整体介绍》,欢迎查阅。

1 openInula框架2025年规划

为了打造卓越的开源竞争力,openInula团队正在构建API 2.0。这一全新的API设计理念,将为开发者带来更佳的开发体验和更好的性能表现。

2 openInula框架的使用指导

2.1 基于React的产品

为了能够无缝兼容React的相关生态(如React-redux、React-router、React-intl、Axios等),我们提供与React API保持一致的openInula 1.0版本,支持产品核心业务代码不变更,通过修改Webpack 的alias属性的方式即可平滑切换到openInula框架。

2.2 基于Vue的产品

今年openInula团队将提供针对VUE技术栈的Vue2openInula迁移工具,将现有的Vue代码通过工具一键翻译成为openInula代码,简化迁移过程。同时我们还提供了与Vue生态等效的Reactive API、状态管理器、路由、国际化等能力,确保迁移后的应用能够正常运行,保持功能一致性。

3 openInula 1.0的核心算法

3.1 数据结构:什么是虚拟DOM

说到前端的渲染,大家首先会想到虚拟DOM。虚拟DOM通过将真实DOM的操作转换为对JavaScript对象的操作,最少化直接操作DOM的次数,从而提升了性能。openInula 1.0也采用了虚拟DOM这种渲染模式。下面我们通过下图来了解虚拟DOM产生和使用的4个关键过程:

图1 虚拟DOM的生命周期

  1. ①为典型的函数组件和jsx声明文件。

  2. 经过Babel等工具的编译,组件和标签会被统一转换为对应的createVNode函数调用,形成一棵函数调用树,如上图中②所示。在这棵树中,每个节点都是一个函数,其第二个参数表示它的子节点。

  3. 在运行时,createVNode函数会被逐层执行,生成上图中③所示的虚拟DOM树。虚拟DOM树中的每个节点都是一个JS对象,存储组件的各种信息,如类型、key、property属性,以及用于维护树结构的parent和child等数据。

  4. 虚拟DOM树与界面真实DOM存在对应关系,但并非一一对应。因为在页面上我们自定义的函数组件并不存在对应的DOM节点。通过比对虚拟DOM,可以找出发生变化的节点,从而实现高效的DOM更新。

3.2 核心算法:虚拟DOM树的深度遍历,高效收集变更的状态

在openInula 1.0框架中,界面需要重新渲染时,需要对虚拟DOM树进行一次深度遍历。深度遍历的过程主要分为捕获、冒泡、提交三个阶段,每个阶段负责完成不同的渲染任务,如下图所示:

图2 虚拟DOM树的深度遍历

  1. 捕获(Capture),从上到下:

    (1)基于Diff算法,生成/更新VNode节点。

    (2)识别节点的待处理action,打上flag。

  2. 冒泡(Bubble),由下往上:

    (1)会把子节点的flag汇集在父节点中,最终汇聚到根节点。

    (2)生成界面DOM。

  3. 提交(Submit),依据前两个步骤汇聚在根节点上的flag,进行相应处理:

    (1)更新VNode节点状态。

    (2)挂载、更新、删除DOM。

3.3 核心算法:对深度遍历的三个优化

深度遍历的效率直接影响着界面渲染的性能,因此对深度遍历进行优化至关重要,是提升性能的关键。为了优化深度遍历,openInula 1.0采用了以下三种方法:

1) *寻找最近的公共父节点(自创)*

如图3所示,每个VNode节点都维护了自身的路径信息。例如,N1的路径为[0,0,0],N2的路径为[0,0,0,1],N3的路径为[0,0,1,1]。通过路径信息,可以高效地找到N2和N3的最近公共父节点P,其路径为[0,0]。

当N2、N3节点触发更新时,我们就可以从它们的最近公共父节点P开始往下遍历,而不是从根节点开始。这种优化方式减少了需要遍历的节点数量,从而提高了整个遍历过程的效率。

图3 虚拟DOM树的公共父节点

**2)**剪枝

如图4所示,在深度遍历VNode树的过程中,每个VNode节点维护两个属性:childShouldUpdate和shouldUpdate。当N1和N2节点触发更新时,就可以跳过那些shouldUpdate或childShouldUpdate被设置为false的子树,从而提高遍历效率,达到最小化遍历的目的。

图4 虚拟DOM树的剪枝

**3)**DOM删除的优化(自创)

如图5所示,当需要删除N1,N2,...,NN等多个节点时,常规的做法是逐个删除每个节点,这样就需要执行N次DOM操作,效率较低。

自创优化策略:首先克隆这些节点的共同父节点P,然后将父节点P从DOM树中删除,最后再将克隆的P节点添加回DOM树中。通过这种方式,原本需要N次的DOM操作被减少为只需3次,即克隆、删除和添加。

图5 虚拟DOM节点的删除优化

3.4 核心算法:Diff对比,高效找到变化的节点

除了首次生成虚拟DOM树外,后续的状态更新会生成一个新的虚拟DOM树,用来与当前的DOM树进行对比,以尽可能复用虚拟DOM树中的内容。这个比对过程被称为Diff对比。

为了提高两棵DOM树进行Diff比对的效率,我们采用了四种主要算法:同层对比、同类型对比、双端对比和最长递增子序列。

**1)**同层对比:将两棵树的对比简化为两个数组的对比,只比较同一层级的节点。

图6 两棵虚拟DOM树同层对比

**2)**同类型对比:当节点类型不同时(如div和Row),不再对比其孩子节点,只需直接删除原节点重建新节点。

图7 两棵虚拟DOM树同类型对比

**3)**双端对比:先排除前后相同的节点,减少需要比对的节点数量,提高效率。

图8 两棵虚拟DOM树同类型对比

**4)**最长递增子序列:通过寻找位置相对不变的最长节点序列,确定需要移动的节点,减少不必要的节点移动操作。

如图9所示,在新旧节点中,我们发现bcefgi这个子序列的相对位置是不变的,它们构成了我们要寻找的最长递增子序列。因此,我们可以保持bcefgi不动,只移动其他节点如adh,就能以最高效的方式完成节点的修改。

图9 寻找最长递增子序列

4 openInula 2.0的核心设计

在兼容React API的基础上,为了提供更好的开发体验和优化性能,我们正在构建API 2.0。

4.1 "零API"理念

API 1.0和API 2.0在代码编写方式上有明显的差异,如图10所示。使用API 1.0时,开发者需要手动定义状态和依赖关系,代码相对繁琐。而API 2.0则采用了更加简洁和直观的方式,只需使用原生的JavaScript即可完成同样的功能。

图10 API 1.0与API 2.0写法对比

相比于API 1.0,API 2.0的代码简洁,学习成本更低,开发者只需掌握原生JavaScript 的语法和概念,即可快速上手。

4.2 编译优先策略

API 2.0采用了编译优先的方案,如图11所示。在编译阶段,编译器会自动分析数据的使用情况,并根据分析结果生成高度优化的更新代码。这样,当数据发生变化时,系统可以以最小的代价进行精准更新。

图11 API 2.0编译前后代码对比

此外,通过编译过程,JSX代码被转换为直接创建DOM的代码,不再需要虚拟DOM树作为中间表示。这种方式可以减少运行时的开销,提高性能。

如下图所示,API 2.0基于js-framework-benchmark的性能跑分,相比React、Vue、Angular等业界知名框架,API 2.0在性能上的表现得更加出色。

图12 API 2.0基于js-framework-benchmark的性能跑分

5 openInula代码介绍

openInula代码结构如图13所示。

对外API:开发者可以使用openInula提供的内置组件和Hooks API编写用户侧的JSX代码,描述整个UI的组成。

渲染器:TreeBuilder.ts是整个框架的核心调度器,核心渲染过程如图13中的①到⑥ :

① 任务执行器启动一个渲染任务(同步/异步)。

② 发起深度遍历,在遍历过程中,调用不同类型的虚拟DOM组件,进行捕获(Capture)和冒泡(Bubble)处理。

③ 在捕获(Capture)阶段,执行Diff处理,更新VNode节点。

④ Submit处理:执行组件生命周期钩子函数。

⑤ Submit处理:根据VNode上的全量标志,进行DOM操作(挂载、修改、删除)。

⑥ DOM操作完成后,绑定事件代理(Event Proxy)。

图13 openInula代码结构

除了核心的渲染器,openInula代码库中还包含了多个重要的组件包,如Inula-X、Inula-router、Inula-intl和Inula-request等,为开发者提供了丰富的功能支持。

6 openInula 4大核心组件

openInula 1.0框架提供了多款应用开发中常用的生态组件:状态管理器、请求组件、路由组件、国际化组件。这些组件已经规模商用。openInula 2.0框架组件也将持续更新。

6.1 状态管理器:Inula-X

Inula-X是openInula框架默认提供的状态管理器,可以轻松实现跨组件和页面的状态共享。

与Redux相比,Inula-X具有以下特点:

  1. 多Store特性:支持创建多个Store,每个Store的颗粒度更小,调用更加简单,有利于代码的拆分和维护。
  2. 内置异步处理:开发者无需额外考虑状态管理器的异步问题,简化了开发过程。
  3. 简化返回值:在Reducer中不需要返回完整的state,编写更简洁直观。
  4. 减少业务代码:简化了Action和Reducer的创建步骤,相比Redux,可以减少约40%的业务代码量,如下图所示。

图14 Inula-X与Redux使用对比

  1. 兼容性:Inula-X还提供了兼容Redux的接口,具备替代Redux、React-Redux和Redux-Thunk的能力。在切换过程中,只需要从Inula-X提供的reduxAdapter模块中解构出同名函数即可无缝替换。

6 **.2** 请求组件:Inula-request

Inula-request是openInula框架提供的网络请求库,提供了与Axios相同的API,如get、post、put、delete等方法,用于发送各种类型的rest请求。支持请求和响应拦截器、请求取消、错误处理等功能。

此外,openInula框架今年还将提供Vue2openInula工具和openInula-test-library。通过使用Vue2openInula工具,开发者可以降低Vue项目迁移的复杂度,减少手动转换的工作量,并更快地适应openInula的开发模式。通过使用openInula-test-library,开发者可以更加方便地为openInula应用编写可维护的测试用例,提高代码的质量和稳定性。

6.3 路由组件:Inula-router

Inula-router在设计上考虑了与主流路由库的兼容性,提供了相似的API和使用方式。开发者可以轻松地将现有项目中使用的React-Router、Path-to-RegExp、History、Connected-React-Router等库替换为Inula-router。

通过集成路由相关的功能于一身,Inula-router简化了路由管理的复杂性,减少了项目中需要引入和维护的第三方依赖。

6.4 国际化组件:Inula-intl

Inula-intl提供了与React-intl相同的API,例如FormattedMessage、FormattedNumber、FormattedDate等,开发者可以轻松替换React-intl。

openInula是一个开源项目,欢迎广大开发者参与贡献,共同推进框架的发展和完善。

🌈代码仓地址:gitee.com/openinula/i...

💻官网:www.openinula.net/

相关推荐
晓夜残歌2 小时前
安全基线-rm命令防护
运维·服务器·前端·chrome·安全·ubuntu
inxunoffice2 小时前
批量删除 PPT 空白幻灯片页面
前端·powerpoint
Setsuna_F_Seiei4 小时前
前端切图仔的一次不务正业游戏开发之旅
前端·游戏·cocos creator
laimaxgg4 小时前
Qt窗口控件之颜色对话框QColorDialog
开发语言·前端·c++·qt·命令模式·qt6.3
爱编程的鱼5 小时前
Unity—从入门到精通(第一天)
前端·unity·ue5·游戏引擎
默默无闻 静静学习5 小时前
sass介绍
前端·sass
大怪v5 小时前
前端佬们,装起来!给设计模式【祛魅】
前端·javascript·设计模式
vvilkim6 小时前
Vue.js 插槽(Slot)详解:让组件更灵活、更强大
前端·javascript·vue.js
学无止境鸭6 小时前
uniapp报错 Right-hand side of ‘instanceof‘ is not an object
前端·javascript·uni-app
豆豆(设计前端)6 小时前
一键秒连WiFi智能设备,uni-app全栈式物联开发指南。
前端