第 10 章:组件化设计思想
"好的架构不是设计出来的,而是演进出来的。但组件模型除外,它得一开始就想清楚。"
在传统的活动开发中,每个页面都是"硬编码"的。今天要改个文案,明天要换张图,程序员的生命就浪费在这些琐事上。
我们的目标:打造一个"低代码编辑器",让运营人员(社长/管理员)能像搭积木一样自己拼出一个精美的活动页面。
要实现这个目标,核心在于:一切皆组件,组件皆数据。
10.1 什么是"活动页面配置组件"?
在我们的系统中,一个活动页面不是 .vue 文件,而是一串 JSON 数据。
比如,一个包含"头图 + 标题 + 报名按钮"的页面,在数据库里长这样:
json
[
{ "type": "image", "config": { "src": "banner.jpg" } },
{ "type": "text", "config": { "content": "中秋晚会", "fontSize": 20 } },
{ "type": "button", "config": { "text": "立即报名" } }
]
我们的渲染引擎(Runtime)读取这个数组,依次把它们画到屏幕上。这就是数据驱动 UI。
10.2 组件模型设计
我们需要定义一套标准,规定每个组件长什么样。这套标准就是 Component Schema。
通用结构 (ActivityComponent)
所有组件都必须遵循这个接口定义 (src/types/activity.d.ts):
typescript
interface ActivityComponent {
id: string // 唯一ID (如 "w1710001"),用于查找和更新
type: string // 组件类型 (如 "text", "image")
order: number // 排序权重
visible: boolean // 是否可见
style: {
// 通用样式容器
marginTop?: number
padding?: number
// ...
}
config: any // 组件特有的业务配置 (AnyScript?? 不,下面细说)
}
具体组件定义
1. 文本组件 (text)
typescript
interface TextConfig {
content: string // 文本内容
fontSize: number // 字号 (默认 32)
color: string // 字体颜色
lineHeight: number // 行高 (默认 1.6)
bold: boolean // 加粗
decorType: 'none' | 'underline' | 'through' | 'overline' // 装饰线类型
decorColor: string // 装饰线颜色
decorTextColor: string // 装饰文字颜色
}
2. 图片组件 (image)
typescript
interface ImageConfig {
src: string // 图片地址
}
3. 按钮组件 (button)
typescript
interface ButtonConfig {
text: string // 按钮文案
}
4. 分割线组件 (divider)
typescript
interface DividerConfig {
styleType: 'simple' | 'text' // 样式类型
color: string // 线条颜色
text: string // 标题文字
description: string // 描述信息
textColor: string // 文字颜色
}
5. 倒计时组件 (countdown)
typescript
interface CountdownConfig {
targetTime: number // 目标时间戳
textPrefix: string // 前缀文案
stylePreset: 'classic' | 'modern' // 样式预设
}
6. 视频组件 (video)
typescript
interface VideoConfig {
src: string // 视频链接
autoplay: boolean // 自动播放
loop: boolean // 循环播放
}
10.3 组件注册与渲染机制
有了数据模型,前端如何把它们变成 Vue 组件?
组件映射表 (Component Map)
我们不需要写一堆 if (type === 'text')。Vue 的 <component :is="..."> 是实现动态渲染的绝佳工具。
虽然 Vue 支持动态导入,但在小程序环境中,更推荐静态注册以保证兼容性。
html
<!-- ActivityDesign/index.vue -->
<template>
<view v-for="item in components" :key="item.id">
<ImageWidget v-if="item.type === 'image'" :item="item" />
<TextWidget v-else-if="item.type === 'text'" :item="item" />
<ButtonWidget v-else-if="item.type === 'button'" :item="item" />
<!-- ... -->
</view>
</template>
(注:虽然写了 if-else,但这只是为了类型安全和 Props 传递的显式控制。本质上依然是根据 Type 渲染 Widget)
本章小结 : 我们定义了组件的"基因"------JSON Schema。只要符合这个 Schema 的数据,都能被我们的系统识别并渲染。下一章,我们将亲手打造那个能生成这些 JSON 的可视化编辑器。