文章篇幅较长,慎入
随着网络应用的丰富和发展,网站往往不能迅速跟进大量信息衍生及业务模式变革的脚步,常常需要花费许多时间、人力和物力来处理信息更新和维护工作。 ——引自「百科」
小公司、小网站因人员、技术问题,在内容管理处理上处于「食之无味弃之可惜」的一种情况:内容维护会经常打乱开发人员节奏,但是又没有精力去思考投入统一去解决类似问题。
有哪些运营内容需要维护
开始之前,我们先看下小网站一般会存在哪些内容需要维护配置:
- 页面静态内容,一般用于导流或者介绍:如轮播图,特色、功能、产品介绍等;
- 全站统一内容:如导航、友情链接等;
- 站点新闻公告:包含列表、详情;
- 其他类似的静态文档,用来展示或者介绍公司相关:如帮助、关于我们等;
- 其余无业务功能逻辑,需要人工维护的内容
现状
遇到这些内容维护,好的小公司可能会一开始就同步建立一个配置发布管理功能,其他的可能因为工期赶就代码写死,通过发布解决,发布频繁了才去做配置发布管理功能。
- 页面代码写死,调整即发布
抓狂
- 每类内容都配套「配置发布管理功能」,需要处理以下内容
- 抽象数据,创建表
- 前端开发相关管理页面,包含「列表」、「新增编辑」、「表单」功能
- 因为是后台管理页面,一般情况这个配置页面都比较简单,甚至缺少校验,只能人肉在线查看效果来决定是否配置正确
作为一个有追求的攻城狮,这样的重复性工作应该主动拒绝,通过工具或者流程去改善生活
CMS是什么
在很早以前,内容管理就成为一个重要的应用领域,并且伴随着时间、技术的推移,CMS的功能也变得越来越强大。
CMS简单来说就是内容管理系统(Content Management System),主要解决用户网站建设与信息发布中常见的问题和需求,运营同学可以通过它进行网站内容新增、修改、审核发布,提高信息发布效率和准确性;其技术核心思想是分离内容的管理和设计。
业界CMS系统
篇幅原因,这里并不想去对比和介绍业界相关的系统,只是想大概介绍下相关类型
- 传统CMS系统
- 简单的小至类似上面提到的「配置发布管理功能」,如公告发布模块
- 可视化建站,如
- 云凤蝶:可以基于组件可视化搭建页面,站点托管在云凤蝶提供的服务。
- 阿里内部CMS系统:也是基于组件可视化搭建页面,天猫、淘宝成千上万的活动页面都是基于此运营自己选择相关组件搭建并投放的;
不对外
从小公司的诉求和运营的专业水平来看,类似云凤蝶这样的基于组件化搭建页面的CMS平台,其操作成本高,另外组件开发、维护成本也很高,小公司也很难有业务发展需求,可以沉淀出自己的组件模块。所以除非一开始就不打算投入网站服务建设,只是简单得搭个网站门面,才会使用类似可视化建站服务。
下面的篇幅我们还是以实现「传统CMS系统」为目标,去考虑如何做得更好
诉求
那么小网站对内容管理又有哪些诉求呢?
- 有一套通用解决方案可以为任意运营内容快速生成「配置发布管理页面」
- 表单页面强化校验,增加体验
- 尽可能保证内容填写的正确性,避免线上出现问题
- 统一的数据获取API,避免重复开发
- 上篇文章提到的jsonschema-form-vue - 自动生成表单库,就能快速生成体验一致的表单;
- 通过抽象将内容表设计成一套统一结构,一方面避免每种配置都建表,另一方面也容易提供统一API;
- 关于正确性保障,可以通过 预览 -> 审核发布流程 提前保障;
不过虽然保证了内容配置管理,但是文章一开始提到的「新闻公共」、「帮助」、「关于我们」这些内容相似、字段一致的页面,难道每次都要创建类似的配置管理,然后在web应用创建相关页面,再发布应用吗?
- 需要提供一套页面模板机制,通过复制页面模板,快速复制同类型页面以及跟它关联的内容配置;通过这种关联关系,应用只要通过路由
pageId
去查询页面相关的内容,即可避免发布
目标
基于诉求,先明确解决小网站内容管理需要完成的目标
- 内容管理:减少内容运营成本
- 减少内容的维护成本
- 减少应用发布成本
- 通过配置化自动生成「配置表单」,减少开发投入成本
- jsonschema-form-vue
- 可基于页面模板快速复制、新增页面
- 保证发布质量、体验
- 支持预览、审核发布、回滚
- 支持可视化编辑
- 成本小
- 能容易和现有系统结合
- 考虑投入产出比
分层关系和表设计
通过内容,对数据模型进行抽象,主要分成以下三层:
cms-guanxi.png
组件
最底层组件,它包含了基于 jsonschema-form-vue的表单相关配置,可以使用版本管理,让配置升级更安全。
{
id: Schema.Types.ObjectId,
name: String, // 配置名称
version: String, // 版本号
jsonSchema: String, // JSONSchema
definition: String, // Form Definition
histories: Array, // 历史记录
author: String, // 用户
createdAt: Date, // 创建时间
modifiedAt: Date // 修改时间
}
模块
模块是运营内容编辑单元,基于组件表单配置去生成表单,可以保存草稿和发布数据,用于预览和线上内容
{
id: Schema.Types.ObjectId,
name: String, // 模块名称
title: String, // 模块标题
siteId: Schema.Types.ObjectId, // 所属站点ID
pageId: Schema.Types.ObjectId, // 页面ID
componentId: Schema.Types.ObjectId, // 组件ID
componentVersion: String, // 组件版本号
model: {}, // 配置数据
draft: {}, // 配置草稿
status: Number, // 状态 0: 配置更新, 1: 内容更新, 2: 发布审核
online: Boolean,
histories: Array, // 历史记录
author: String, // 用户
createdAt: Date, // 创建时间
modifiedAt: Date // 修改时间
}
为什么不把表单配置直接放模块,而是增加组件这一层? 这主要是因为模块和组件是「多对一」的关系,一个表单配置可能会被用到多个模块;另外上面提到的相似页面,会复制出相似模块,如果将表单配置放在模块表中,会存在重复数据,后续也很难同步配置升级
页面
页面就是浏览器中看到页面,主要用来保存模块的关联关系,一个页面可能存在多个独立模块
{
id: Schema.Types.ObjectId,
name: String, // 页面名称
url: String, // 页面url
realUrl: String, // 页面实际url
useRoute: Boolean, // 是否启用路由规则
router: String, // 路由规则
siteId: Schema.Types.ObjectId, // 所属站点ID
modules: String, // 模块
owner: String, // 用户
createdAt: Date, // 创建时间
modifiedAt: Date // 修改时间
}
为什么有url又有实际url?这主要考虑后面的「可视化编辑」功能,需要去请求真实页面,所以在页面复制发布时,需要基于「页面路由」生成实际url
因为数据模型的核心是配置和内容,所以选用了文档型数据库
MongoDB
整体设计
抽象了数据模型,先通过设计看下CMS需要提供哪些功能
cms-jiagou.png设计上,CMS遵循以下几点原则:
- 对现有web应用侵入性、影响最小,方便接入
- 操作简单,尽量设计简单,功能明确
- CMS只提供数据服务,不允许用户流量直接进入,导致需要跟web应用同步扩容
所以:
- 提供组件、模块、页面管理功能;
- 提供API让接入方可以方便获取数据,但是接入方需要对数据进行缓存,减小对CMS服务的压力;
- 支持获取草稿数据,让web应用支持预览功能;
- 提供可视化编辑功能,页面编辑可以可视化预览编辑线上页面,实时查看效果,方面查找模块;
Web应用接入只要改动以下几点:
- 如果之前没有数据、结构分离,要先分离,数据源替换成CMS数据;
- 线上数据要进行缓存,减少CMS服务压力;在服务启动时读取CMS内容数据,进行缓存;
- 提供方法让Controller可以获取CMS数据;
- 可选择支持预览功能,如定义url规则
?preview=true
则实时请求CMS草稿数据; - 提供缓存更新API,在CMS内容审核发布更新时,能更新缓存,不用重启应用;
- 可选择接入「可视化编辑脚本」,在使用CMS数据的html模块进行
moduleId
标识
流程
通过流程可以更清楚了解CMS的功能
cms-process.png- 创建组件配置
前端在本地开发完静态页面后,先mock内容,完成数据模板分离;然后可以借助JSON2JSONSchema工具快速生成JSONSchema结构,根据 jsonschema-form-vue文档,生成组件配置
-
创建内容模块,引用相应的组件配置(内容模块才是运营内容维护单元);如果有初始数据,可以帮运营填入;复制
moduleId
到web应用中调用相应方法获取CMS数据;然后,发布web应用后,开发的工作就完成了,以后的内容维护都交由运营同学。 -
运营同学维护模块内容,有两种方式:
3.1 通过模块名查找模块进行编辑
3.2 通过页面编辑进行可视化编辑,见下面 -
内容编辑确认无误后,进行保存预览
-
预览确认没有问题后,审核发布,内容上线
到这里,CMS最基本的功能已经完成了;再也不用因为文案内容改动,需要进行应用发布重启了
MORE
接下来的篇幅最后介绍「页面复制」和「可视化编辑」功能
关于页面复制
上文提到的新闻公告、关于我们,或者一些风格类似的活动页面,需要提供快捷复制创建新页面,然后修改页面内容即可。
主要思考如下:
- 模块才是内容编辑单元,页面维护着模块映射关系,复制页面的同时复制相应模块,并更新对应的映射关系即可;
- 提供通过
pageId
获取数据接口,这样类似页面可以通过路由规则params或query
来获取对应页面数据渲染;
可视化编辑
最后必须来点看起来科技感满满的功能来个收尾,以提供该CMS系统的档次,直接跟前沿接轨。
在具备了模块编辑功能后,可视化编辑就是如何获取编辑的数据并实时渲染页面预览,同时预览页面可识别可编辑模块,并表现出自己能被编辑,引起运营点击修改的欲望:
- 关于实时预览
上文我们已经提到预览功能,实时预览问题不攻而破 - 关于可编辑模块
要想识别模块并编辑,必须在HTML Template上标识上moduleId
,然后通过规则识别在CMS系统中引入「可视化编辑.js」,CMS在编辑时通过iframe引入线上页面,做以下事情:- 识别页面可编辑模块,在上面遮上一层编辑DIV,表现出可编辑样子
- 点击编辑DIV时,iframe通信,告知CMS编辑模块,弹出模块配置
- 编辑保存后,更新iframe时间戳,重新请求页面,获取新数据
就这样,马上提供一个档次
到这里,文章就结束了,在工作过程中,多去发现工作流程中,重复性的、自己不喜欢去处理但又不得不去处理的活儿,去思考如何通过工具、流程化去处理它们;让事情变得简单、可复制;这个思考和实现的过程,不仅能提升你的设计和技术能力,同时也能增加公司对你的依赖性,走向升职加薪的路上。
网友评论