架构师思维:技术为业务服务
简介:架构从业务出发,先理解需求,再做设计,最后才是开发
-
核心观点
- 脱离业务的架构就是耍流氓
- 没有业务就不需要架构,业务和架构不分家
- 技术永远为业务服务
- 架构师职责:通过技术手段保证业务增长
- 架构设计本质:深入理解业务需求后,用软件把业务模拟出来
- 需求指导设计,设计指导开发
- 脱离业务的架构就是耍流氓
-
分析需求的三种思维
| 思维 | 说明 |
|---|---|
| 全局 | 范围全面,不遗漏角色和场景 |
| 整体 | 流程从开始到结束完整串起来 |
| 闭环 | 有输入有输出,做完能拿到反馈 |
-
设计三原则
- 不关注细节,关注整体和范围
- 判断可行性,不确定就先调研
- 控制复杂度,越简单越好
- 不要过度设计,不要为了炫技而设计
-
普通程序员 vs 架构师
| 维度 | 普通程序员 | 架构师 |
|---|---|---|
| 对需求 | 被动接收 | 参与需求,看透业务本质 |
| 对 PM | 水火不容 | 统一战线,对业务负责 |
| 关注点 | 技术点、框架 | 解决方案、业务增长 |
产品研发流程
简介:需求和技术方案设计在整个研发流程中非常靠前
- 主流程

| 阶段 | 关键环节 |
|---|---|
| 项目启动 | kick-off 启动会、项目计划、角色划分 |
| 需求 | 编写需求、评审需求(可能多轮)、UI 视觉稿评审 |
| 技术方案设计 | 技术难点调研、方案设计、方案评审(可能多轮) |
| 开发 | 写代码含单元测试、代码走查、合并代码(CI)、自测(准入测试) |
-
日常项目管理贯穿全程
- 站会、周会、风险记录跟踪、问题记录跟踪
-
每个环节都要有产出

以架构师思维分析需求:抽奖页面
简介:用一道面试题说明如何用全局、整体、闭环思维挖出隐藏需求
-
题目
- H5 抽奖页,只有活动说明和一个抽奖按钮
- PM 和后端都是新手,需要服务端提供哪些接口和能力
-
常见答案
- 抽奖接口:几乎所有人都能想到
- 登录 / 用户信息:不到 1/3 的人能想到
- 其他能力:很少有人想到
-
画流程图补全需求

- 完整能力清单(橙色节点是容易漏掉的需求)
| 能力 | 作用 |
|---|---|
| 登录 / 用户信息 | 确定奖品归属,也可用手机号代替 |
| 是否已抽奖 | 防止重复抽奖 |
| 抽奖接口 | 返回抽奖结果 |
| 分享 | 拉新,带来用户增长 |
| 埋点统计 | PV / UV + 抽奖、分享的自定义事件 |
- 关键点
- 统计让需求闭环
- 参与率 = 抽奖次数 / UV
- 分享率 = 分享数 / PV
- 漏斗:1 万人访问,1 千人抽奖,转化率 10%
- 这道题考的是业务能力,不是技术点
- 统计让需求闭环
浅层需求与深度需求
简介:浅层需求一眼可见,深度需求决定它是不是真正的线上业务
- 平台核心流程

- 浅层需求
| 模块 | 需求点 |
|---|---|
| 用户 | 注册、登录(手机号 + 短信验证码)、获取用户信息 |
| 作品 | 创建、保存、发布、获取作品信息、作品列表 |
| 模板 | 模板列表、使用模板创建作品 |
- 深度需求
| 方面 | 需求 | 原因 |
|---|---|---|
| 作品管理 | 删除恢复、转赠、复制 | 误删要能找回;离职要交接;想共享作品 |
| 统计 | 作品统计、分渠道统计 | 没有效果反馈就不闭环 |
| 发布 | URL 不能变 | 广告位链接写死且不受自己控制 |
| H5 | 分享 | 微信裂变传播,带来增长 |
| 后台管理 | 数据统计、作品下线、用户冻结、模板管理 | 把控全局,快速处理违规内容 |
-
分渠道统计
-
为什么需要
- 同一作品投放微信、头条、支付宝,PV 100 万分不清来源
- 分出来才能判断渠道效果,指导下次投放
-
实现原理:URL 加渠道参数,访问时按参数分别统计
微信 https://h5.example.com/p/{作品id}?channel=A → 60 万
头条 https://h5.example.com/p/{作品id}?channel=B → 20 万
支付宝 https://h5.example.com/p/{作品id}?channel=C → 20 万
-
-
注意点
- 练手项目只有增删改查,线上业务必须考虑完整使用场景和用户价值
- 敏感词检查只是辅助手段,后台人工管理必不可少
需求总览:角色与输入输出
简介:三个角色各有输入输出,靠四个核心模块串成闭环
| 角色 | 输入 | 输出(反馈) |
|---|---|---|
| 制作人 | 通过 B 端和编辑器制作作品 | 作品效果:统计、分渠道统计 |
| C 端用户 | 浏览 H5 获取信息 | 分享欲望,带来业务增长 |
| 管理员 | 通过管理后台监管、审核、上下线 | 平台稳定和增长 |
- 四个核心模块
- B 端和编辑器、H5 端、统计服务、管理后台
项目拆分
简介:先确定范围,再根据需求拆分项目
- 确定范围
- 确定范围是做任何事情的第一步
- 范围不清就开工,做着做着不断加东西,项目永远偏离计划
- 确定范围是做任何事情的第一步
| 需求方面 | 项目 | 说明 |
|---|---|---|
| B 端和编辑器 | biz-editor-fe / biz-editor-server |
前后端分离,编辑器复杂也可单独拆出 |
| H5 | h5-server |
SSR,C 端追求打开速度 |
| 管理后台 | admin-fe / admin-server |
前后端分离 |
| 业务组件库 | 独立 npm 包 | 同时给编辑器画布和 H5 使用 |
- SSR 怎么选
| 场景 | 核心诉求 | 是否用 SSR |
|---|---|---|
| toC(H5) | 首屏性能,手机网络复杂 | 适合 |
| toB(B 端、管理后台) | 稳定性,电脑访问网络好 | 一般不适合 |
-
注意点
- SSR 会增加开发、调试、上线成本,只在需要的地方用
-
业务组件库为什么独立
- 编辑器是所见即所得
- 画布和 H5 的组件相同,渲染逻辑也相同
- 不独立:画布和 H5 各写一套,冗余且难保证一致
- 独立:作为 npm 包被两端引用,复用并保证一致
- 编辑器是所见即所得
-
共享数据库
- B 端服务、H5、管理后台共用一个数据库
- 作品只有一份,统一存储
自研统计服务
简介:分渠道统计需要自定义事件统计和 OpenAPI,第三方满足不了,只能自研
| 能力 | 说明 |
|---|---|
| 日志收集 | 接收 H5 埋点请求,带作品 id 和 channel |
| 日志分析 | 汇总计算各渠道结果 |
| OpenAPI | 以 API 形式提供结果给 B 端和管理后台 |
- 为什么自研
- 友盟、百度统计、ARMS 要么不支持,要么一年收费几万
- 需求不能砍,只能自研
| 对比 | PV / UV 统计 | 自定义事件统计 |
|---|---|---|
| 粒度 | 页面级、域名级 | 参数级(作品 id、channel) |
| 方案 | 第三方,免费好用 | 自研 |
- 注意点
- 只自研自定义事件统计,PV / UV 和性能分析仍用第三方
- OpenAPI 就是普通 API,面向多个使用方开放
- 设计时对第三方能力一定要先调研,不能想当然
项目关系图
简介:各项目通过 API、组件库、数据库和统计服务串起来

- 关键关系
- 下线:管理后台改数据库状态,H5 判断是否展示
- 红色节点是需要重点说明的两个模块:业务组件库、统计服务
- 统计服务收集 H5 上报的数据,再通过 OpenAPI 提供给 B 端和管理后台
- 组件平台(图中未画)为业务组件库创建和发布组件
- 脚手架、组件平台与业务无关,用于研发提效
作品数据结构设计
简介:数据结构看似是细节,实际是核心,架构阶段就要论证清楚
-
三个设计问题
- 点保存时,传给服务端的数据结构是什么
- 画布和属性面板如何同步更新
- 如何扩展图层面板
-
常见设计的问题
| 常见做法 | 问题 | 修正 |
|---|---|---|
| 组件用自造格式 | 学习和沟通成本高 | 使用 vnode 格式 |
| 组件用对象存储 | 对象 key 无序 | 改用数组 |
| 只知道"Vuex 能同步" | 抓不住要点 | 用 activeComponentId 表示当前选中组件 |
| 图层单独存一份 | 两份数据要互相同步 | 用 getter 从组件列表计算 |
-
正确的 store 结构
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
}{
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} work: {
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} title: '作品标题',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} setting: {}, // 预留配置项,保证扩展性
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} props: {}, // 页面 body 设置,如背景色
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} components: [ // 数组,有序
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} {
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} id: 'xxx', // 唯一 id
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} name: '文本1',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} tag: 'text',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} attrs: { fontSize: '20px' },
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} children: ['文本1']
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} },
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} {
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} id: 'yyy',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} name: '图片1',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} tag: 'image',
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} attrs: { src: 'xxx.png', width: '100px' },
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} children: null
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} }
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} ]
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} },
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
} activeComponentId: 'xxx' // 当前选中组件,只存 id
#{
work: {
title: '作品标题',
setting: {}, // 预留配置项,保证扩展性
props: {}, // 页面 body 设置,如背景色
components: [ // 数组,有序
{
id: 'xxx', // 唯一 id
name: '文本1',
tag: 'text',
attrs: { fontSize: '20px' },
children: ['文本1']
},
{
id: 'yyy',
name: '图片1',
tag: 'image',
attrs: { src: 'xxx.png', width: '100px' },
children: null
}
]
},
activeComponentId: 'xxx' // 当前选中组件,只存 id
}}#// 图层由 components 计算得出,不单独存储
layers() {
return store.work.components.map(c => ({ id: c.id, name: c.name }))
}// 图层由 components 计算得出,不单独存储
#// 图层由 components 计算得出,不单独存储
layers() {
return store.work.components.map(c => ({ id: c.id, name: c.name }))
}layers() {
#// 图层由 components 计算得出,不单独存储
layers() {
return store.work.components.map(c => ({ id: c.id, name: c.name }))
} return store.work.components.map(c => ({ id: c.id, name: c.name }))
#// 图层由 components 计算得出,不单独存储
layers() {
return store.work.components.map(c => ({ id: c.id, name: c.name }))
}} -
设计三原则
- 每个组件符合 vnode 规范
- 用数组组织数据,保证有序
- 使用引用关系,不冗余,单一数据源
-
关键点
activeComponentId是画布和属性面板同步更新的关键- 设计尽量遵循业界规范,大家一看就懂,坑少
数据流转
简介:三端共用一个数据库,每个作品本质就是一份 JSON
| 操作 | 数据层面 |
|---|---|
| 创建作品 | 初始化 JSON |
| 保存作品 | 修改 JSON |
| 发布作品 | 只改一个标记 |
| C 端浏览 | 获取 JSON,SSR 渲染 |
| 下线作品 | 只改一个标记,C 端判断是否展示 |
- 注意点
- C 端加缓存,避免频繁查库
- 设计只确定方向和思路,字段名后续变化是正常的
- 方向不变、后续不需要重构,设计就算成功
技术方案设计文档
简介:技术方案的产出就是设计文档,核心是讲清楚"将如何做"
-
为何难写
- 没有规范可依
- 平时不常写
-
两个技巧
- 随性一些:就当向领导解释将如何实现,该画图画图,该写伪代码写伪代码
- 没思路先写点代码捋思路
- 写代码只是工具,产出仍然是文档
-
写文档浪费时间吗
- 真想明白了:写文档最多一两小时,不会导致延期
- 写不出来:正好暴露没想清楚的问题
-
文档目录
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展# 整体架构设计 V0.1
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 需求 需求文档链接
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 范围 整体设计,不涉及细节
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 模块设计 拆分和关系图、各模块职责、特殊模块说明
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 数据结构 store 结构及解释、数据流转图
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 开发提效 脚手架、组件平台
整体架构设计 V0.1
需求 需求文档链接
范围 整体设计,不涉及细节
模块设计 拆分和关系图、各模块职责、特殊模块说明
数据结构 store 结构及解释、数据流转图
扩展性保证 扩展组件、扩展编辑器功能、扩展页面配置
开发提效 脚手架、组件平台
运维保障 线上服务、安全、监控报警、服务扩展## 运维保障 线上服务、安全、监控报警、服务扩展
-
注意点
- 只写结论和重点,不写论证过程
- 扩展性一节一定要写,用来引导评审讨论
- 运维保障:大厂一般用自研服务,小厂用第三方云服务
面试/考试记忆点
- 架构设计本质:理解业务需求后,用软件把业务模拟出来
- 分析需求三种思维:全局、整体、闭环
- 统计让业务闭环,分渠道统计靠 URL 参数实现
- 只有 H5 用 SSR:toC 追求性能,toB 追求稳定
- 组件库独立:画布和 H5 组件与渲染逻辑一致,需要复用
- 自研统计服务:需要自定义事件统计 + OpenAPI,第三方不支持或太贵
- 数据结构三原则:vnode 规范、数组有序、引用不冗余
- 发布和下线在数据层面都只是改一个标记
拓展:SSR(服务端渲染)
简介:SSR 在服务端把页面渲染成完整 HTML 再返回,首屏快、利于 SEO,代价是服务端成本更高
-
核心概念
- SSR(Server-Side Rendering):服务端渲染
- 服务端取数据并拼好完整 HTML,浏览器拿到就能直接显示
- CSR(Client-Side Rendering):客户端渲染
- 服务端只返回空壳 HTML 和 JS,浏览器执行 JS、请求数据后才渲染出内容
- 水合(Hydration)
- SSR 返回的 HTML 只能看不能点,浏览器加载 JS 后给页面绑定事件,页面才可交互
- SSR(Server-Side Rendering):服务端渲染
-
工作原理

- CSR vs SSR
| 维度 | CSR | SSR |
|---|---|---|
| 首屏速度 | 慢,要等 JS 下载执行和数据请求 | 快,直接返回完整 HTML |
| SEO | 差,爬虫拿到的是空 HTML | 好,HTML 里有完整内容 |
| 服务端压力 | 小,只提供静态资源和 API | 大,每次请求都要渲染 |
| 开发调试 | 简单 | 复杂,要区分服务端和浏览器环境 |
| 部署运维 | 静态资源托管即可 | 需要 Node 服务,要考虑性能、缓存、容灾 |
-
适用场景
- 适合:toC、首屏性能敏感、需要 SEO
- H5 活动页、官网、内容站、电商详情页
- 不适合:toB 后台、强交互应用、内网系统
- 适合:toC、首屏性能敏感、需要 SEO
-
在本项目中的应用
- 只有 H5 用 SSR:作品在手机上传播,网络环境复杂,首屏必须快
- 配合缓存,避免每次访问都查库和重复渲染
- 渲染过程

- 注意点
- 代码要能同时在服务端和浏览器运行,服务端没有
window、document - 服务端只执行组件的创建阶段,例如 Vue 的
mounted不会在服务端执行 - SSR 不是越多越好,会增加开发、调试、上线、运维成本
- 常见方案:Vue 用 Nuxt,React 用 Next.js
- 代码要能同时在服务端和浏览器运行,服务端没有