# rose
**Repository Path**: yifan521/rose
## Basic Information
- **Project Name**: rose
- **Description**: No description available
- **Primary Language**: Unknown
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2023-12-07
- **Last Updated**: 2023-12-07
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
## Rose
活动页构建利器,用于快速搭建活动页
[示例DEMO](https://muwoo.github.io/rose/) (逐步完善中...)
[node构建服务](https://github.com/roooses/rose-server)
[template 模板](https://github.com/roooses/rose-template)
[文本组件示例](https://github.com/roooses/rose-text)
## 前言
如果你经常接触一些公司的活动页,可能会经常头疼以下问题:这些项目周期短,需求频繁,迭代快,技术要求不高,成长空间也小。但是我们还是马不停蹄的赶着产品提来的一个个需求,随着公司规模的增加,我们不可能无限制的增加人手不断地重复着这些活动。这里我就不具体介绍一些有的没的的一些概念了,因为要介绍的概念实在太多了,作为一个前端的我们,直接上代码撸就好了!!!!
## 目标
我们的目标是实现一个页面制作后台,在后台中我们可以对页面进行 `组件选择 --> 布局样式调整 --> 发布上线 --> 编辑修改`这样的流程操作。
## 架构设计
首先是要能提供组件给用户进行选择,那么我们需要一个`组件库`,然后需要对选择的组件进行布局样式调整,所以我们需要一个`页面编辑后台`接着我们需要将编辑产出的数据渲染成真实的页面,所以我们需要一个`node服务`和用于填充的`template 模板`。发布上线,这个直接对接各个公司内部的发布系统就好了,这里我们不做过多阐述。最后的编辑修改功能也就是针对配置的修改,所以我们需要一个数据库,这里我选择的是用了`mysql`。当然你也可以顺便做做权限管理,页面管理....等等之类的活。
啰嗦了这么长,我们来画个图,了解下大概的流程:
## 开撸
#### 组件的实现
首先我们来实现组件这一部分,因为组件关联着后台编辑的预览和最后发布的使用。组件设计我们应该尽量保持组件的对外一致性,这样在进行渲染的时候,我们可以提供一个统一的对外数据接口。这里我们的技术选型是基于 Vue 的,所以下面的代码部分也主要是基于 Vue 的,但是万变不离其宗,其他语言也类似。
根据上图,我们的组件是会被一个个拆分单独发布到 `npm`仓库的,为什么这么设计呢?其实之前也考虑过设计成一个组件库,所有组件都包含在一个组件库内,这样只需要发布一个组件库包,用的时候按需加载就好了。后来在实践的过程中发现这样并不合适协同开发,其他前端如果想贡献组件,接入的改造成本也很大。举个🌰:小明在业务中写了个`Button`组件,这个组件经常会被其他项目复用,他想把这个组件贡献到我们的系统中,被模板使用,如果是一个组件库的话,他首先得拉取我们组件库的代码,然后按照组件库的规范格式进行提交。这样一来,偷懒的小明可能就不太愿意这么干,最爽的方法当然是在本地构建一个npm库,开发选用的是用`TypeScript`还是其他的我们不关心,选用的 Css 预处理器我们也不关心,甚至编码规范的`ESLint`我们也不关心。最后只需通过编译后的文件即可。这样就避免了一个组件库的约束。依托于NPM完善的发布/拉取,以及版本控制机制,可以让我们少做一些额外的工作,也可以快速的把平台搭建起来。
说了这么多,代码呢?,我们以一个`Button`为例,我们对外提供这样的形式组件:
```html
```
可以看到我们只对外暴露了一个`props`,这样做法的好处是可以统一组件对外暴露的数据,组件内部爱怎么玩怎么玩。注意,这里我们也可以引入一些第三方组件库,比如`mint-ui`之类的。
#### 后台编辑的实现
在写代码前,我们先考虑一下需要实现哪些功能:
1. 一个属性编辑区,提供给使用者编辑组件内部`props`的功能
2. 一个组件选择区,提供使用者选择需要的组件
3. 一个组件预览区,提供使用者拖拽排序页面预览的功能
###### 编辑区的实现
按照顺序,我们先来实现组件的属性编辑功能。我们要考虑,一个组件暴露出哪些可配置的信息。这些可配置的信息如何同步到后台编辑区,让使用者进行编辑,一个按钮的可配置信息可能是这样:

如果把这些配置全部写在后台库里面,根据当前选择的组件加载不同的配置,维护起来会相当麻烦,而且随着组件数量的增加,也会变得臃肿,所以我们可以将这些配置存储在服务端,后台只需要根据存储的规则进行解析便可,举个例子,我们其实可以存储这样的编辑配置:
```json
[
{
"blockName": "按钮布局设置",
"settings": {
"src": {
"type": "input",
"require": true,
"label": "按钮文案"
}
}
}
]
```
我们在编辑后台,通过接口请求到这些配置,便可以进行规则渲染:
```js
/**
* 根据类型,选择创建对应的组件
* @param {VNode} vm
* @returns {any}
*/
createEditorElement (vm: VNode) {
let dom = null
switch (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 脚本来遍历所需组件,进行组件的安装注册:
```js
// 定义渲染模板和路径
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);
```
最后渲染出来的文件大概是这样:
```js
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`上的组件,进行聚合,聚合成一个组件包引用,我们在后台编辑的时候,是需要全量引入的:
```js
import * as W_UI from '../../packages'
Vue.use(W_UI)
```
这样,我们组件便注册完了,组件选择区,主要是提供组件的可选项,我们可以遍历组件,提供一个个 List 让用户选择,当然如果我们每个组件如果只提供一个组件名,用户可能并不知道组件长什么样,所以我们最好可以提供一下组件长什么样的缩略图。这里我们可以在组件发布的时候,也通过 node 脚本进行。这里要实现的代码比较多,我就大致说一下过程,因为也不是核心逻辑,可有可无,只能说有了体验上会好一点:
1. 用户启用 dev-server 进行代码编写测试
2. server 脚本使用 Chrome 工具 `puppeteer`,调整页面到手机端模式, 进行当前 dev-server 截图。
3. 生成截图文件,上传到node服务,关联组件
这样,就可以在加载组件选择区的时候,为组件附上缩略图。
#### 组件预览区
当用户在选择区选择了组件,我们需要展示在预览区域,那么我们怎么知道用户选择了哪些组件呢?总不能提前全部把组件写入渲染区域,通过 `v-if`来判断选择吧?当然没有这么蠢,Vue 已经提供了动态组件的功能了:
```html