当前位置: 首页 > 科技观察

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

时间:2023-03-12 19:16:35 科技观察

我觉得前端开发存在很多问题,尤其是以下3点。UI变老了,开发要跟上逻辑的挑战,开发也要改代码,很多后端处理逻辑都在里面。组合接口,这个是历史原因,主要是配合后台造成的。实际上没有NodeBFF层,都是组件做的,会带来很多问题。最近,我们的开源项目iMove一天获得280+星,一举登上github趋势榜第一名。取得的成绩还是不错的,说明这个项目定位准确,确实解决了开发者的问题。今天通过这篇文章,给大家介绍一下iMove开源项目。内容包括iMove功能及实现原理、原线上代码运行能力、如何自动解析节点的npm包依赖等。亮点和创新点还是很多的。简单说iMove,其实我们理想的前端可以做到以下4点。逻辑可组装:其实就是接口和UI最小粒度的复用。流程可视化:这些可重用的最小单元可以通过流程进行编排,进而达到简化操作的目的。运营配置收敛:这是由于多系统导致的运营成本高,最好统一放在一起。玩法能力沉淀:促使产品沉淀玩法,将其转化为可复用的能力。对于开发者而言,iMove是实现这些目标的理想工具。动动鼠标,写一个node函数,将代码导出,放到具体项目中直接使用,是不是很方便?那么,什么是iMove?它是一种工具,不是侵入性的。双击编写功能,编排后的流程可以导出可执行代码,方便集成到具体项目中。测试方便,右键直接执行,这里有创新。让开发完成操作配置等功能开发,实现复用和Lowcode。例如,上面的介绍可能不太好看,也不容易理解。举个例子吧~如果有一天你在详情页收到一个购买按钮的请求:在详情页获取商品信息。产品信息包括以下当前用户是否已经收到优惠券?用户是否需要关注店铺或成为会员?根据返回的商品信息,购买按钮有以下几种形式。关注店铺,按钮显示“关注店铺领券+购买”如需加入会员领券,按钮显示“加入会员领券+购买”,如有异常,显示底部样式备注:如果用户没有登录,我们可以调用登录页面将上面复杂的业务逻辑转化为如下示意图伪代码://checkloginconstcheckLogin=()=>{returnrequestData('/is/login').then((res)=>{const{isLogin}=res||{};returnisLogin;}).catch(err=>{returnfalse;});};//获取详情页数据constfetchDetailData=()=>{returnrequestData('/get/detail/data').then((res)=>{const{hasApplied,needFollowShop,needAddVip,}=res;if(hasApplied){setStatus('hasApplied');}else{if(needFollowShop){setStatus('needFollowShop');}elseif(needAddVip){setStatus('needAddVip');}else{setStatus('exception');}}}).catch(错误=>{setStatus('exception');});};checkLogin().then(isLogin=>{if(isLogin){returnfetchDetailData();}else{goLogin();}});如上例,虽然业务的复杂度不是很高,但背后的沟通和理解成本其实并不低。想必大家在各自业务中遇到的实际场景要比这复杂和棘手得多。但是,业务逻辑的复杂度决定了代码的复杂度,越复杂的代码越难维护。如果哪天接手一个逻辑复杂的项目,维护成本会非常高。然而,这恰恰是iMove解决的问题之一。面对上述相同的业务需求,我们来看看如何使用iMove进行开发。如上图,原本晦涩难懂的代码逻辑通过iMove以流程图的形式表现出来,现在产品的业务逻辑一目了然。另外,iMove中的每个节点都支持代码编写,流程图的走向决定了图中节点的执行顺序。可以说“流程可视化是一种天然的代码注释”。因此,从“易读性”和“可维护性”的角度来看:iMove流程可视化形式>产品经理的PRD文本描述形式>程序代码形式。应用场景前端React组件一般在ComponentDidMount发起请求,根据成功的请求数据完成渲染或其他业务逻辑。这是一个完全无UI的Ajax请求处理。除了组件声明循环,就只有各种交互事件,一般是UI和Ajax混合的场景。前端流程,如点击事件、组件生命周期回调等。后端流程,如Node.js或Serverless域。前端+后端,比如前端点击事件、Ajax请求和后端API。优势以上所提及的内容只是iMove的冰山一角。让我们来看看iMove的优势:1)可重用的逻辑。面对频繁迭代的日常业务需求,我们肯定会遇到很多相似重复的开发工作。体现在代码中,可以是通用的utils工具方法,也可以是通用的业务逻辑代码(比如分享),但本质上都是一个代码片段。为了提高代码的复用性,我们往往会封装成一些通用的类或者函数,然后复制粘贴到各个项目中(做的好的话可以打包成npm包,但是修改的过程发布会稍微复杂一些。麻烦)。在iMove中,每个可重用的代码片段都可以封装到流程图中的一个节点中。当你想在不同的项目中复用逻辑时,可以直接导入相应的节点/子流程。每个节点还支持参数配置,进一步提高了节点的复用性。用户体验可以说是非常简单。再进一步想象,如果iMove在某个业务场景下已经积累了一定数量的业务节点,下次遇到类似的业务需求时,逻辑部分是否可以直接复用现成的节点组装。这大大提高了研发效率,缩短了项目的研发周期。2)功能导向在节点设计上,iMove相对克制。每个节点实际上都导出了一个函数,所以在编码体验上几乎没有成本上手,只要有JavaScript基础就可以使用。你可以像往常一样导入其他npm包,也不用担心节点间的全局变量命名污染,把它当成一个普通的js文件就行了。3)过程可视化我们把这种过程可视化的开发方法称为“逻辑排列”。它的好处(逻辑表达更直观易懂)前面已经介绍过,这里不再赘述。4)逻辑/UI解耦我们在日常业务开发中经常会遇到:UI风格变化频繁,但业务逻辑相对稳定,连ABTest都需要检查改版效果。然而,很多开发者在组件开发之初并没有想象到未来这一步,所以一个业务组件往往耦合了“业务逻辑”和“UI风格”。但是当版本改版的时候,会发现业务逻辑的抽取和复用并不容易,维护成本也会大大增加。但是当你使用iMove进行开发时,你会发现组件代码很自然地拆分成了“业务逻辑”+“UI风格”。而且,不同版本的UI可以维护多套,而业务逻辑部分只需要交给iMove维护一套即可。这样的开发方式不仅可以最大程度的复用业务逻辑代码,还可以提高项目的可维护性。5)更简单的代码测试为了提升iMove的使用体验,我们实现了“在浏览器端在线运行节点代码”的功能。这意味着当你完成一个节点的功能后,你可以随时在浏览器端模拟各种输入来测试节点的运行结果是否符合你的预期。也就是说,你可以在不引入测试框架、断章取义的情况下独立测试某个节点的功能,大大降低了代码测试的成本和门槛。同时,您还可以将每一次测试输入/输出保存为一个测试用例,逐步形成一个完整的测试用例,既保证了节点的代码质量,又可以在其他项目中进行引用。6)无语言/场景限制虽然iMove本身是一个JavaScript工具库,但在我们的设计中iMove并没有对使用的语言和场景做限制。也就是说,你不仅可以用iMove来编排前端项目中的js代码,还可以用iMove编排后端项目中的java代码,甚至其他场景下的其他语言。而这一切,其实最终还是取决于iMove将流程图编译成什么语言。iMove原理在对iMove的项目背景有了一定的了解后,本文将带你揭开它背后的技术原理~如何搭建一个可绘制的流程图应用?不管iMove开发的功能如何,你都可以把它当作流程图绘制工具来使用(也可以画完后导出图片保存到本地~)。那么iMove是如何绘制流程图的呢?想必大家对此一定很好奇,这里不得不给蚂蚁团队做的X6引擎点个赞:+1:,真的很好用~X6本身没有绑定React或者Vue,所以可以用它在X6的任何框架中一起使用。此外,它还提供了一系列开箱即用的交互组件和简单易用的节点定制能力,只需调用一些API即可快速实现相应的功能。点我后,我们会出一篇专题文章,介绍iMove如何使用X6快速搭建可绘制流程图应用。imove的核心是基于x6协议实现的。有节点:使用x6的可视化界面,便于复用和编排。有尖边:即过程可视化,简单直观,边上也可以附加参数。有function和schema2form,支持函数定义,是给开发者用的。支持表单,使每个函数都可以配置输入参数。这部分是基于阿里开源的form-render实现的。整个项目并不难。基于x6和form-render的进一步融合,规范了书写,工具化了布局。这样内敛的设计让imove变得小巧美观,方便开发使用。如何将流程图编译成可运行的代码?与画流程图相比,iMove更吸引人的地方在于它可以将流程图编译成真正可以在业务项目中运行的代码。OnlineCompilationvsLocalCompilation首先,iMove不仅支持浏览器端在线编译提供zip包下载,还支持本地命令行watch实时编译代码。1)在线编译为了降低iMove的上手成本,我们增加了在线编译并在浏览器端输出的功能,方便开发者直接下载编译后的代码,无需安装工具。如何实现?经过研究,我们发现jszip是一个读取/写入/修改zip文件的JavaScript库,同时也支持浏览器端操作。因此,我们可以根据输出的文件目录生成一个json丢给jszip打包,最后使用file-saver提供下载。//key为“文件/目录名”,value为对应的“文件内容”{"nodeFns":{"node1.js":"...","node2.js":"...",index.js":"..."},"context.js":"...","dsl.json":"...","index.js":"...","逻辑.js":"..."}2)本地编译和在线编译的方式很简单,但是在项目开发中会有一个问题:每次修改代码,都需要重新下载zip包和解压到指定目录,尤其是调试的时候需要经常修改代码,非常不方便。为了解决这个问题,iMove提供了本地编译的方式,通过watch流程图的保存操作,将代码实时编译输出到业务项目中。如何实现?上述问题的关键在于:如何在本地监听浏览器端的流程图保存操作。但是反过来想想,为什么流程图保存的时候不能发消息通知本地呢?这样我们就可以使用socket.io等类库建立浏览器与本地的websocket通信,或者使用koa/express等类库在本地启动一个http服务器,只要收到流程图保存信号即可只需触发编译并输出代码即可。iMove代码运行的基本原理解决了iMove如何编译代码的问题之后,我们来看看iMove编译出来的代码是如何运行的。iMove要运行代码,需要解决两个核心问题:如何按照流程图的顺序依次执行节点如何处理数据流(比如上一个节点的返回值是下一个节点的输入)RxJS似乎是个不错的选择,函数式Reactive编程似乎很自然地解决了上面的问题。但考虑到其上手成本无疑会给iMove用户造成巨大的心理负担,最终我们没有采用该方案。1)对于第一个顺序执行的问题,iMove采用了一种低成本的方式来解决:首先,X6支持导出流程图的json数据,我们可以将其简化保存为DSL文件。其次,根据这个DSL文件,我们可以将每个节点的代码部分提取到一个单独的文件中,然后形成一个节点功能集。最后,你需要做的就是按照节点和边的上下游关系顺序调用相应的节点函数。2)对于第二个数据流问题,iMove考虑到实际应用中节点对数据进行操作的各种场景,设计了四种数据读写方式:config:只读,各节点下发配置,不干扰彼此。payload:只读,logic.invoke触发逻辑时,可以传递一个payload值,每个节点都可以读取该值。pipe:只读,上一个节点的输出是下一个节点的输入。context:可读可写,逻辑流程中的公共数据,如果祖先节点通过setContext设置数据,那么后代节点可以通过getContext跨节点访问数据至此,iMove解决了运行流程图代码的问题。如果你关注iMove的编译代码,你可以看到如下结构:理解FlowFlow的基本概念非常简单,它是一个有向无环图(DAG),数据在节点之间流动。NodeNode是流量的主要单元,负责处理流入该节点的数据,并输出到后续节点做进一步处理。Port端口每个节点都有输入和输出端口。输入端口负责数据流入节点,输出端口负责数据流出节点。每个节点可能有一个或多个输入和输出端口。连接链路一个节点的输出端口连接到另一个节点的输入端口,该节点处理的数据通过连接流入后续节点。Flow的基本思想是用一个函数来实现一个节点,输入端口映射到函数的输入参数。输出端口映射到函数的返回值。流程中的一个节点被设置为结束节点(EndNode)。通过节点间的连接关系,从末端节点开始,通过连接(树搜索)搜索所有的依赖关系,得到一个节点运行栈。比如上图中,我们可以得到一个像[node1,node2,node3]这样的栈。执行每个节点的功能以弹出堆栈可以运行整个流程。(注意这是简化版的Flow,仍然是批处理,不是流)需要假设每个节点的功能是无状态的,这样计算结果可以使用输入输出端口进行缓存,但是输入的值是计算过的值时,不需要计算,直接返回计算过的值。以上就是Flow-baseprogramming(FBP)中的Flow概念。其实这个和imove是同一个概念。imove基于x6,x6解决了DAG实现和可视化的问题,结合节点扩展函数的编写方式,进而实现了一个面向开发者的逻辑编排工具。就这么简单。如何在线运行节点代码?iMove有个很酷的功能,可以在浏览器端在线运行节点功能代码,实时查看运行结果。这种所见即所得的开发体验非常人性化,不仅大大降低了测试调试的成本,而且作为一套测试用例,进一步保证了节点的质量。要在iMove中编写代码,只需双击节点即可。右键执行代码,完成单节点测试。打开运行面板,填写相应的参数,即可执行具体代码。在线运行iMove节点代码需要解决以下问题:如何在浏览器端直接运行节点中的import/export。node中导入的npm包也可能是cjs规范。如何运行浏览器同时选中多个有节点时如何运行代码?iMove的实现原理主要是基于http-import。后面我们会专门出一篇文章介绍它的实现原理,敬请期待~如何自动解析节点的npm包依赖?由于每个iMove节点都支持导入其他npm包,因此每个节点都有npm依赖项。但是如果开发者手动填写这个工作,体验会很差,所以我们实现了自动解析的功能。原理其实比较简单,理解npmview命令即可。以npmviewlodash.get为例,如果查看命令行输出,您可以看到:$npmviewlodash.getlodash.get@4.4.2|MIT|deps:none|versions:13Thelodashmethod`_.get`exportedasamodule。https://lodash.com/dist.tarball:https://r.cnpmjs.org/lodash.get/download/lodash.get-4.4.2.tgz.shasum:2d177f652fa31e939b4438d5341499dfa3825e99maintainers:-phateddist-tags:latest:4.4.2publishedoverayearagobyjdalton如上所述,该命令成功获取了最新版本的lodash.get包。当然,npmview命令并不适合在浏览器端执行,但本质上,它会使用网络请求查询。我们可以通过npmviewlodash.get--verbose来查看在执行过程中是否真的发起了请求:https://r.cnpmjs.org/lodash.get。接下来的步骤就很简单了,只要将节点代码中的依赖按照import语法规则进行正则表达式匹配,就可以调用上面的API自动解析出节点的包依赖了。综上所述,UI端有imgcook等设计稿转换工具,足以应对变化。但是在逻辑领域,真正能够解决问题的面向开发者的解决方案少之又少。imove就是这方面的探索。相信通过上面的讲解,大家可以感受到它的魅力了。imove的口号是Moveyourmouse,generatecodefromflowchart,即动动鼠标,编写节点函数,导出代码,放到具体项目中直接使用。像操作配置一样开发不再是一个愿望,而是一个现实。