当前位置: 首页 > 后端技术 > Node.js

带你7天畅享可视化建站平台

时间:2023-04-03 20:07:50 Node.js

前言对于互联网公司来说,活动运营页面发布频率高,每次开发的人工成本不划算。因此,需要有一个平台系统来满足运营产品快速建站的需求。这类软件产品最早可以追溯到QQ空间、凡客简战等。本项目旨在以最少的代码实现可视化构建、发布、预览、调试等核心功能。解释了每个关键原则。在此之前,希望你对Javascript(ES2015、Reacthooks)、nodejs、webpack等有一定的基础知识,这样你就可以轻松设计和开发自己的建站平台(也许只需要你一个星期ヽ( ̄)▽ ̄)?).首先我们看一下效果:目前支持的编辑器相关功能:将菜单组件拖放到画布中,可以放置的位置用绿框标注,不能放置的位置用a标注红框;选择锚点并移至突出显示。按住鼠标拖动画布四周组件改变宽高,拖动中心改变定位属性面板输入样式,自定义属性配置,实时更新画布预览页面编辑快捷键操作,包括:保存(Ctrl+S)、撤消(Ctrl+Z)、恢复(Ctrl+Y)、删除(DEL)、复制(Ctrl+C)、剪切(Ctrl+X)、粘贴(Ctrl+V)、上移节点(↑),MoveNodeDown(↓),缩放移动画布(按空格键+左键拖动+滚轮缩放),直接拖动左下树结构批量移动节点服务层相关:提供请求接口和路由平台前端页面模板打包(编辑器、预览页)构建:组件仓库分包、编辑器SDK打包开发、组件调试模式命令行脚本预览相关:构建配置页面创建React组件树,动态加载需要的组件JS文件,实现组件的懒加载原理由编辑器、预览页面、服务器、组件仓库4部分组成。用户在编辑器中拖入组件仓库开发好的组件,设置样式和自定义属性,为页面生成JSON配置,提交到服务器存储。预览页面请求返回配置,预览页面根据配置动态下载并渲染组件文件。1.页面JSON配置与渲染的关系无论是在编辑器还是预览页面,页面都是根据JSON配置递归渲染的{"name":"View","style":{"position":"relative","width":"1089px","height":"820px"},"props":{"lazy":true},"el":"wc12","children":[//{...}]}这是一个简单的布局组件映射的JSON结构,包括组件的样式,传入组件的props属性,以及它的唯一键(el)值,以及它的子组件children数组,在array内容是其包裹组件的JSON结构。通过这样的嵌套关系,可以映射成一个组件树。当页面的JSON配置发生变化时,会依赖React的单向数据流重新渲染。这时候我们就需要一个通用的方法来递归创建组件的占位符DIV,但是需要注意的是第一次创建只是一个空壳。返回子组件为空。同时调用异步加载组件js的方法,组件下载完成后自动注入到shell中。该方法的特点是每次我们加载一个新的组件时,都会先从window.comp中查找是否有缓存的组件对象。如果是undefined,说明这是一个全新的组件,我们会去请求相应的JS下载,并且会将window.comp下的组件标记为请求的Promise,所以如果同一个组件并发调用这个方法,则同一个Promise不会重复请求,组件缓存后直接用await获取组件对象。通过以上方法,我们已经可以将JSON配置渲染成页面DOM,动态加载组件JS文件了~2.编辑器中的操作由于我们的页面是按照JSON配置渲染的,所以对页面的任何修改Addition、删除、修改、查询都可以抽象为修改JSON树中节点的数据结构。我们需要一个通用的搜索方法来搜索JSON树,并传入一个标识符来指示操作的类型。searchTree是所有操作的通用方法。它本质上是对JSON配置树的BFS搜索。只要找到对应的关键节点,就根据EnumEdit中的枚举类型对数据进行操作,返回修改后的结果树。dispatchnewconfigurationtree通知reactRe-render。另外,这里的具体操作涉及到各种键盘鼠标事件的绑定。这部分暂时不再赘述。可以自行查询MDN文档。3.样式和自定义属性的注入我们在右侧编辑区填写的内容在渲染时会被注入到对应的组件中。样式会被注入到被包裹组件的外壳中,而自定义属性会作为prop传递给子Components,在组件开发中,我们可以从props中获取到编辑器中填写的属性值。4.编辑历史记录管理历史记录是一个队列的数据结构。如果我们保存1000条记录,每次修改JSON配置,都会加入到队列中。每次入队时发现记录大于1000,则丢弃队头。.当前页面显示的配置是一个指针,指向队列中的某条记录。取消时,指针向后移动,恢复时,指针向前移动。每次触发编译,都会有一个新的配置树入队,不需要手动记录。使用hooks自带的缓存机制很容易实现。5.Canvas缩放处理在构建和使用过程中,我们希望canvas设计尺寸一直是1920(移动端是750),但是viewport显然没有那么大,所以需要使用左上角画布的缩放焦点transform-origin:00,拖动导航幻灯片或按空格键使用滚轮缩放。在这个过程中,transform:scale不断变化,刷新视图。需要注意的是,缩放比例的变化是浏览器的重绘,不会改变原来的DOM空间占用大小,所以画布缩小时会出现较大的空白区域。为了解决这个问题,我们需要在画布外面再包裹一个。图层div,每次改变和缩放画布时,使用getBoundingClientRect()获取缩放后画布的实际宽高,并在外层div上定义这个值,并设置外层div为overflow:hidden,这样滚动窗口的距离会根据外层容器显示滚动条。在计算canvas的高度时,需要计算一个min-height,即当前构建区域的offsetHeight,以保证即使canvas中没有组件时,也能覆盖屏幕。另外,画布的根节点要设置一个padding-bottom:300px,保证底部始终留有空白区域,方便构建者将新组件拖入根节点。6、组件开发每个组件的固有结构,index.js、config.json必须存在(service层会基于这个文件搭建,后面会提到):入口文件是业务代码,配置为JSON文件,决定了编辑器中可以编辑的自定义选项:{"name":"image","staticProps":[{"name":"clicklink","prop":"link","size":"long"},{"name":"是否在新窗口打开链接","prop":"blank","type":"switch",//配置类型,目前支持`text`默认情况下,`select`,`switch`,`color`"size":"long"//配置是否填满编辑器的一行},{"name":"图片地址","prop":"src","size":"long"}],"defaultStyles":{//将组件拖入画布时的默认样式"position":"relative","width":"180px","height":"180px","marginTop":"0px"},"defaultProps":{//将组件拖入画布时的默认自定义属性"src":"http://r.photo.store.qq.com/psb?/V14dALyK4PrHuj/h50SMf97hSy.BJlJw31fagrw.NUaJD83gvydmoGN77w!/r/dLgAAAAAAAAA","blank":true},"hasChild":false,//是否允许子组件,如果不允许拖动时允许移入花押字红色,提示当前节点无法注入"canResizeByMouse":true//是否允许拖动九方掩码修改组件宽高}一张图组件如上,在编辑器中对应的配置项是:7.组件构建和打包这是构建阶段非常重要的一个环节。上面我们说了,每个组件对应一个JS文件,所以我们需要在页面生成之前构建好当前所有的组件。嗯,先找出仓库中的组件,添加打包入口,然后使用webpack的library和libraryTarget配置将组件打包到window[name]下,其中name为组件名称(如Image,View),看一下封装后的组件代码:果然组件js下载执行后直接挂载到window上了。这个时候你可以回头看看开头提到的loadAsync加载组件的方法,是不是恍然大悟。在这里你可能会发现另一个问题。组件都依赖于react库,所以每个组件都是单独打包的,所以要重新加载。包裹有多大?从JS的体量可以看出,这些通用库其实根本没有封装,只是业务代码。这里也使用了webpack的externals属性指定某些依赖直接从window中获取:Soyouwheninjectreactintothewindow?在编辑器或预览页面加载全局配置,即在SDK初始化之前,注入组件依赖的全局对象,以便异步下载后可以直接执行后续组件。编辑器和预览页面的打包没有特别说明,就是普通的webpack配置打包,记得把public模块解压即可。7、开发了组件代理调试平台。这个时候我们要在里面开发业务组件,那么怎么调试呢。通过npmrundev:compdebug=XXX,YYY命令执行调试脚本(XXX为组件名)脚本首先通过process.argv传入的参数获取要调试的组件,然后使用nodeAPI调用webpack-dev-server需要注意的是,这里只是本地创建的组件的代理,在组件资源加载时也需要区分哪些组件需要请求本地调试地址。具体可以参考上面的loadAsync方法。我们在预览页面和编辑器后面加上debug_comp=XXX参数来告诉这个方法组件需要请求一个本地调试地址。最后记住,如果当前用户请求的URL是调试模式,在nodeexpress服务的ejs模板接口中添加webpack-dev-server的codescript标签。8、服务端配置页面管理因为本项目是demo项目,所以没有使用id来区分页面配置。每次提交访问同一个配置文件page.json。在生产环境中,需要连接数据库,为每个配置生成一个ID。获取对应的请求ID,在打开编辑器时返回配置。需要注意的一点是,我们在返回配置界面数据时,需要查找当前build文件夹下存在的js和hash值的映射关系,以保证前端页面能够正确加载最新构建的js地址项目结构├─config.js//前后端通用配置├─comp//组件仓库│├─Image//组件名称││config.json//组件配置││index.js//组件入口││index.less//组件样式│││├─Text││...│││└─View│...│├─script//配置脚本│debugComp.js//组件调试script│webpack.config.comp.js//组件打包配置│webpack.config.edit.js//编辑器打包配置│webpack.config.page.js//预览页面打包配置│├─server//建站平台server││getCompUrlHook.js//生成组件js文件Hashmap││getCompJSONconfig.js//查询组件仓库中所有存在的组件配置││index.js//通用服务器入口││opPageJSON.js//访问配置页面对应的JSON树│││└─template//模板│index.ejs//html渲染模板│page.json//页面配置JSON树│└─src//建站平台前端SDK│context.js//全局状态对象│global.js//全局配置依赖│reducer.js//全局状态管理│├─edit//编辑器││compile.js//编译配置树为组件树││board.js//编辑器可视区域面板││index.js//编辑器总入口││menu.js//编辑器组件菜单││option.js//编辑器属性操作Panel││record.js//操作历史管理││tree.js//构建树状层级展示││search.js//搜索页面配置树方法│││└─style//编辑器风格│└─page//预览页面compile.js//渲染组件配置index.js//预览页面总入口结语本项目完整实现了可视化建站的前后端整体流程在此基础上,可以展开根据需要定制编辑器功能、页面渲染功能等。