当前位置: 首页 > Web前端 > HTML

厌倦了编写活动页面?快来获取页面构建器吧!

时间:2023-04-02 19:58:00 HTML

前言如果你经常接触一些公司的活动页面,你可能经常会遇到以下问题:这些项目周期短,需求频繁,迭代快,技术要求低,成长空间小。但是,我们仍然在马不停蹄地逐一追赶产品的需求。随着公司规模的扩大,我们不能无限制地增加人力,不断重复这些活动。这里就不介绍一些具体的概念了,因为要介绍的概念太多了,作为前端的我们,直接写代码就好了!!!!如果想了解更多,欢迎访问:源地址blogsGoal我们的目标是实现一个页面制作后台,在后台我们可以为页面选择组件-->布局样式调整-->在线发布-->编辑并修改这样的流程操作。架构设计首先是提供组件供用户选择,然后我们需要一个组件库,然后我们需要调整所选组件的布局样式,所以我们需要一个页面编辑后台,然后我们需要渲染编辑后的输出数据进入真实页面,所以我们需要一个节点服务和一个模板模板来填充。发布上线。这直接连接到每个公司的内部发布系统。我们不会在这里详细说明。最后一个编辑功能是修改配置,所以我们需要一个数据库,这里我选择使用mysql。当然你也可以顺便做权限管理,页面管理……等等。扯了这么久,先画个图了解下大概流程:打开组件实现首先我们来实现组件部分,因为组件关联的是后台编辑的预览和最终发布的使用。组件设计我们应该尽量保持组件对外的一致性,这样我们在渲染的时候就可以对外提供统一的数据接口。这里我们的技术选型是基于Vue的,所以后面的代码部分也是以Vue为主,但还是一样,其他语言也类似。根据上图,我们的组件会被一个一个拆分出来发布到npm仓库中。为什么要这样设计?其实我之前也考虑过设计一个组件库,所有的组件都包含在一个组件库中,这样只需要发布一个组件库包,使用的时候可以按需加载。后来在实践过程中,发现这样不适合协同开发。其他前端如果要贡献组件,接入的改造成本也很高。比如:小明在自己的业务中写了一个Button组件。该组件经常被其他项目重用。他想把这个组件贡献给我们的系统,供模板使用。如果是组件库,他必须先Pull我们组件库的代码,然后按照组件库的标准格式提交。这样一来,懒惰的小明未必愿意这么做。最酷的方法当然是在本地构建一个npm库。我们不关心我们是用TypeScript还是其他的进行开发,我们也不关心我们选择的Css预处理器。我们不关心,甚至不关心编码标准的ESLint。最后只传编译好的文件。这避免了组件库的约束。依托NPM完善的release/pull和版本控制机制,让我们少做一些额外的工作,快速搭建平台。说了这么多,那代码呢?,我们以一个Button为例,我们提供这样的外部组件:{{data.context}}

可以看到我们对外只暴露了一个props。这种方式的好处是可以统一组件暴露的数据,组件内部可以随心所欲的发挥。注意这里我们还可以引入一些第三方的组件库,比如mint-ui之类的。后台编辑的实现在写代码之前,先考虑一下需要实现哪些功能:属性编辑区,为用户提供编辑组件内部props的功能;一个组件选择区,允许用户选择他们需要的组件;组件预览区,功能编辑区的实现,为用户提供拖拽排序页面预览为了方便,我们先实现组件的属性编辑功能。我们需要考虑组件公开哪些可配置信息。如何将这些可配置信息同步到后台编辑区供用户编辑,一个按钮的可配置信息可能是这样的:如果把这些配置都写在后台库里,根据当前选择的组件加载不同的配置,维护会比较麻烦,而且随着组件的增多,会变得臃肿,所以我们可以把这些配置存储在服务端,后台只需要按照存储的规则解析即可。例如,我们实际上可以这样存储编辑配置:[{"blockName":"按钮布局设置","settings":{"src":{"type":"input","require":true,"label":"按钮文案"}}}]我们在编辑后台,通过接口请求这些配置,然后我们就可以渲染规则了:/***根据类型,选择创建对应的组件*@param{VNode}vm*@returns{any}*/createEditorElement(vm:VNode){letdom=nullswitch(vm.config.type){case'align':dom=this.createAlignElement(vm)break;case'select':dom=this.createSelectElement(vm)中断;case'actions':dom=this.createActionElement(vm)break;case'vue-editor':dom=this.createVueEditor(vm)中断;default:dom=this.createBasicElement(vm)}returndom}组件选择区首先我们需要考虑一下,组件如何注册?因为组件是用户使用的在选择的时候,我们需要渲染组件,所以我们可以提供一个节点脚本来遍历需要的组件,并安装和注册组件://定义渲染模板和路径varOUTPUT_PATH=path.join(__dirname,'../packages/index.js');console.log(chalk.yellow('Generatingpackagereferencefile...'))varINSTALL_COMPONENT_TEMPLATE='{{name}}';varIMPORT_TEMPLATE='import{{componentName}}from\'{{name}}\'';varMAIN_TEMPLATE=`/*由'./compiler/build-entry.js'自动生成*/{{include}}constcomponents=[{{install}}]constinstall=函数(Vue){组件。map((component)=>{Vue.component(component.name,component)})}/*伊斯坦布尔忽略if*/if(typeofwindow!=='undefined'&&window.Vue){install(window.Vue)}export{install,{{list}}}`;//渲染参考文件vartemplate=render(MAIN_TEMPLATE,{include:includeComponentTemplate.join(endOfLine),install:installTemplate.join(`,${endOfLine}`),version:process.env.VERSION||require('../package.json').version,list:listTemplate.join(`,${endOfLine}`)});//写引用fs.writeFileSync(OUTPUT_PATH,模板);最终呈现的文件如下所示:})}/*istanbulignoreif*/if(typeofwindow!=='undefined'&&window.Vue){install(window.Vue)}export{install,WButton}这也是组件库常用的写法,所以这里的想法是将发布在npm上的组件聚合起来,聚合成一个组件包引用。当我们在后台编辑时,我们需要将它们全部导入:import*asW_UIfrom'../../packages'Vue.use(W_UI)这样我们的组件就注册好了。组件选择区主要提供组件的选项。我们可以遍历组件,提供一个列表供用户选择。当然,如果我们每个组件只提供一个组件名,用户可能不知道这个组件长什么样子,所以我们最好提供一个组件长什么样子的缩略图,这里我们也可以在组件时使用节点脚本发行了。这里要实现的代码很多,所以我就大致说一下流程,因为不是核心逻辑,是可选的,只能说体验会更好:用户开启dev-server使用ChromeToolpuppeteer编写代码和测试服务器脚本,将页面调整为手机模式,并对当前的dev-server进行截图。生成截图文件,上传到节点服务,关联组件。这样就可以在加载组件选择区的时候给组件附加一个缩略图。组件预览区当用户在选择区选中一个组件,我们需要在预览区显示出来,那么我们怎么知道用户选中了哪些组件呢?总不能把所有的组件都预先写进渲染区,用v-if判断选区吧?当然没那么傻,Vue已经提供了动态组件的功能:data">
为什么我们不用缩略图来代替真实的组件呢?一方面,生成的缩略图大小有问题。另一方面,我们需要编辑联动,即编辑区的编辑需要及时反馈给用户。附加问题说了这么多,好像一切顺利,但是在实践中,我们发现了一个明显的问题:我们中间的预览区其实是为了尽可能模拟移动端页面的效果。但是如果我们添加一些包含类似position:fixed样式的组件,我们会发现样式存在明显的问题。典型的如DialogLoading等。所以我们参考了m-ui组件库的设计,将中间预览操作容器展示为iframe。将iframe的大小调整为375*667,模拟iPhone6的移动端,这样就不会有样式问题了。但是这样又产生了一个难点,就是左边的编辑数据如何及时的反映到iframe中呢?没错,就是postMessgae。大致思路如下:使用vuex对数据进行所有修改存储池中的数据通过postMessgae进行同步,这样我们只需要保证数据池中的数据变化能够映射到渲染层的变化即可。比如我们在预览区选择了组件,通过拖拽的方式进行了排序,那么我们只需要通过vuex同步信息即可://action.tsconstaction={setCurrentPage({commit,state},page:number){//更新当前storecommit('setCurrentPage',page)//对应postMessagehelper.postMsgToChild({type:'syncState',value:state})},//...}Template的设计与实现template模板,我参考了基于Vue-cli2.x版本的思想,这里的模板存放在对应的git仓库中。当用户需要构建页面时,可以直接从git仓库中拉取相应的模板。当然拉取之后,也会在本地缓存一个副本,渲染之后直接从本地缓存中读取即可。我们现在关注模板的格式和规范。不管我们对模板使用什么语法,这里我使用与Vue-cli相同的Handlerbars引擎。下面是我们模板的直接设计:"{backgroundColor:'{{bgColor}}',backgroundImage:'{{bgImage}}'?'url({{bgImage}})':null,backgroundSize:'{{bgSize}}',backgroundRepeat:'no-重复'}">{{#components}}<{{name}}class="temp-component":data="{{tostringdata}}"data-type="{{upcasefirstname}}">
{{/components}}
为了简化逻辑,我们将模板设计为流式布局,所有组件逐一堆叠按照下一个顺序排列此文件是我们的vue-webpack-simple模板中的App.vue。我们已经重写了它。这样填完数据后,就可以渲染一个Vue单文件了。这里我只是举个例子。我们也可以实现多页模板等复杂的模板,根据需求拉取不同的模板即可。节点渲染服务后台提交渲染请求时,我们节点服务的主要工作是拉取对应的模板渲染数据,编译拉取,即通过download-git-repo从指定仓库拉取模板插入。编译其实就是使用metalsmith静态模板生成器,将模板作为输入,填入数据,按照handlebars的语法进行渲染。最后生成build目录。在这一步中,我们之前需要的组件将被渲染到package.json文件中。我们来看看核心代码://这就像一个管道,以数据入口为生成源,通过renderTemplateFiles函数编译输出到目标目录build(data,temp_dest,source,dest,cb){letmetalsmith=Metalsmith(temp_dest).use(renderTemplateFiles(data)).source(source).destination(dest).clean(false)returnmetalsmith.build((error,files)=>{if(error)console.log(error);让f=Object.keys(files).filter(o=>fs.existsSync(path.join(dest,o))).map(o=>path.join(dest,o))cb(error,f)})}functionrenderTemplateFiles(data){returnfunction(files){Object.keys(files).forEach((fileName)=>{letfile=files[fileName]//渲染方法file.contents=Handlebars.compile(file.contents.toString())(data)})}}最后得到的是一个Vue项目。这个时候不能直接在浏览器端运行。这涉及到当前发布系统支持的形式。怎么说?如果你公司的发布系统需要在线编译,你可以直接将源文件上传到git仓库,触发仓库的WebHook让发布系统为你发送项目。如果你的发布系统需要你编译并提交编译后的文件发布,那么你可以使用node命令在本地构建并生成HTML、CSS和JS。直接提交给发布系统。至此,我们的任务就差不多完成了~大部分具体的核心固体已经解释清楚了。执行中有什么问题和不妥之处,欢迎大家一起讨论交流!!题外话为了实现这样一个页面构建系统,其实我这里简化了很多东西,旨在提供给大家一个思路。另外,其实我们所有的页面都是在搭建服务器的时候产生的。我们可以在服务器端做很多工作,比如页面性能优化。因为我们有了所有的页面数据,所以我们还可以做页面预渲染,骨架屏,ssr,编译时优化等等。而且我们还可以对输出的活动页面做数据分析~想象空间很大.