页面生成器实现及源码下载
关注公众号 前端开发博客,回复“加群”
加入我们一起学习,天天进步
作者:muwoo
链接:https://zhuanlan.zhihu.com/p/48347377
最近更新:
收到很多私信,大家都想了解构建的源码和过程,因为之前涉及到很多公司内部业务和隐私,所以不方便放出来。应大家强烈要求,这里我将重写一个活动页构建工具供大家参考,持续更新,欢迎start和关注:
//github.com/muwoo/rose
线上预览地址:
//muwoo.github.io/rose/
前言
如果你经常接触一些公司的活动页,可能会经常头疼以下问题:这些项目周期短,需求频繁,迭代快,技术要求不高,成长空间也小。但是我们还是马不停蹄的赶着产品提来的一个个需求,随着公司规模的增加,我们不可能无限制的增加人手不断地重复着这些活动。这里也只是简单的给大家提供一种构建思路,比较笼统。想要了解更多,也欢迎访问。有问题的地方也欢迎拍正 :
//github.com/muwoo/blogs/issues/36
目标
我们的目标是实现一个页面制作后台,在后台中我们可以对页面进行 组件选择 --> 布局样式调整 --> 发布上线 --> 编辑修改
这样的流程操作。
架构设计
首先是要能提供组件给用户进行选择,那么我们需要一个组件库
,然后需要对选择的组件进行布局样式调整,所以我们需要一个页面编辑后台
接着我们需要将编辑产出的数据渲染成真实的页面,所以我们需要一个node服务
和用于填充的template 模板
。发布上线,这个直接对接各个公司内部的发布系统就好了,这里我们不做过多阐述。最后的编辑修改功能也就是针对配置的修改,所以我们需要一个数据库,这里我选择的是用了mysql
。当然你也可以顺便做做权限管理,页面管理....等等之类的活。啰嗦了这么长,我们来画个图,了解下大概的流程:
开撸
组件的实现
首先我们来实现组件这一部分,因为组件关联着后台编辑的预览和最后发布的使用。组件设计我们应该尽量保持组件的对外一致性,这样在进行渲染的时候,我们可以提供一个统一的对外数据接口。这里我们的技术选型是基于 Vue 的,所以下面的代码部分也主要是基于 Vue 的,但是万变不离其宗,其他语言也类似。
根据上图,我们的组件是会被一个个拆分单独发布到 npm
仓库的,为什么这么设计呢?其实之前也考虑过设计成一个组件库,所有组件都包含在一个组件库内,这样只需要发布一个组件库包,用的时候按需加载就好了。后来在实践的过程中发现这样并不合适协同开发,其他前端如果想贡献组件,接入的改造成本也很大。
举个 :小明在业务中写了个Button
组件,这个组件经常会被其他项目复用,他想把这个组件贡献到我们的系统中,被模板使用,如果是一个组件库的话,他首先得拉取我们组件库的代码,然后按照组件库的规范格式进行提交。这样一来,偷懒的小明可能就不太愿意这么干,最爽的方法当然是在本地构建一个npm库,开发选用的是用TypeScript
还是其他的我们不关心,选用的 Css 预处理器我们也不关心,甚至编码规范的ESLint
我们也不关心。最后只需通过编译后的文件即可。这样就避免了一个组件库的约束。依托于NPM完善的发布/拉取,以及版本控制机制,可以让我们少做一些额外的工作,也可以快速的把平台搭建起来。
我们以一个Button
为例,我们对外提供这样的形式组件:
<template><div :style="data.style.container" class="w_button_container"><button :style="data.style.btn"> {{data.context}}</button></div>
</template>
<script>export default {name: 'WButton',props: {data: {type: Object,default: () => {}}}
}
</script>
可以看到我们只对外暴露了一个props
,这样做法的好处是可以统一组件对外暴露的数据,组件内部爱怎么玩怎么玩。注意,这里我们也可以引入一些第三方组件库,比如mint-ui
之类的。
后台编辑的实现
在写代码前,我们先考虑一下需要实现哪些功能:1. 一个属性编辑区,提供给使用者编辑组件内部props
的功能 2. 一个组件选择区,提供使用者选择需要的组件 3. 一个组件预览区,提供使用者拖拽排序页面预览的功能
编辑区的实现
按照顺序,我们先来实现组件的属性编辑功能。我们要考虑,一个组件暴露出哪些可配置的信息。这些可配置的信息如何同步到后台编辑区,让使用者进行编辑,一个按钮的可配置信息可能是这样:
如果把这些配置全部写在后台库里面,根据当前选择的组件加载不同的配置,维护起来会相当麻烦,而且随着组件数量的增加,也会变得臃肿,所以我们可以将这些配置存储在服务端,后台只需要根据存储的规则进行解析便可,举个例子,我们其实可以存储这样的编辑配置:
[{"blockName": "按钮布局设置", "settings": {"src": {"type": "input", "require": true,"label": "按钮文案"}}}
]
我们在编辑后台,通过接口请求到这些配置,便可以进行规则渲染:
/*** 根据类型,选择创建对应的组件 * @param {VNode} vm * @returns {any} */createEditorElement (vm: VNode) {let dom = nullswitch (vm.config.type) {case 'align':dom = this.createAlignElement(vm)break;case 'select':dom = this.createSelectElement(vm)break;case 'actions':dom = this.createActionElement(vm)break;case 'vue-editor':dom = this.createVueEditor(vm)break;default:dom = this.createBasicElement(vm)}return dom}
组件选择区
首先我们需要考虑的是,组件怎么进行注册?因为组件被用户选用的时候,我们是需要渲染该组件的,所以我们可以提供一段 node 脚本来遍历所需组件,进行组件的安装注册:
// 定义渲染模板和路径 var OUTPUT_PATH = path.join(__dirname, '../packages/index.js');
console.log(chalk.yellow('正在生成包引用文件...'))
var INSTALL_COMPONENT_TEMPLATE = ' {{name}}';
var IMPORT_TEMPLATE = 'import {{componentName}} from '{{name}}'';
var MAIN_TEMPLATE = `/* Automatic generated by './compiler/build-entry.js' */ {{include}} const components = [ {{install}} ] const install = function(Vue) {components.map((component) => { Vue.component(component.name, component) }) } /* istanbul ignore if */ if (typeof window !== 'undefined' && window.Vue) {install(window.Vue) } export {install, {{list}} } `;
// 渲染引用文件 var template = 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, template);
最后渲染出来的文件大概是这样:
import WButton from 'w-button'
const components = [WButton
]
const install = function(Vue) {components.map((component) => {Vue.component(component.name, component)})
}
/* istanbul ignore if */
if (typeof window !== 'undefined' && window.Vue) {install(window.Vue)
}
export {install,WButton
}
这个也是组件库的通用写法,所以这里的思想就是把发布到npm
上的组件,进行聚合,聚合成一个组件包引用,我们在后台编辑的时候,是需要全量引入的:
import * as W_UI from '../../packages'Vue.use(W_UI)
这样,我们组件便注册完了,组件选择区,主要是提供组件的可选项,我们可以遍历组件,提供一个个 List 让用户选择,当然如果我们每个组件如果只提供一个组件名,用户可能并不知道组件长什么样,所以我们最好可以提供一下组件长什么样的缩略图。这里我们可以在组件发布的时候,也通过 node 脚本进行。这里要实现的代码比较多,我就大致说一下过程,因为也不是核心逻辑,可有可无,只能说有了体验上会好一点:
用户启用 dev-server 进行代码编写测试
server 脚本使用 Chrome 工具
puppeteer
,调整页面到手机端模式, 进行当前 dev-server 截图。生成截图文件,上传到node服务,关联组件
这样,就可以在加载组件选择区的时候,为组件附上缩略图。
组件预览区
当用户在选择区选择了组件,我们需要展示在预览区域,那么我们怎么知道用户选择了哪些组件呢?总不能提前全部把组件写入渲染区域,通过 v-if
来判断选择吧?当然没有这么蠢,Vue 已经提供了动态组件的功能了:
<div :class="[index===currentEditor ? 'active' : '']" :is="select.name" :data="select.data">
</div>
为什么我们不用缩略图代替真实组件?一方面生成的缩略图尺寸存在问题,另一方面,我们需要编辑的联动性,就是编辑区的编辑需要及时的反馈给用户。
额外的问题
说了这么多,貌似一切都很顺利,但是这样在实践的时候,发现了存在一个明显的问题就是:我们中间的预览区域其实就是为了尽可能模拟移动端页面效果。但是如果我们加入了一些包含类似 position: fixed
样式的组件,会发现样式上就出现了明显的问题。典型的比如Dialog Loading
等。所以我们参考了 m-ui
组件库的设计,将中间预览操作容器展示为一个iframe
。将iframe
大小调整为375 * 667
,模拟 iPhone 6 的手机端。这样就不会存在样式问题了。可是这样又出现了另一个难点,那就是左侧的编辑数据如何及时的反应到iframe
中?没错,就是postMessgae
,大致思路如下:
利用 vuex
做数据存储池,所有的变化,通过 postMessgae
进行同步,这样我们只用确保数据池中的数据变化,便可以映射到渲染层的变化。比如,我们在预览区进行了组件选择和拖拽排序,那么我们只需通过vuex
出发同步信息便可:
// action.ts const action = {setCurrentPage ({commit, state}, page: number) {// 更新当前store commit('setCurrentPage',page)// 对应postMessage helper.postMsgToChild({type: 'syncState', value: state})},// ... }
Template 模板的实现
模板的设计实现,我参考了 Vue-cli 2.x 版本的思想,把这里的模板,存在了对应的 git 仓库中。当用户需要进行页面构建的时候,直接从 git 仓库中拉取对应的模板即可。当然拉取完,也会缓存一份在本地,以后渲染,直接从本地缓存中读取即可。我们现在把中心放在模板的格式和规范上。模板我们采用什么样的语法无所谓,这里我才用了和 Vue-cli 一样的Handlerbars 引擎。这里直接上我们模板的设计:
<template><div class="pg-index" :style="{backgroundColor: '{{bgColor}}'}"><div class="main-container" :style="{backgroundColor: '{{bgColor}}', backgroundImage: '{{bgImage}}' ? 'url({{bgImage}})' : null, backgroundSize: '{{bgSize}}', backgroundRepeat: 'no-repeat' }">{{#components}}<div class="cp-module-editor {{className}} {{data.className}}"><{{name}} class="temp-component" :data="{{tostring data}}" data-type="{{upcasefirst name}}"></{{name}}></div>{{/components}}</div></div>
</template><script>{{#noRepeatCpsName}}import {{upcasefirst this}} from '{{this}}'{{/noRepeatCpsName}}
export default {name: '{{upcasefirst repoName}}',components: {{{#noRepeatCpsName}}{{upcasefirst this}},{{/noRepeatCpsName}}}
}
</script>
为了简化逻辑,我们把模板都设计成流式布局,所有组件一个个堆叠往下顺序排列。这个文件便是我们vue-webpack-simple
的模板中的App.vue
。我们对其进行了改写。这样在数据填充万,便可以渲染出一个 Vue 单文件。这里我只举着一个例子,我们还可以实现多页模板等等复杂的模板,根据需求拉取不同的模板即可。
Node 渲染服务
当后台提交渲染请求的时候,我们的 node 服务所做的工作主要是:
拉取对应模板
渲染数据
编译
拉取也就是去指定仓库中通过download-git-repo
插件进行拉取模板。编译其实也就是通过metalsmith
静态模板生成器把模板作为输入,数据作为填充,按照handlebars
的语法进行规则渲染。最后产出build
构建好的目录。在这一步,我们之前所需的组件,会被渲染进package.json
文件。我们来看一下核心代码:
// 这里就像一个管道,以数据入口为生成源,通过renderTemplateFiles编译产出到目标目录 function build(data, temp_dest, source, dest, cb) {let metalsmith = Metalsmith(temp_dest).use(renderTemplateFiles(data)).source(source).destination(dest).clean(false)return metalsmith.build((error, files) => {if (error) console.log(error);let f = Object.keys(files).filter(o => fs.existsSync(path.join(dest, o))).map(o => path.join(dest, o))cb(error, f)})
}function renderTemplateFiles(data) {return function (files) {Object.keys(files).forEach((fileName) => {let file = files[fileName]// 渲染方法 file.contents = Handlebars.compile(file.contents.toString())(data)})}
}
最后我们得到的是一个 Vue 项目,此时还不能直接跑在浏览器端,这里就涉及到当前发布系统所支持的形式了。怎么说?如果你的公司发布系统需要在线编译,那么你可以把源文件直接上传到git仓库,触发仓库的 WebHook 让发布系统替你发掉这个项目即可。如果你们的发布系统是需要你编译后提交编译文件进行发布的,那么你可以通过 node 命令,进行本地构建,产出 HTML,CSS,JS。直接提交给发布系统即可。到这里,我们的任务就差不多了~具体的核心实心大多已经阐述清楚,如果实现当中有什么问题和不妥,也欢迎一起探讨交流!!
题外话
实现这样一套页面构建系统,其实我这里简化了很多东西,旨在给大家提供一种思路。另外,其实我们的页面全部在服务端构建的时候产出,我们可以再服务端这一层做很多工作,比如页面的性能优化,因为页面数据我们全部都有,我们也可以做页面的预渲染,骨架屏,ssr,编译时优化等等。而且我们也可以对产出的活动页做数据分析~有很多想象的空间。
相关文章
Javascript 里的奇葩知识
由浅入深,66条JavaScript面试知识点
20个常用的JavaScript简写技巧
最后
转发文章并关注公众号:前端开发博客,回复 1024,领取前端进阶资料
回复「电子书」领取27本精选电子书
回复「加群」加入前端大神交流群,一起学习进步
回复「JS」获取 JavaScript 精选文章
分享和在看就是最大的支持❤️
页面生成器实现及源码下载相关推荐
- php获取笔顺矢量,笔顺生成器在线-笔顺生成器php版源码下载-西西软件下载
笔顺生成器php版源码,笔顺生成器在线工具及其源码资源,可以通过输入汉字来获取对应的笔顺以及书法方式,可以用来进行线上教育以及辅助工作,还能通过源码资源来进行掌握了解.本次放出笔顺生成器工具资源下载, ...
- 【web开发网页制作】Html+Css网页制作关于我的家乡(6页面)【附源码下载】
[写在前面]之前学生时代自己也做了不少页面,现在毕业后也希望能慢慢的分享出来给大家,希望能给刚接触web开发的你带来一些启发.其实关于网页制作,没有大家想象中的那么难,接下来给大家详细介绍一下如何实现 ...
- Wine零知识学习2 —— 源码下载、构建与安装
一.源码下载 1. 源码下载地址 wine / wine · GitLab 页面如下: 2. 源码下载 使用以下命令下载wine源码(需要事先准备好源码放置目录): git clone https:/ ...
- 小程序源码:朋友圈集赞万能截图生成器微信小程序源码下载
大家好这是一款朋友圈积攒截图小程序 里面内涵三款样式生成,一款图文,一款分享,一款查看的样式 也就是我们微信朋友圈所用到的样式就包含了 里面的流量主 那些可以用户自由的添加哈! 赞的数量那些可以用户自 ...
- 朋友圈集赞万能截图生成器威信小程序源码下载
大家好这是一款朋友圈积攒截图小程序 里面内涵三款样式生成,一款图文,一款分享,一款查看的样式 也就是我们威信朋友圈所用到的样式就包含了 那些可以用户自由的添加哈! 赞的数量那些可以用户自定义的哈 另外 ...
- 会所网站php源代码,anmo 按摩休闲会所网站模板,全套 包含近20个HTML 页面,web制作源码。 WEB(ASP,PHP,...) 238万源代码下载- www.pudn.com...
文件名称: anmo下载 收藏√ [ 5 4 3 2 1 ] 开发工具: HTML 文件大小: 4797 KB 上传时间: 2013-12-27 下载次数: 5 提 供 者: 烟鸭掌儿 详细 ...
- 胡言乱语生成器微信小程序源码下载在线取名等等支持流量主收益免服务器和域名
这是一款纯前端的一款生成器小程序源码 该小程序源码无需服务器和域名,也无需设置合法域名 该小程序里面的生成样式多样化有很多种 不过小编在测试该款小程序的时候,打开有部分生成的界面是空白可能是小编打开的 ...
- FreeMarker_模板引擎_代码自动生成器_源码下载
首先我们先来认识一下Freemarker 1.what is the FreeMarker? 你可以到freemarker的官网上去,那里有很详细的介绍:http://freemarker.org/ ...
- java验证码源码_Java通用验证码程序及应用示例(提供源码下载)
评论 # re: Java通用验证码程序及应用示例(提供源码下载) 2009-11-27 17:09 零全零美 多谢博主写出这么好的总结,如果能加上汉字验证码,会更完美! 回复 更多评论 # re ...
最新文章
- dapper 注意事项之GUID
- oracle指定用户SID,如何修改oracle SID
- POJ2391 Floyd+离散化+二分+DINIC
- c语言程序设计 在线课程设计,c语言程序设计 本科课程设计
- 1.8-zabbix服务端安装
- c语言实验题数组逆序,【C语言】利用栈将数组中字符串逆序
- adodb.recordset.open方法的参数
- 【疑难杂症】AiO Runtimes 微软常用运行库合集工具一键式安装全部 Windows 系统必备常用运行库合集,解决各种.dll文件缺失问题
- Vysor 1.7.6 chrome 插件破解
- 批量调取接口_调用API接口批量查手机归属地
- 比特bit,字节Byte,带宽流量和流速的关系,存储容量单位
- python实现B站自动发弹幕_python代码
- 聊聊几个阿里 P8、P9 程序员的故事
- mysql键值相同_mysql什么是键值重复
- windows画图工具入口
- 信息传播学习笔记(1)——SIS模型原理与公式推导
- 油猴相关玩法-下载,脚本下载
- 获取光标位置及动态设置光标到指定位置
- 短距离激光测距仪方案模块
- 千万不要尝试波段操作