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

前端:如何基于Node.JS从零开始构建自动化打包工作流?

时间:2023-03-20 19:51:08 科技观察

前言NodeJS在前端领域扮演着越来越重要的角色。它不仅可以让前端工作者使用javascript编写后端代码,还可以方便地构建响应速度快、易于扩展的网络应用。Node.js采用事件驱动、非阻塞I/O模型,轻量高效,非常适合在分布式设备上运行数据密集型实时应用。因此,作为一名优秀的前端工程师,了解和掌握Node.js是非常有必要的。接下来,笔者将利用H5-Dooring项目中的实时在线代码下载功能,为大家展示如何从零开始构建在线自动化打包工作流程。你将获得设计在线工作流的基本思路如何使用nodejs常用的API文件并支持zip包文本的前端下载我们用过gulp、webpack等自动化工具,它们可以轻松帮助我们打包编译代码,比较优雅的写出我们的项目代码。但是仔细一想,我们可以发现,这些产品的背后都有nodejs和babel作为底层支持。我们无非就是设计一个架构模式,通过babel的loader和nodejs的服务能力,将代码进行JS-AST-JS的处理(这里忽略css和插件处理)。吹牛之后,我们开始介绍如何设计在线工作流程。一、设计在线工作流的基本思路在线工作流是一个总称。其实任何一个产品线都有自己独特的工作流程,但最终还是要回归到业务上。所以笔者在这里专门介绍一下H5-Dooring实时下载代码的在线工作流程。我们来看看下面的设计流程:以上就是我们要做的在线实时打包下载代码的工作流程。由于nodejs是单线程的,为了不阻塞进程,我们可以使用父子进程通信方式和异步模型来处理复杂耗时的Task,以便通知用户完成状态任务,我们可以使用套接字进行双向通信。当前场景下,代码编译压缩完成后,会通知浏览器,让浏览器显示下载状态弹窗。共有三种状态:进行中、完成和失败。对应下图所示界面:H5-DooringH5-Dooring至于为什么没有下载失败状态,别问我,问了就没有失败(完了,找虐).以上就是H5-Dooring实时编译下载的工作流程设计。至于线上更多的实际需求,我们也可以参考上面的设计来实现。接下来,笔者将详细介绍实现过程。2.nodejs如何使用父子进程如果我们要实现一个自动化的工作流,需要考虑的一个关键问题是何时以及如何执行任务。因为用户需要等待H5页面打包编译压缩后才能下载代码,而这个过程需要一定的时间(8-30s),所以我们可以认为这是一个耗时的任务。当我们使用nodejs作为后台服务器时,由于nodejs本身是单线程的,当用户请求传入nodejs时,nodejs必须等待这个“耗时任务”完成才能处理其他请求,这会导致其他请求在页面要等待任务完成才能继续,所以为了更好的用户体验和流畅的响应,一定不要不考虑多进程处理。好在nodejs设计支持子流程,我们可以把比较耗时的任务放到子流程中去处理,子流程处理完再通知主流程。整个流程如下图所示:nodejs有3种创建子进程的方式,这里简单介绍一下fork的方式。用法如下://child.jsfunctioncomputedTotal(arr,cb){//耗时计算任务}//与主进程通信//监听主进程信号process.on('message',(msg)=>{computedTotal(bigDataArr,(flag)=>{//发送完成信号给主进程process.send(flag);})});//main.jsconst{fork}=require('child_process');app.use(async(ctx,next)=>{if(ctx.url==='/fetch'){constdata=ctx.request.body;//通知子进程开始执行任务,并通过indataconstres=awaitcreatePromisefork('./child.js',data)}//创建异步线程函数createPromisefork(childUrl,data){//加载子进程constres=fork(childUrl)//通知子进程开始workdata&&res.send(data)returnnewPromise(reslove=>{res.on('message',f=>{reslove(f)})})}awaitnext()})在H5-Dooring在线打包的工作流程中,我们将使用child_process的exec方法解析并执行commands命令。至于父子进程的更多应用,大家可以自行探索。3、使用child_process的exec解析执行命令行指令在上面介绍的dooring工作流程中,我们知道为了实现实时打包,我们需要一个H5Template项目作为打包的主人。当用户点击下载时,将页面的jsonschema数据传输到节点服务器,节点服务器对jsonschema数据进行清洗,最终生成一个template.json文件,移交给H5Templatemaster。这时master拿到数据源,打包编译,最后生成可执行的可执行文件。上面的过程很关键,这里我画一个粗略的流程图:H5-Dooring为了实现上面的过程,我们需要两个关键环节:1.处理用户配置的数据,生成一个json文件,然后进行移动到parentH5TemplateVersion2.master中自动执行打包编译脚本。第一个环节很容易实现。我们只需要使用nodejs的fs模块生成文件到指定目录即可。这里,笔者着重介绍第二个环节的实现。我们将json数据生成到H5Template中后,就可以进行打包了,但是这个过程需要自动处理。我们不能像之前启动项目那样手动执行npmstart或yarnstart。我们需要程序自动为我们执行这条命令行命令。查了一下nodejs的API,突然发现child_process的exec方法,可以用来解析command。这正好满足了我们的需求,于是我们开始实施了。代码如下:import{exec}from'child_process'constoutWorkDir=resolve(__dirname,'../h5_landing')constfid=uuid(8,16)constcmdStr=`cd${outWorkDir}&&yarnbuild${fid}`//...exec相关代码constfilePath=resolve(__dirname,'../h5_landing/src/assets/config.json')constres=WF(filePath,data)exec(cmdStr,function(err,stdout,stderr){if(err){//错误处理}else{//成功处理}})上面的代码我们理解起来并不难,我们只需要定义封装好的指令串即可(方法和命令行操作差不多),然后传给第一个exec参数,他会帮我们解析字符串并执行相应的命令行指令。执行完成后,我们可以根据回调函数(第二个参数)中的参数值判断执行结果。整个过程是异步的,所以我们不用担心阻塞。为了实时反馈进度,我们可以使用socket将进度信息推送到浏览器。4、http://socket.io实现消息实时推送在上面介绍的exec中实现解析执行命令行指令,还有一些细节可以优化,比如代码执行过程和执行状态的反馈.因为我们使用的是异步编程,所以请求不会一直等待下去。如果不采取优化措施,用户不可能知道代码是什么时候打包编译的,或者代码是否编译失败。所以这个时候,会采用几种常用的方法。解决方案:客户端请求长轮询postmessage消息推送*websocket双向通信显然使用websocket双向通信会更适合这个项目。这里我们直接使用社区比较流行的http://socket.io。由于官网介绍较多,笔者在此不再一一说明。直接看业务中的代码使用://nodeexec(cmdStr,function(err,stdout,stderr){if(err){console.log('apierror:'+stderr);io.emit('htmlFail',{result:'error',message:stderr})}else{io.emit('htmlSuccess',{result:dest,message:stderr})}})//浏览器端constsocket=io(serverUrl);//...省略其他业务代码useEffect(()=>{socket.on('connect',function(){console.log('connect')});socket.on('htmlFail',function(data){//...});socket.on('disconnect',function(e){console.log('disconnect',e)});},[])这样我们就可以了解到服务器的状态任务流实时反馈给浏览器。5、在服务器端使用jszip压缩文件,支持前端下载zip包,实现前端下载功能其实很简单,因为用户配置的H5工程中包含了各种资源,如css、js、html,image,所以为了提高下载性能方便方便,我们需要将整个网站打包生成一个zip文件供用户下载。原理是使用jszip压缩目录,然后将压缩后的路径返回给前端,前端使用a标签进行下载。至于如何实现目录遍历压缩和遍历读取目录,笔者在此不多说。有兴趣的可以参考作者其他的nodejs文章。6.总结以上教程作者已经集成到H5-Dooring中。对于一些比较复杂的交互功能,也可以通过合理的设计来实现。大家可以自行探索研究。浏览器搜索:H5-Dooring