登上 Github 趋势榜,iMove 原理技术大揭秘!

开发 前端
今天,就通过本篇文章和大家介绍一下 iMove 开源项目,内容包含iMove功能和实现原理、独创的在线代码运行能力,以及如何自动解析节点的npm包依赖,还是有非常多亮点和创新的。

[[376777]]

 我认为前端开发中问题很多,尤其是以下3点。

  • UI老变,导致开发必须跟紧

  • 逻辑挑战,开发也必须改代码,很多后端处理逻辑都在里面

  • 组合接口,这是历史原因,主要是和后端配合导致的。其实没有Node BFF层,都由组件来做,会问题非常多。

最近我们的开源项目 iMove 一天就涨了 280+ star,一举登上了 github 趋势榜第 1 名,取得的成绩还是不错的,说明这个项目定位准确,确确实实解决了开发者问题。

今天,就通过本篇文章和大家介绍一下 iMove 开源项目,内容包含iMove功能和实现原理、独创的在线代码运行能力,以及如何自动解析节点的npm包依赖,还是有非常多亮点和创新的。

关于 iMove

简单讲,其实我们理想的前端可以做以下4点。

  • 逻辑可组装:其实是接口和UI在最小粒度上的复用。

  • 流程可视化:这些可复用的最小单元,可以通过流程来进行编排,继而达到让运营简化的目的。

  • 运营配置收敛:这是因为多套系统导致运营成本很高导致的,统一放到一起最好。

  • 玩法能力沉淀:促使产品将玩法进行沉淀,变成可复用的能力。

对开发者而言,iMove恰好是可以完成这些目标的理想工具。动动鼠标,写一下节点函数,代码导出,放到具体工程里就可以直接使用,是不是很方便?

那么,什么是 iMove?

  1. 它是个工具,无侵入性。

  2. 双击编写函数,编排后的流程可以导出可执行代码,便于在具体项目里做集成。

  3. 测试方便,右键直接执行,此处有创新。

  4. 让开发像运营配置一样完成功能开发,做到复用和Lowcode。

举个栗子

上面这些介绍,也许看着没什么体感,不容易理解,让我们一起来看个例子~

假如有天你接到了一个 详情页购买按钮 的需求:

  • 获取详情页的商品信息

  • 商品信息包含以下

    • 当前用户是否已经领券

    • 商品领券是需要关注店铺还是加入会员

  • 根据返回的商品信息,购买按钮有以下几种形态

    • 如果已经领券,按钮展示 "已领券 + 购买"

    • 如果没有领券

      • 如果领券需要关注店铺,按钮展示 "关注店铺领券 + 购买"

      • 如果领券需要加入会员,按钮展示 "加入会员领券 + 购买"

    • 异常情况时,展示兜底样式

  • 注意:如果用户未登录,唤起登录页
    我们可以将以上这段复杂的业务逻辑转化成如下这段示意伪代码:

  1. // 检查登录 
  2. const checkLogin = () => { 
  3.   return requestData('/is/login').then((res) => { 
  4.     const {isLogin} = res || {}; 
  5.     return isLogin; 
  6.   }).catch(err => { 
  7.     return false
  8.   }); 
  9. }; 
  10.  
  11.  
  12. // 获取详情页数据 
  13. const fetchDetailData = () => { 
  14.   return requestData('/get/detail/data').then((res) => { 
  15.     const { 
  16.       hasApplied, 
  17.       needFollowShop, 
  18.       needAddVip, 
  19.     } = res; 
  20.     if(hasApplied) { 
  21.       setStatus('hasApplied'); 
  22.     } else { 
  23.       if(needFollowShop) { 
  24.         setStatus('needFollowShop'); 
  25.       } else if(needAddVip) { 
  26.         setStatus('needAddVip'); 
  27.       } else { 
  28.         setStatus('exception'); 
  29.       } 
  30.     } 
  31.   }).catch(err => { 
  32.     setStatus('exception'); 
  33.   }); 
  34. }; 
  35.  
  36.  
  37. checkLogin().then(isLogin => { 
  38.   if(isLogin) { 
  39.     return fetchDetailData(); 
  40.   } else { 
  41.     goLogin(); 
  42.   } 
  43. }); 

诚如上述例子所示,虽然业务复杂度并不是非常高,但其背后的沟通和理解成本其实并不低,想必大家在各自业务中遇到的实际场景比这复杂棘手得多。然而,业务逻辑的复杂度决定了代码的复杂度,越复杂的代码越难维护,假如某天你接手了一个逻辑很复杂的他人项目,这其中的维护成本是非常高的。不过这也正是 iMove 所解决的问题之一,面对上述同样的业务需求,我们来看看使用 iMove 是如何开发的。

如上所示,原本晦涩难懂的代码逻辑通过 iMove 以流程图的形式表达了出来,现在产品的业务逻辑一目了然。除此之外, iMove 中的每个节点都是支持编写代码的,而流程图的走向又决定了图中节点的执行顺序,可以说 "流程可视化即天然的代码注释" 。

因此,就从 "易读性" 和 "可维护性" 上来看: iMove 流程可视化的形式 > 产品经理的 PRD 文字描述形式 > 程序代码形式。

应用场景

前端React组件一般在ComponentDidMount里发起请求,根据请求成功的数据完成渲染或其他业务逻辑,这种是完全无UI的Ajax请求处理。除了组件声明周期,就只有各种交互事件里,这里面一般是UI和Ajax混用的场景。

  1. 前端流程,比如点击事件,组件生命周期回调等。

  2. 后端流程,比如Node.js或Serverless领域。

  3. 前端+后端,比如前端点击事件,Ajax请求和后端API。

优势

上文提到的内容只是 iMove 的冰山一角,我们一起来看下 iMove 都有哪些优势:

1)逻辑可复用

面对频繁迭代的日常业务需求,我们一定会遇到许多相似、重复的开发工作。体现在代码中它可以是一段通用的 utils 工具方法,也可以是一段通用的业务逻辑代码(比如分享),但究其本质它们就是一个 代码片段 。为了提高代码的复用,我们往往会将其封装成一些通用的类或函数,然后在各个项目中拷贝粘贴(做得好一点可以将其封装成 npm 包,但修改发布的流程又会稍显繁琐)。

而在 iMove 中,每个可复用的代码片段都可以被封装成流程图中的节点。当想在不同项目中复用逻辑的时候,直接引入对应的节点/子流程即可,每个节点还支持参数配置,进一步提升了节点的复用性,使用体验可以说是非常简单了。

再往后设想一步,假如 iMove 已经在某个业务场景中沉淀了一定量的业务节点,当下次再遇到相似的业务需求时,逻辑部分是否可以直接复用现成的节点拼装而成。这可是大大提升了研发效率,缩短了项目的研发周期。

2)面向函数

在节点的设计方面, iMove 做得比较克制,每个节点其实就是导出一个函数,因此编码体验上几乎没有什么上手成本,只要你有JavaScript基础就能使用。你可以照常 import 其他 npm 包,也不用考虑节点之间的全局变量命名污染等问题,将它当做一个普通的 js 文件即可。

3)流程可视化

我们将流程可视化的这种开发方式称之为 "逻辑编排" ,它的好处(逻辑表达更为直观,易于理解)前文已经介绍过,这里就不再重复赘述。

4)逻辑/UI 解耦

我们在日常业务开发中经常会遇到: UI 样式经常变化,而业务逻辑较为稳定,甚至还会有 ABTest 需求查看改版效果 。

然而许多开发者在组件开发之初并不会设想到将来会有这一步,因此一个业务组件往往会将 "业务逻辑" 和 "UI样式" 耦合在一起。而到了改版的时候才会发现要想抽离和复用业务逻辑并不容易,维护成本也大大增加。

不过当你使用 iMove 开发之后就会发现:组件代码自然而然就拆成了 "业务逻辑" + "UI样式"。而且,不同版本的 UI 可以维护多套,但业务逻辑部分只要交给 iMove 维护一套即可。这样的开发方式不仅可以最大程度地复用业务逻辑代码,而且还提高了项目的可维护性。

5)更简单的代码测试

为了提升 iMove 的使用体验,我们实现了 "浏览器端在线运行节点代码" 的功能。这意味着当完成一个节点的函数功能时,你随时可以在浏览器端 mock 各种输入来测试该节点的运行结果是否符合你的预期。

也就是说,在无须引入测试框架、脱离上下文环境的前提下,你就可以单独对某一个节点的函数进行测试,这大大降低了代码测试的成本和门槛。与此同时,你还可以顺手将每次的测试输入/输出作为测试用例进行保存,渐而形成一份完备的测试用例,这不但可以保障该节点的代码质量,还可以更放心得被引用在其他项目当中。

6)无语言/场景限制

虽然 iMove 本身是一个 JavaScript 工具库,但在我们的设计中 iMove 并没有对使用语言和场景加以限制。也就是说,你不仅可以用 iMove 编排前端项目中的 js 代码,同样也可以用 iMove 编排后端项目中的 java 代码,甚至其他场景的其他语言。而这一切,其实最终只取决于 iMove 将流程图编译出码成什么语言而已。

iMove 原理

在对 iMove 的项目背景有了一定了解后,本文接下来将带大家一起揭开它背后的技术原理~

如何搭建可绘制的流程图应用?

抛开 iMove 偏开发的功能不说,大家完全可以把它当做一个流程图的绘制工具来使用(画完之后还可以导出图片保存到本地~)。那么 iMove 又是如何绘制流程图的呢?想必大家一定对此比较好奇,这里必须得给蚂蚁团队做的 X6 引擎点个赞 :+1: ,真的很好用~

X6 本身并不和 React 或 Vue 捆绑,因此你可以在任何框架内配合 X6 一起使用。除此之外,它提供了一系列开箱即用的交互组件和简单易用的节点定制能力,只需简单调用一些 API 就能快速实现相应的功能。

点击i

后续我们会专门出一篇文章介绍 iMove 如何使用 X6 快速搭建一个可绘制的流程图应用。

imove的核心就是基于x6协议实现的。

  • 有节点:利用x6的可视化界面,便于复用和编排。

  • 有指向边:即流程可视化,简单直观,边上还可以带参数。

  • 有function和schema2form,支持函数定义,这是面向开发者的。支持form,让每个函数都可以配置入参,这部分是基于阿里开源的form-render实现的。
    整个项目难度不大,基于x6和form-render进一步整合,将写法规范化,将编排工具化,这样克制的设计使得imove具备小而美的特点,便于开发使用。

如何将流程图编译成可运行代码?

相比于绘制流程图, iMove 更吸引人的是它可以将流程图编译成业务项目中可实际运行的代码。

在线编译 vs 本地编译

首先, iMove 既支持浏览器端在线编译提供 zip 包下载,也支持本地命令行 watch 实时编译出码。

1)在线编译

为了降低 iMove 的上手成本,我们加入了浏览器端在线编译出码的功能,这样开发者无须安装工具就能直接下载编译好的代码。

具体该如何实现呢?经过调研,我们发现 jszip 是一个集读/写/改 zip 文件于一身的 JavaScript 库,而且还支持浏览器端运行。因此,我们完全可以按照出码的文件目录生成一个 json 丢给 jszip 打包,最后再用 file-saver 提供下载即可。

  1. // key 是"文件/目录名",value 是对应的"文件内容" 
  2.   "nodeFns": { 
  3.     "node1.js""..."
  4.     "node2.js""..."
  5.     "index.js""..." 
  6.   }, 
  7.   "context.js""..."
  8.   "dsl.json""..."
  9.   "index.js""..."
  10.   "logic.js""..." 

2)本地编译

在线编译的方式固然简单,但在项目开发中会遇到一个问题: 每次修改代码都需要重新下载 zip 包并解压到指定目录,尤其是调试时需要频繁修改代码会非常不便 。为了解决这个问题, iMove 提供了本地编译的方式,通过 watch 流程图的保存操作,实时地编译出码到业务项目中。

具体该如何实现呢?上述问题的关键在于: 本地如何监听浏览器端的流程图保存操作 。但是反过来想,为什么不可以是 流程图在保存时发送消息通知本地呢? 这么一来,我们既可以使用 socket.io 等类库在浏览器和本地之间建立 websocket 通信,也可以使用 koa / express 等类库在本地启动一个 http 服务器,只要在接收到流程图保存信号时触发编译出码即可。

iMove 代码运行基本原理

解决完 iMove 如何编译代码的问题,再来看看 iMove 编译的代码又是如何运行的。

要想运行代码, iMove 需要解决两个核心问题:

  1. 如何按流程图的顺序依次执行节点

  2. 如何处理数据流(比如上一节点的返回值是下一节点的输入)

RxJS 看起来是个不错的选择,函数响应式编程似乎天然解决上面的问题。但考虑到它的上手成本无疑会对 iMove 的使用者造成巨大的心智负担,因此最终我们并没有采用这套方案。

1)对于第一个顺序执行问题, iMove 采用了一种低成本的方式来解决:

  • 首先, X6 支持导出流程图的 json 数据,我们可以将其精简处理后保存为一份 DSL 文件。

  • 其次,根据这份 DSL 文件,我们可以从中提取出每个节点的代码部分成为单独的文件,进而构成一个 节点函数集 。

  • 最后,接下来只要按照 节点和边的上下游关系 顺序调用相应的节点函数即可。

2)对于第二个数据流问题, iMove 考虑到实际应用中节点对数据操作的各种场景,一共设计了四种数据读写方式:

  • config:只读型,每个节点的投放配置,节点之间互不干扰。

  • payload: 只读型, logic.invoke 触发逻辑时,可以传一个 payload 值,每个节点都能读取该值。

  • pipe:只读型,上一个节点的输出是下一个节点的输入。

  • context: 可读写,某一个逻辑流中的公有数据,如果祖先节点又通过 setContext 设置数据,那么子孙节点可以跨节点通过 getContext 访问到该数据

至此, iMove 解决了流程图代码运行的问题。如果你有注意 iMove 编译后的代码,就能看到如下结构:

理解Flow

Flow的基本概念很简单,就是一个有向无环图(DAG),数据在节点间流动。

  • 节点 Node

    • 节点是组成流的主要单元,负责对流入节点的数据进行处理,并输出到后续节点进行进一步的处理。

  • 端口 Port

    • 每个节点拥有输入和输出端口,输入端口负责数据流入节点,输出端口负责数据流出节点。每个节点都可能拥有一个或者多个输入和输出端口。

  • 连接 Link

    • 一个节点的输出端口连接到另一个节点的输入端口,节点处理好的数据通过连接流入其后的节点。

Flow的实现基本思路就是用一个函数function实现一个节点,输入端口映射为函数的输入参数。输出端口映射为函数的返回值。

流中有一个节点被设置为终点节点(End Node),通过节点间的连接关系,以终点节点开始通过连接搜索所有的依赖关系(树形查找),得到一个节点运行的栈。例如上图,我们就可以得到一个 [node1,node2, node3] 这样的栈。按顺序出栈的方式执行每一个节点的功能就可以运行整个流。(注意,这是一个简单版本的Flow的实现,仍然是一个批处理,不是streaming)

需要假定每一个节点的功能是无状态的,这样就可以利用输入输出端口对计算结果进行缓存,但输入值是已经运算过的值的时候,不需要运算,直接返回已经计算过的值。

以上是Flow-base programing(FBP)里的Flow概念。其实这和imove里的概念是一样的。imove基于x6,x6解决了DAG实现和可视化问题,再结合节点扩展函数写法,继而实现面向开发者的逻辑编排工具。殊途同归,就是这么简单。

如何在线运行节点代码?

iMove 有个比较 cool 的功能就是可以在浏览器端在线运行节点函数代码,实时看到运行结果。这种 所见即所得 的开发体验对使用者来说还是很友好的,不但测试调试的成本大大降低,还能成为测试用例集进一步保障节点质量。

在 iMove 里编写代码,双击节点即可。

右键执行代码,即可完成对单个节点的测试。

打开运行面板,填写对应参数,即可执行具体代码。

在线运行 iMove 节点代码需要解决以下一些问题:

  • 浏览器端如何直接运行节点中的 import/export 等 esm 规范代码

  • 节点中 import 进来的 npm 包还有可能是 cjs 规范,浏览器又该如何运行

  • 同时选中多个节点时,又该如何运行代码

iMove 实现的背后原理主要还是借助 http-import ,后面我们会专门出一篇文章介绍它的实现原理,敬请期待~

如何自动解析节点的 npm 包依赖?

由于每个 iMove 的节点都支持 import 其他 npm 包,因此每个节点都有 npm 依赖。但是,如果这项工作让开发者手动填写体验会非常差,因此我们做了自动解析的功能。

原理其实比较简单,了解 npm view 命令即可。就拿 npm view lodash.get 举例来说,查看命令行输出可以看到:

  1. $ npm view lodash.get 
  2.  
  3.  
  4. lodash.get@4.4.2 | MIT | deps: none | versions: 13 
  5. The lodash method `_.get` exported as a module. 
  6. https://lodash.com/ 
  7.  
  8.  
  9. dist 
  10. .tarball: https://r.cnpmjs.org/lodash.get/download/lodash.get-4.4.2.tgz 
  11. .shasum: 2d177f652fa31e939b4438d5341499dfa3825e99 
  12.  
  13.  
  14. maintainers: 
  15. - phated <blaine.bublitz@gmail.com> 
  16.  
  17.  
  18. dist-tags: 
  19. latest: 4.4.2 
  20.  
  21.  
  22. published over a year ago by jdalton <john.david.dalton@gmail.com> 

如上所述,该命令成功获取到 lodash.get 这个包的最新版本。当然, npm view 命令不适合在浏览器端执行,但究其本质都会走网络请求查询。我们再用 npm view lodash.get --verbose 就能看到执行过程中其实发起了请求: https://r.cnpmjs.org/lodash.get 。

接下来就很简单了,只要根据 import 语法规则用正则匹配出节点代码中的依赖,再调用上面的 api 就可以自动解析出该节点的包依赖了。

总结

在UI侧有imgcook这样的设计稿转代码的工具,应对变化是足够的。但在逻辑领域,真正能解决问题的又面向开发者的少之又少,imove算这个方向的一个探索。相信通过上面的讲解,大家都能够感受到它的魅力。

imove的口号是 Move your mouse, generate code from flow chart ,即动动鼠标,写一下节点函数,代码导出,放到具体工程里就可以直接使用。像运营配置一样开发,这已经不是愿望,而是已经实现了的。

责任编辑:张燕妮 来源: Alibaba F2E
相关推荐

2019-08-13 09:35:13

人工智能人脸识别技术

2019-12-26 15:31:17

腾讯框架开源

2023-04-19 08:14:24

2023-06-26 18:03:26

btrace 2.0开源

2021-02-17 10:55:32

XRVRAR

2020-04-16 15:10:56

COBOLGitHub编程语言

2017-08-24 09:19:20

分解技术揭秘

2021-08-05 10:46:59

GitHub代码开发者

2019-11-18 09:44:51

GitHub代码开发者

2019-11-08 07:27:34

网络网络体检网络技术

2019-10-31 08:50:25

网络网络体检网络技术

2019-11-07 22:12:50

网络网络体检网络技术

2009-10-23 13:32:34

.NET CRL

2012-11-02 13:13:25

笔记本

2019-08-08 16:37:31

开源技术 数据

2009-09-07 09:55:19

.NET技术

2023-10-04 10:38:38

模型方法

2012-08-08 09:29:41

App Store刷榜

2019-08-20 15:22:40

GitHub代码开发者

2019-12-02 14:48:43

技术互联网网络
点赞
收藏

51CTO技术栈公众号