feat(appendix): 重构工程实践章节,添加交互式演示组件
## 新增组件 (14个) - CodeSmellDemo.vue: 代码异味识别演示 - DecisionMatrixDemo.vue: 决策矩阵工具 - DesignPatternCatalogDemo.vue: 设计模式目录 - DocStructureDemo.vue: 文档结构示例 - LicenseComparisonDemo.vue: 开源许可证对比 - OpenSourceWorkflowDemo.vue: 开源协作流程 - PatternPlaygroundDemo.vue: 设计模式演练场 - RefactoringDemo.vue: 重构实战演示 - SecurityChecklistDemo.vue: 安全检查清单 - TDDCycleDemo.vue: TDD 循环演示 - TechRadarDemo.vue: 技术雷达图 - TechWritingPracticeDemo.vue: 技术写作实践 - TestPyramidDemo.vue: 测试金字塔 - WebSecurityDemo.vue: Web 安全演示 ## 文档更新 (7篇) - code-quality-refactoring.md: 代码质量与重构 - design-patterns.md: 设计模式 - open-source-collaboration.md: 开源协作 - security-thinking.md: 安全思维 - technical-writing.md: 技术写作 - technology-selection.md: 技术选型 - testing-strategies.md: 测试策略 ## 其他变更 - 将 browser-as-os.md 内容合并到 computer-networks.md - 更新 .gitignore 和 theme/index.js
This commit is contained in:
@@ -1,3 +1,237 @@
|
||||
# 代码质量与重构
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**代码写出来能跑就行了吗?** 你可能写过这样的代码:功能是实现了,但过了两周自己都看不懂了。或者团队里有人离职,留下一堆"只有上帝和他才能看懂"的代码。
|
||||
|
||||
本章带你理解什么是好代码,如何识别坏代码,以及如何安全地改进它。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 代码坏味道 | 识别常见问题 |
|
||||
| **第 2 章** | 重构手法 | 安全地改进代码 |
|
||||
| **第 3 章** | 代码审查 | 团队协作中的质量保障 |
|
||||
| **第 4 章** | 质量度量 | 用数据衡量代码健康度 |
|
||||
|
||||
学完本章,你将掌握识别代码问题、安全重构、以及通过团队协作持续提升代码质量的方法。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:代码的生命周期
|
||||
|
||||
在软件开发中,有一个常被忽视的事实:**代码被阅读的次数远远多于被编写的次数**。
|
||||
|
||||
一段代码从诞生到退役,大致会经历这样的旅程:
|
||||
|
||||
::: tip 代码的一生
|
||||
- **编写阶段**:开发者写下第一版实现,功能跑通了,测试通过了。
|
||||
- **审查阶段**:团队成员阅读代码,提出改进建议。
|
||||
- **维护阶段**:修 Bug、加功能、适配新需求——这个阶段占据了代码生命周期的 80% 以上。
|
||||
- **重构阶段**:当代码变得难以维护时,需要在不改变外部行为的前提下改善内部结构。
|
||||
- **退役阶段**:技术迭代,旧代码被新方案替代。
|
||||
:::
|
||||
|
||||
Martin Fowler 在《重构》一书中说过:**"任何一个傻瓜都能写出计算机能理解的代码,唯有好的程序员才能写出人类能理解的代码。"**
|
||||
|
||||
---
|
||||
|
||||
## 1. 代码坏味道:识别常见问题
|
||||
|
||||
### 1.1 什么是代码坏味道?
|
||||
|
||||
"代码坏味道"(Code Smell)这个概念由 Kent Beck 提出,指的是代码中那些**虽然不是 Bug,但暗示着更深层设计问题**的特征。就像房间里有股怪味——不会立刻让你生病,但说明某个地方需要清理了。
|
||||
|
||||
通过下面的交互组件,识别几种最常见的代码坏味道:
|
||||
|
||||
<CodeSmellDemo />
|
||||
|
||||
### 1.2 常见坏味道清单
|
||||
|
||||
| 坏味道 | 症状 | 危害 |
|
||||
|-------|------|------|
|
||||
| **过长函数** | 函数超过 50 行 | 难以理解、测试和复用 |
|
||||
| **魔法数字** | 代码中直接写 `86400000` | 含义不明,修改时容易遗漏 |
|
||||
| **重复代码** | 相似逻辑出现在多处 | 修改时必须同步多处,容易遗漏 |
|
||||
| **过深嵌套** | 超过 3 层的 if/for | 逻辑像迷宫,难以追踪 |
|
||||
| **过长参数列表** | 函数参数超过 4 个 | 调用困难,容易传错顺序 |
|
||||
| **上帝类** | 一个类/模块做了太多事 | 职责不清,牵一发动全身 |
|
||||
|
||||
::: tip 核心洞察
|
||||
坏味道不是"错误",而是"信号"。它告诉你:这里的设计可能需要改进。不是所有坏味道都需要立刻修复,但你需要有能力识别它们。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 重构手法:安全地改进代码
|
||||
|
||||
### 2.1 什么是重构?
|
||||
|
||||
重构(Refactoring)的定义非常精确:**在不改变代码外部行为的前提下,改善其内部结构。**
|
||||
|
||||
关键词是"不改变外部行为"。重构不是重写,不是加功能,不是修 Bug。它是对代码内部的"整理收纳"。
|
||||
|
||||
通过下面的组件,对比几种常见重构手法的前后变化:
|
||||
|
||||
<RefactoringDemo />
|
||||
|
||||
### 2.2 常用重构手法
|
||||
|
||||
**提炼函数(Extract Function)**
|
||||
|
||||
这是最常用的重构手法。当一段代码可以用一个有意义的名字来概括时,就应该把它提炼成函数。
|
||||
|
||||
```javascript
|
||||
// 重构前
|
||||
function printReport(data) {
|
||||
// 计算总价
|
||||
let total = 0
|
||||
for (const item of data.items) {
|
||||
total += item.price * item.qty
|
||||
}
|
||||
// 打印...
|
||||
}
|
||||
|
||||
// 重构后
|
||||
function calculateTotal(items) {
|
||||
return items.reduce((sum, item) => sum + item.price * item.qty, 0)
|
||||
}
|
||||
|
||||
function printReport(data) {
|
||||
const total = calculateTotal(data.items)
|
||||
// 打印...
|
||||
}
|
||||
```
|
||||
|
||||
**重命名(Rename)**
|
||||
|
||||
好的命名是最廉价也最有效的文档。当你需要写注释来解释一个变量/函数的含义时,说明它的名字不够好。
|
||||
|
||||
```javascript
|
||||
// 重构前
|
||||
const d = new Date() - startTime // 经过的时间
|
||||
const arr = users.filter(u => u.a) // 活跃用户
|
||||
|
||||
// 重构后
|
||||
const elapsedMs = new Date() - startTime
|
||||
const activeUsers = users.filter(user => user.isActive)
|
||||
```
|
||||
|
||||
**用卫语句替代嵌套(Replace Nested Conditional with Guard Clauses)**
|
||||
|
||||
```javascript
|
||||
// 重构前
|
||||
function getPayAmount(employee) {
|
||||
if (employee.isSeparated) {
|
||||
return { amount: 0 }
|
||||
} else {
|
||||
if (employee.isRetired) {
|
||||
return { amount: employee.pension }
|
||||
} else {
|
||||
return { amount: employee.salary }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 重构后
|
||||
function getPayAmount(employee) {
|
||||
if (employee.isSeparated) return { amount: 0 }
|
||||
if (employee.isRetired) return { amount: employee.pension }
|
||||
return { amount: employee.salary }
|
||||
}
|
||||
```
|
||||
|
||||
::: tip 重构的安全网
|
||||
重构最大的风险是"改着改着就改出 Bug 了"。所以重构的前提是**有测试覆盖**。每次小步重构后运行测试,确保行为没变。没有测试的代码,先补测试再重构。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 代码审查:团队协作中的质量保障
|
||||
|
||||
### 3.1 为什么需要代码审查?
|
||||
|
||||
代码审查(Code Review)是团队中最有效的质量保障手段之一。它的价值不仅在于发现 Bug,更在于:
|
||||
|
||||
- **知识共享**:团队成员了解彼此的代码,降低"巴士因子"(如果某人被巴士撞了,项目还能继续吗?)
|
||||
- **统一风格**:通过审查逐步形成团队的编码规范
|
||||
- **提前发现设计问题**:比 Bug 更难修的是糟糕的架构决策
|
||||
- **互相学习**:看别人的代码是提升编程能力的捷径
|
||||
|
||||
### 3.2 审查什么?
|
||||
|
||||
| 维度 | 关注点 |
|
||||
|------|--------|
|
||||
| **正确性** | 逻辑是否正确?边界条件是否处理? |
|
||||
| **可读性** | 命名是否清晰?结构是否易懂? |
|
||||
| **安全性** | 是否有注入风险?敏感数据是否暴露? |
|
||||
| **性能** | 是否有明显的性能问题?N+1 查询? |
|
||||
| **测试** | 是否有对应的测试?覆盖了关键路径吗? |
|
||||
|
||||
### 3.3 审查的礼仪
|
||||
|
||||
好的代码审查是**对代码的讨论,而不是对人的批评**:
|
||||
|
||||
- 用"我们"而不是"你":~~"你这里写错了"~~ → "这里我们可以考虑用 guard clause"
|
||||
- 提问而不是命令:~~"改成 const"~~ → "这个变量后面会被重新赋值吗?如果不会,用 const 更安全"
|
||||
- 给出理由:不只说"不好",要说"为什么不好"以及"怎样更好"
|
||||
|
||||
---
|
||||
|
||||
## 4. 代码质量度量
|
||||
|
||||
### 4.1 圈复杂度
|
||||
|
||||
圈复杂度(Cyclomatic Complexity)衡量代码中独立路径的数量。每个 `if`、`for`、`case`、`&&`、`||` 都会增加复杂度。
|
||||
|
||||
| 复杂度 | 评价 | 建议 |
|
||||
|--------|------|------|
|
||||
| 1-10 | 简单 | 容易理解和测试 |
|
||||
| 11-20 | 中等 | 考虑拆分 |
|
||||
| 21-50 | 复杂 | 必须重构 |
|
||||
| 50+ | 不可维护 | 紧急重构 |
|
||||
|
||||
### 4.2 代码覆盖率
|
||||
|
||||
代码覆盖率衡量测试执行了多少比例的代码。常见指标:
|
||||
|
||||
- **行覆盖率**:被执行的代码行占总行数的比例
|
||||
- **分支覆盖率**:被执行的条件分支占总分支的比例
|
||||
|
||||
::: tip 覆盖率的陷阱
|
||||
80% 的覆盖率不代表代码质量好。覆盖率只能告诉你"哪些代码没被测试到",不能告诉你"测试是否有意义"。一个只断言 `expect(true).toBe(true)` 的测试可以提高覆盖率,但毫无价值。
|
||||
:::
|
||||
|
||||
### 4.3 实用工具
|
||||
|
||||
| 工具 | 用途 |
|
||||
|------|------|
|
||||
| **ESLint** | JavaScript/TypeScript 静态分析 |
|
||||
| **Prettier** | 代码格式化,统一风格 |
|
||||
| **SonarQube** | 综合代码质量平台 |
|
||||
| **Husky** | Git hooks,提交前自动检查 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
|
||||
回顾这一路,我们从识别问题到解决问题,建立了一套完整的代码质量改进体系:
|
||||
|
||||
1. **识别**:学会闻到代码坏味道,知道哪里需要改进
|
||||
2. **重构**:掌握安全的重构手法,在测试保护下小步改进
|
||||
3. **协作**:通过代码审查,让团队共同守护代码质量
|
||||
4. **度量**:用客观指标追踪代码健康度
|
||||
|
||||
::: tip 终极思考
|
||||
代码质量不是一次性的工作,而是持续的习惯。就像保持房间整洁一样——不是等到乱得不行了才大扫除,而是每天随手整理。**童子军法则**说得好:离开时让代码比你来时更干净一点。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **经典书籍**:Martin Fowler《重构:改善既有代码的设计》是这个领域的圣经。
|
||||
- **代码整洁之道**:Robert C. Martin《Clean Code》提供了大量实用的编码原则。
|
||||
- **实践工具**:尝试在项目中配置 ESLint + Prettier + Husky,体验自动化代码质量保障。
|
||||
- **代码审查**:Google 的 Code Review 指南是业界标杆,值得学习。
|
||||
|
||||
@@ -1,3 +1,228 @@
|
||||
# 设计模式
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**为什么你的代码总是"能跑但很乱"?** 你可能遇到过这样的情况:需求一变,代码就要大改;想复用一段逻辑,却发现它和其他代码纠缠在一起。设计模式就是前人总结的"代码组织套路",帮你写出灵活、可维护的代码。
|
||||
|
||||
本章带你理解最实用的设计模式,不是死记硬背,而是理解"什么场景用什么套路"。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 设计模式是什么 | 模式的本质与分类 |
|
||||
| **第 2 章** | 创建型模式 | 如何优雅地创建对象 |
|
||||
| **第 3 章** | 结构型模式 | 如何组织代码结构 |
|
||||
| **第 4 章** | 行为型模式 | 如何管理对象间的交互 |
|
||||
|
||||
学完本章,你将掌握最常用的设计模式,能在实际项目中识别适用场景并灵活运用。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:设计模式的本质
|
||||
|
||||
想象你在学做菜。你可以每次都从零开始摸索,也可以学习经典菜谱——菜谱不会限制你的创造力,反而让你站在前人的肩膀上。设计模式就是编程世界的"经典菜谱"。
|
||||
|
||||
::: tip 设计模式的价值
|
||||
- **共同语言**:说"这里用观察者模式",团队立刻理解你的设计意图
|
||||
- **经验复用**:不用重新踩前人踩过的坑
|
||||
- **灵活扩展**:好的模式让代码面对变化时只需小改,而不是大改
|
||||
:::
|
||||
|
||||
通过下面的交互组件,浏览常见设计模式的分类和用途:
|
||||
|
||||
<DesignPatternCatalogDemo />
|
||||
|
||||
---
|
||||
|
||||
## 1. 创建型模式:如何优雅地创建对象
|
||||
|
||||
### 1.1 单例模式(Singleton)
|
||||
|
||||
**场景**:全局只需要一个实例,比如配置管理器、日志记录器、数据库连接池。
|
||||
|
||||
```javascript
|
||||
class ConfigManager {
|
||||
static instance = null
|
||||
|
||||
static getInstance() {
|
||||
if (!ConfigManager.instance) {
|
||||
ConfigManager.instance = new ConfigManager()
|
||||
}
|
||||
return ConfigManager.instance
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.config = {}
|
||||
}
|
||||
}
|
||||
|
||||
// 无论调用多少次,都是同一个实例
|
||||
const a = ConfigManager.getInstance()
|
||||
const b = ConfigManager.getInstance()
|
||||
console.log(a === b) // true
|
||||
```
|
||||
|
||||
### 1.2 工厂模式(Factory)
|
||||
|
||||
**场景**:根据不同条件创建不同类型的对象,调用方不需要知道具体的创建细节。
|
||||
|
||||
```javascript
|
||||
function createNotification(type, message) {
|
||||
switch (type) {
|
||||
case 'email':
|
||||
return { send: () => console.log(`发送邮件: ${message}`) }
|
||||
case 'sms':
|
||||
return { send: () => console.log(`发送短信: ${message}`) }
|
||||
case 'push':
|
||||
return { send: () => console.log(`推送通知: ${message}`) }
|
||||
default:
|
||||
throw new Error(`未知通知类型: ${type}`)
|
||||
}
|
||||
}
|
||||
|
||||
// 调用方不关心具体实现
|
||||
const notification = createNotification('email', '你好')
|
||||
notification.send()
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 结构型模式:如何组织代码结构
|
||||
|
||||
### 2.1 适配器模式(Adapter)
|
||||
|
||||
**场景**:两个接口不兼容,需要一个"转换插头"。比如旧 API 返回的数据格式和新组件期望的格式不一致。
|
||||
|
||||
```javascript
|
||||
// 旧 API 返回的格式
|
||||
const oldApi = {
|
||||
getUserInfo: () => ({ user_name: '张三', user_age: 25 })
|
||||
}
|
||||
|
||||
// 适配器:转换为新格式
|
||||
function adaptUser(oldUser) {
|
||||
return { name: oldUser.user_name, age: oldUser.user_age }
|
||||
}
|
||||
|
||||
const user = adaptUser(oldApi.getUserInfo())
|
||||
// { name: '张三', age: 25 }
|
||||
```
|
||||
|
||||
### 2.2 装饰器模式(Decorator)
|
||||
|
||||
**场景**:在不修改原有代码的前提下,给对象添加新功能。像给手机套壳——手机功能不变,但多了保护。
|
||||
|
||||
```javascript
|
||||
// 基础日志函数
|
||||
function log(message) {
|
||||
console.log(message)
|
||||
}
|
||||
|
||||
// 装饰:添加时间戳
|
||||
function withTimestamp(fn) {
|
||||
return (message) => fn(`[${new Date().toISOString()}] ${message}`)
|
||||
}
|
||||
|
||||
// 装饰:添加日志级别
|
||||
function withLevel(fn, level) {
|
||||
return (message) => fn(`[${level}] ${message}`)
|
||||
}
|
||||
|
||||
const enhancedLog = withTimestamp(withLevel(log, 'INFO'))
|
||||
enhancedLog('服务启动成功')
|
||||
// [2025-01-15T10:30:00.000Z] [INFO] 服务启动成功
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 行为型模式:如何管理对象间的交互
|
||||
|
||||
### 3.1 观察者模式(Observer)
|
||||
|
||||
**场景**:一个对象状态变化时,需要自动通知其他对象。比如用户下单后,需要同时发邮件、扣库存、记日志。
|
||||
|
||||
```javascript
|
||||
class EventEmitter {
|
||||
constructor() {
|
||||
this.listeners = {}
|
||||
}
|
||||
|
||||
on(event, callback) {
|
||||
if (!this.listeners[event]) this.listeners[event] = []
|
||||
this.listeners[event].push(callback)
|
||||
}
|
||||
|
||||
emit(event, data) {
|
||||
(this.listeners[event] || []).forEach(cb => cb(data))
|
||||
}
|
||||
}
|
||||
|
||||
const bus = new EventEmitter()
|
||||
bus.on('order:created', (order) => console.log('发送确认邮件', order.id))
|
||||
bus.on('order:created', (order) => console.log('扣减库存', order.id))
|
||||
bus.emit('order:created', { id: 'ORD-001' })
|
||||
```
|
||||
|
||||
### 3.2 策略模式(Strategy)
|
||||
|
||||
**场景**:同一个操作有多种算法/策略,需要在运行时切换。比如不同的排序方式、不同的价格计算规则。
|
||||
|
||||
```javascript
|
||||
const pricingStrategies = {
|
||||
normal: (price) => price,
|
||||
vip: (price) => price * 0.8,
|
||||
svip: (price) => price * 0.6
|
||||
}
|
||||
|
||||
function calculatePrice(price, memberLevel) {
|
||||
const strategy = pricingStrategies[memberLevel] || pricingStrategies.normal
|
||||
return strategy(price)
|
||||
}
|
||||
|
||||
calculatePrice(100, 'vip') // 80
|
||||
calculatePrice(100, 'svip') // 60
|
||||
```
|
||||
|
||||
通过下面的交互组件,动手体验不同设计模式的运行效果:
|
||||
|
||||
<PatternPlaygroundDemo />
|
||||
|
||||
---
|
||||
|
||||
## 4. 如何选择设计模式?
|
||||
|
||||
| 你遇到的问题 | 推荐模式 | 核心思路 |
|
||||
|-------------|---------|---------|
|
||||
| 全局只需一个实例 | 单例 | 控制实例数量 |
|
||||
| 根据条件创建不同对象 | 工厂 | 封装创建逻辑 |
|
||||
| 接口不兼容需要转换 | 适配器 | 包装一层转换 |
|
||||
| 动态添加功能 | 装饰器 | 层层包装增强 |
|
||||
| 状态变化需通知多方 | 观察者 | 发布-订阅解耦 |
|
||||
| 多种算法需运行时切换 | 策略 | 将算法封装为对象 |
|
||||
|
||||
::: tip 核心原则
|
||||
设计模式不是越多越好。**过度设计**和**没有设计**一样糟糕。只在真正需要灵活性的地方使用模式,简单问题用简单方案。记住 KISS 原则:Keep It Simple, Stupid。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
|
||||
1. **创建型模式**:解决"如何创建对象"的问题,让创建过程更灵活
|
||||
2. **结构型模式**:解决"如何组织代码"的问题,让结构更清晰
|
||||
3. **行为型模式**:解决"对象间如何交互"的问题,让协作更松耦合
|
||||
4. **灵活运用**:根据实际场景选择,不要为了用模式而用模式
|
||||
|
||||
::: tip 终极思考
|
||||
设计模式的本质是**管理变化**。好的设计让变化的部分容易修改,不变的部分保持稳定。当你写代码时问自己:"如果需求变了,我需要改多少地方?"——如果答案是"很多地方",那可能需要一个设计模式来帮忙了。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **经典书籍**:GoF《设计模式:可复用面向对象软件的基础》是设计模式的开山之作。
|
||||
- **现代视角**:JavaScript 中很多模式因为语言特性(闭包、高阶函数)变得更简洁。
|
||||
- **实践建议**:先理解问题,再考虑模式。不要拿着锤子找钉子。
|
||||
- **进阶学习**:了解 SOLID 原则,它是设计模式背后的指导思想。
|
||||
|
||||
@@ -1,3 +1,163 @@
|
||||
# 开源协作
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**想参与开源项目但不知道从哪开始?** 开源不只是"免费用别人的代码",更是一种协作方式和职业加速器。一次高质量的开源贡献,可能比简历上写十个个人项目更有说服力。
|
||||
|
||||
本章带你理解开源协作的完整流程,从找项目到提交 PR,迈出开源贡献的第一步。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 开源贡献流程 | Fork → PR 的完整链路 |
|
||||
| **第 2 章** | 开源许可证 | 不同许可证的区别 |
|
||||
| **第 3 章** | 协作礼仪 | 如何做一个受欢迎的贡献者 |
|
||||
| **第 4 章** | 从零开始贡献 | 找到适合新手的项目 |
|
||||
|
||||
学完本章,你将掌握开源协作的完整流程和礼仪,有信心向任何开源项目提交贡献。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:开源的价值
|
||||
|
||||
开源不只是代码共享,更是一种**全球化的协作模式**。Linux、React、Vue、Node.js——这些改变世界的项目都是开源的。
|
||||
|
||||
::: tip 参与开源的好处
|
||||
- **技术成长**:阅读优秀代码,接受高手 Review
|
||||
- **职业发展**:开源贡献是最好的技术名片
|
||||
- **社区归属**:成为全球开发者社区的一员
|
||||
- **回馈生态**:你每天用的工具,也需要有人维护
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 开源贡献流程
|
||||
|
||||
通过下面的交互组件,逐步了解从 Fork 到 Merge 的完整流程:
|
||||
|
||||
<OpenSourceWorkflowDemo />
|
||||
|
||||
### 1.1 流程概览
|
||||
|
||||
```
|
||||
Fork → Clone → Branch → Commit → Push → PR → Review → Merge
|
||||
```
|
||||
|
||||
### 1.2 关键步骤详解
|
||||
|
||||
**创建功能分支**:不要直接在 main 上开发。
|
||||
|
||||
```bash
|
||||
git checkout -b fix/typo-in-readme
|
||||
```
|
||||
|
||||
**写清晰的 Commit Message**:遵循项目的提交规范。
|
||||
|
||||
```bash
|
||||
git commit -m "fix: 修复 README 中的安装命令拼写错误"
|
||||
```
|
||||
|
||||
**创建 Pull Request**:PR 描述应包含:
|
||||
- 改了什么、为什么改
|
||||
- 关联的 Issue 编号(如 `Fixes #123`)
|
||||
- 如何测试你的改动
|
||||
|
||||
---
|
||||
|
||||
## 2. 开源许可证
|
||||
|
||||
通过下面的交互组件,对比常见开源许可证的区别:
|
||||
|
||||
<LicenseComparisonDemo />
|
||||
|
||||
### 2.1 常见许可证
|
||||
|
||||
| 许可证 | 特点 | 典型项目 |
|
||||
|-------|------|---------|
|
||||
| **MIT** | 最宽松,几乎无限制 | React, Vue, jQuery |
|
||||
| **Apache 2.0** | 需保留版权声明,有专利授权 | Android, Kubernetes |
|
||||
| **GPL** | 衍生作品必须也开源 | Linux, WordPress |
|
||||
| **BSD** | 类似 MIT,略有不同 | FreeBSD, Flask |
|
||||
|
||||
### 2.2 如何选择?
|
||||
|
||||
- **想让更多人用**:选 MIT
|
||||
- **想保护专利**:选 Apache 2.0
|
||||
- **想确保衍生品也开源**:选 GPL
|
||||
|
||||
---
|
||||
|
||||
## 3. 协作礼仪
|
||||
|
||||
### 3.1 提 Issue 的礼仪
|
||||
|
||||
```markdown
|
||||
<!-- 差 -->
|
||||
标题:不能用了
|
||||
内容:你们的东西有 bug
|
||||
|
||||
<!-- 好 -->
|
||||
标题:v2.1.0 在 Safari 17 下登录页白屏
|
||||
内容:
|
||||
- 环境:macOS 14.2, Safari 17.2
|
||||
- 复现步骤:1. 打开登录页 2. 输入账号密码 3. 点击登录
|
||||
- 期望行为:跳转到首页
|
||||
- 实际行为:页面白屏,控制台报错 TypeError: xxx
|
||||
- 截图:[附图]
|
||||
```
|
||||
|
||||
### 3.2 提 PR 的礼仪
|
||||
|
||||
- 先看 `CONTRIBUTING.md`,了解项目的贡献规范
|
||||
- 一个 PR 只做一件事,不要混合多个改动
|
||||
- 保持 PR 小而聚焦,方便 Review
|
||||
- 耐心等待 Review,礼貌回应反馈
|
||||
|
||||
### 3.3 Review 他人代码
|
||||
|
||||
- 先肯定做得好的地方,再提改进建议
|
||||
- 提问而不是命令:"这里是否考虑过用 X 方案?"
|
||||
- 给出理由和替代方案,而不只是说"不好"
|
||||
|
||||
---
|
||||
|
||||
## 4. 从零开始贡献
|
||||
|
||||
### 4.1 适合新手的贡献类型
|
||||
|
||||
| 类型 | 难度 | 说明 |
|
||||
|------|------|------|
|
||||
| 修复文档错误 | 低 | 错别字、过时链接、不清晰的说明 |
|
||||
| 翻译 | 低 | 将文档翻译为其他语言 |
|
||||
| 补充测试 | 中 | 为未覆盖的代码添加测试 |
|
||||
| 修复标记为 `good first issue` 的 Bug | 中 | 项目维护者标记的新手友好问题 |
|
||||
| 新功能 | 高 | 先在 Issue 中讨论方案,获得认可后再动手 |
|
||||
|
||||
### 4.2 找到合适的项目
|
||||
|
||||
- 从你日常使用的工具开始
|
||||
- GitHub 搜索 `good first issue` 标签
|
||||
- 关注项目的活跃度(最近是否有人维护)
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
|
||||
1. **流程**:Fork → Branch → Commit → PR → Review → Merge
|
||||
2. **许可证**:MIT 最宽松,GPL 最严格,根据需求选择
|
||||
3. **礼仪**:清晰的 Issue、聚焦的 PR、礼貌的沟通
|
||||
4. **起步**:从文档修复和 `good first issue` 开始
|
||||
|
||||
::: tip 终极思考
|
||||
开源的本质是**协作**。技术能力固然重要,但沟通能力、协作意识同样关键。一个态度友好、描述清晰的 PR,比一个代码完美但沟通粗暴的 PR 更受欢迎。**你的第一个 PR 不需要完美,只需要迈出第一步。**
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **入门指南**:GitHub 的 Open Source Guide 是最好的开源入门资源。
|
||||
- **实践建议**:找一个你喜欢的项目,先 Star,再读代码,最后找机会贡献。
|
||||
- **社区参与**:参加 Hacktoberfest 等开源活动,获得社区支持。
|
||||
- **维护者视角**:了解维护者的工作量和压力,做一个体贴的贡献者。
|
||||
|
||||
@@ -1,3 +1,169 @@
|
||||
# 安全思维与攻防基础
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**你的网站安全吗?** 很多开发者觉得"安全是安全团队的事",直到自己的项目被攻击、用户数据泄露。安全不是可选项,而是每个开发者的基本功。
|
||||
|
||||
本章带你建立安全思维,理解最常见的 Web 安全威胁和防御方法。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 安全思维模型 | 像攻击者一样思考 |
|
||||
| **第 2 章** | 常见 Web 攻击 | XSS、SQL 注入、CSRF |
|
||||
| **第 3 章** | 防御策略 | 输入验证、输出编码、权限控制 |
|
||||
| **第 4 章** | 安全检查清单 | 项目上线前的安全自查 |
|
||||
|
||||
学完本章,你将具备基本的安全意识,能识别和防御最常见的 Web 安全威胁。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么开发者需要懂安全?
|
||||
|
||||
想象你建了一栋房子,功能齐全、装修漂亮,但忘了装锁。安全漏洞就是代码世界里"忘了装的锁"。
|
||||
|
||||
::: tip 安全的核心原则
|
||||
- **最小权限**:只给必要的权限,不多给一分
|
||||
- **纵深防御**:不依赖单一防线,层层设防
|
||||
- **永不信任输入**:所有来自外部的数据都可能是恶意的
|
||||
- **安全默认**:默认配置应该是安全的,而不是方便的
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 常见 Web 攻击
|
||||
|
||||
通过下面的交互组件,了解三种最常见的 Web 攻击原理(仅用于教育目的):
|
||||
|
||||
<WebSecurityDemo />
|
||||
|
||||
### 1.1 XSS(跨站脚本攻击)
|
||||
|
||||
攻击者将恶意脚本注入到网页中,当其他用户访问时,脚本在他们的浏览器中执行。
|
||||
|
||||
```javascript
|
||||
// 危险:直接将用户输入插入 HTML
|
||||
element.innerHTML = userInput
|
||||
// 如果 userInput 是 <script>恶意代码</script>,就会执行
|
||||
|
||||
// 安全:使用 textContent 或转义
|
||||
element.textContent = userInput
|
||||
// 或使用框架的自动转义(Vue 的 {{ }}、React 的 JSX)
|
||||
```
|
||||
|
||||
**防御要点**:
|
||||
- 输出时转义 HTML 特殊字符(`<`, `>`, `&`, `"`, `'`)
|
||||
- 使用现代框架的自动转义机制
|
||||
- 设置 `Content-Security-Policy` HTTP 头
|
||||
|
||||
### 1.2 SQL 注入
|
||||
|
||||
攻击者通过构造特殊输入,篡改 SQL 查询的逻辑。
|
||||
|
||||
```javascript
|
||||
// 危险:字符串拼接 SQL
|
||||
const query = `SELECT * FROM users WHERE name = '${userInput}'`
|
||||
// 如果 userInput 是 ' OR '1'='1,就会返回所有用户
|
||||
|
||||
// 安全:使用参数化查询
|
||||
const query = 'SELECT * FROM users WHERE name = ?'
|
||||
db.execute(query, [userInput])
|
||||
```
|
||||
|
||||
**防御要点**:
|
||||
- 永远使用参数化查询 / 预编译语句
|
||||
- 使用 ORM 框架(如 Prisma、Sequelize)
|
||||
- 限制数据库账号权限
|
||||
|
||||
### 1.3 CSRF(跨站请求伪造)
|
||||
|
||||
攻击者诱导已登录用户访问恶意页面,利用用户的登录状态发起请求。
|
||||
|
||||
**防御要点**:
|
||||
- 使用 CSRF Token
|
||||
- 检查 `Referer` / `Origin` 头
|
||||
- 关键操作使用 POST 而非 GET
|
||||
- Cookie 设置 `SameSite` 属性
|
||||
|
||||
---
|
||||
|
||||
## 2. 防御策略
|
||||
|
||||
### 2.1 输入验证
|
||||
|
||||
```javascript
|
||||
// 白名单验证:只允许预期的格式
|
||||
function isValidEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
||||
}
|
||||
|
||||
// 长度限制
|
||||
function isValidUsername(name) {
|
||||
return name.length >= 2 && name.length <= 50
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 敏感数据保护
|
||||
|
||||
| 数据类型 | 保护措施 |
|
||||
|---------|---------|
|
||||
| 密码 | bcrypt/argon2 哈希,永不明文存储 |
|
||||
| API 密钥 | 环境变量,不提交到代码仓库 |
|
||||
| 用户数据 | HTTPS 传输,加密存储 |
|
||||
| 会话令牌 | HttpOnly + Secure + SameSite Cookie |
|
||||
|
||||
### 2.3 HTTP 安全头
|
||||
|
||||
```
|
||||
Content-Security-Policy: default-src 'self'
|
||||
X-Content-Type-Options: nosniff
|
||||
X-Frame-Options: DENY
|
||||
Strict-Transport-Security: max-age=31536000
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 安全检查清单
|
||||
|
||||
上线前,用下面的交互组件检查你的项目安全状况:
|
||||
|
||||
<SecurityChecklistDemo />
|
||||
|
||||
### 3.1 开发阶段
|
||||
|
||||
- [ ] 所有用户输入都经过验证和转义
|
||||
- [ ] 使用参数化查询,无 SQL 拼接
|
||||
- [ ] 密码使用 bcrypt 等算法哈希存储
|
||||
- [ ] 敏感配置通过环境变量管理
|
||||
- [ ] `.env` 文件已加入 `.gitignore`
|
||||
|
||||
### 3.2 部署阶段
|
||||
|
||||
- [ ] 启用 HTTPS
|
||||
- [ ] 配置安全 HTTP 头
|
||||
- [ ] 关闭调试模式和详细错误信息
|
||||
- [ ] 数据库使用最小权限账号
|
||||
- [ ] 定期更新依赖(`npm audit`)
|
||||
|
||||
---
|
||||
|
||||
## 4. 总结
|
||||
|
||||
1. **安全思维**:永不信任外部输入,最小权限,纵深防御
|
||||
2. **常见攻击**:XSS、SQL 注入、CSRF 是最高频的 Web 安全威胁
|
||||
3. **防御策略**:输入验证、输出编码、参数化查询、安全 HTTP 头
|
||||
4. **安全习惯**:上线前过安全检查清单,定期审计依赖
|
||||
|
||||
::: tip 终极思考
|
||||
安全不是一次性的工作,而是贯穿开发全过程的习惯。就像开车系安全带——不是因为你预期会出事故,而是因为这是基本的安全意识。**写每一行代码时都问自己:如果这个输入是恶意的,会发生什么?**
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **OWASP Top 10**:Web 应用安全十大风险清单,每个开发者都应该了解。
|
||||
- **实践工具**:使用 `npm audit` 检查依赖漏洞,使用 ESLint 安全插件检查代码。
|
||||
- **深入学习**:了解 HTTPS 原理、JWT 安全实践、OAuth 2.0 安全考量。
|
||||
- **安全社区**:关注安全公告,及时修补已知漏洞。
|
||||
|
||||
@@ -1,3 +1,172 @@
|
||||
# 技术文档写作
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**你写的文档有人看吗?** 很多开发者觉得"代码能跑就行,文档以后再说"。结果就是:新人入职看不懂项目、API 对接全靠口头沟通、半年后自己都忘了当初为什么这么设计。
|
||||
|
||||
本章带你掌握技术文档写作的核心方法,让你的文档真正有人看、看得懂、用得上。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 文档的类型与结构 | 不同文档的写法 |
|
||||
| **第 2 章** | 写作原则 | 清晰、准确、简洁 |
|
||||
| **第 3 章** | 实战对比 | 好文档 vs 差文档 |
|
||||
| **第 4 章** | 文档维护 | 让文档保持更新 |
|
||||
|
||||
学完本章,你将能写出结构清晰、内容准确、易于维护的技术文档。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么技术文档重要?
|
||||
|
||||
代码告诉计算机"怎么做",文档告诉人类"为什么这么做"。没有文档的项目就像没有说明书的电器——能用,但用起来全靠猜。
|
||||
|
||||
::: tip 好文档的价值
|
||||
- **降低沟通成本**:新人自助上手,减少重复解答
|
||||
- **保存决策上下文**:记录"为什么",而不只是"是什么"
|
||||
- **提升项目可信度**:好文档是开源项目的门面
|
||||
- **加速协作**:API 文档让前后端并行开发
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 文档的类型与结构
|
||||
|
||||
通过下面的交互组件,了解不同类型文档的标准结构:
|
||||
|
||||
<DocStructureDemo />
|
||||
|
||||
### 1.1 常见文档类型
|
||||
|
||||
| 文档类型 | 目标读者 | 核心内容 |
|
||||
|---------|---------|---------|
|
||||
| **README** | 所有人 | 项目是什么、怎么用、怎么贡献 |
|
||||
| **API 文档** | 接口调用方 | 端点、参数、响应、错误码 |
|
||||
| **架构文档** | 开发团队 | 系统设计、技术选型、数据流 |
|
||||
| **变更日志** | 用户/开发者 | 版本变化、新增/修复/破坏性变更 |
|
||||
| **贡献指南** | 贡献者 | 开发环境、代码规范、PR 流程 |
|
||||
|
||||
### 1.2 README 的黄金结构
|
||||
|
||||
一个好的 README 应该包含:
|
||||
|
||||
1. **项目名称 + 一句话描述**:让人 3 秒内知道这是什么
|
||||
2. **快速开始**:最少步骤跑起来
|
||||
3. **功能特性**:核心卖点
|
||||
4. **安装方式**:详细的环境要求和安装步骤
|
||||
5. **使用示例**:可复制粘贴的代码
|
||||
6. **贡献指南**:如何参与
|
||||
7. **许可证**:法律信息
|
||||
|
||||
---
|
||||
|
||||
## 2. 写作原则
|
||||
|
||||
### 2.1 清晰优先
|
||||
|
||||
```markdown
|
||||
<!-- 差:模糊不清 -->
|
||||
这个函数处理数据。
|
||||
|
||||
<!-- 好:具体明确 -->
|
||||
将原始订单数据转换为发票格式,包含税费计算和币种转换。
|
||||
```
|
||||
|
||||
### 2.2 面向读者
|
||||
|
||||
写文档前先问:**谁会读这个文档?他们需要什么信息?**
|
||||
|
||||
- 给新手写:解释术语,提供完整示例
|
||||
- 给有经验的开发者写:直奔主题,提供 API 参考
|
||||
- 给非技术人员写:用类比,避免术语
|
||||
|
||||
### 2.3 代码示例是最好的文档
|
||||
|
||||
```markdown
|
||||
<!-- 差:只有文字描述 -->
|
||||
调用 createUser 函数,传入用户名和邮箱参数。
|
||||
|
||||
<!-- 好:给出可运行的示例 -->
|
||||
const user = await createUser({
|
||||
name: '张三',
|
||||
email: 'zhangsan@example.com'
|
||||
})
|
||||
// 返回: { id: 'u_123', name: '张三', createdAt: '2025-01-15' }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 实战对比
|
||||
|
||||
通过下面的交互组件,对比好的技术写作和差的技术写作:
|
||||
|
||||
<TechWritingPracticeDemo />
|
||||
|
||||
### 3.1 Commit Message 规范
|
||||
|
||||
```
|
||||
# 差
|
||||
fix bug
|
||||
update code
|
||||
|
||||
# 好(Conventional Commits)
|
||||
fix: 修复登录页在 Safari 下白屏的问题
|
||||
feat: 支持批量导出 PDF 格式报表
|
||||
docs: 更新 API 认证章节的示例代码
|
||||
```
|
||||
|
||||
### 3.2 注释的艺术
|
||||
|
||||
```javascript
|
||||
// 差:描述"是什么"(代码已经说了)
|
||||
// 遍历数组
|
||||
for (const item of items) { ... }
|
||||
|
||||
// 好:解释"为什么"
|
||||
// 倒序遍历,因为删除元素时正序会跳过下一个
|
||||
for (let i = items.length - 1; i >= 0; i--) { ... }
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 文档维护
|
||||
|
||||
### 4.1 文档即代码
|
||||
|
||||
把文档和代码放在同一个仓库,用同样的工作流管理:
|
||||
|
||||
- 文档变更随代码一起提交 PR
|
||||
- CI 检查文档格式和链接有效性
|
||||
- 版本发布时同步更新文档
|
||||
|
||||
### 4.2 避免文档腐烂
|
||||
|
||||
| 问题 | 解决方案 |
|
||||
|------|---------|
|
||||
| 文档过时 | 代码变更时强制更新文档(PR 检查) |
|
||||
| 无人维护 | 指定文档负责人 |
|
||||
| 内容重复 | 单一信息源,其他地方引用链接 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
|
||||
1. **类型匹配**:不同文档有不同的结构和写法
|
||||
2. **清晰优先**:具体、准确、面向读者
|
||||
3. **示例驱动**:好的代码示例胜过千言万语
|
||||
4. **持续维护**:文档即代码,随项目一起演进
|
||||
|
||||
::: tip 终极思考
|
||||
写文档不是浪费时间,而是**节省未来的时间**。你今天花 30 分钟写的文档,可能帮 10 个人各节省 1 小时。好的文档是对团队最好的投资。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **写作指南**:Google 的技术写作课程(Technical Writing)免费且实用。
|
||||
- **文档工具**:VitePress、Docusaurus、GitBook 等现代文档框架。
|
||||
- **API 文档**:OpenAPI/Swagger 规范是 API 文档的行业标准。
|
||||
- **实践建议**:从给自己的项目写一个好的 README 开始。
|
||||
|
||||
@@ -1,3 +1,140 @@
|
||||
# 技术选型方法论
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**React 还是 Vue?MySQL 还是 PostgreSQL?** 技术选型是每个项目开始时最重要的决策之一。选错了,可能要花几个月重写;选对了,团队效率翻倍。
|
||||
|
||||
本章带你建立系统化的技术选型思维,不再凭感觉选技术。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 技术雷达 | 了解技术的成熟度 |
|
||||
| **第 2 章** | 选型维度 | 从哪些角度评估技术 |
|
||||
| **第 3 章** | 决策矩阵 | 量化对比做决策 |
|
||||
| **第 4 章** | 常见陷阱 | 避免选型中的坑 |
|
||||
|
||||
学完本章,你将掌握一套系统化的技术选型方法,能为项目做出理性的技术决策。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:技术选型的本质
|
||||
|
||||
技术选型不是"哪个技术最好"的问题,而是"哪个技术最适合当前场景"的问题。就像选交通工具——飞机最快,但去隔壁小区不需要坐飞机。
|
||||
|
||||
::: tip 选型的核心原则
|
||||
- **没有银弹**:没有一种技术适合所有场景
|
||||
- **场景驱动**:先明确需求,再选技术
|
||||
- **团队优先**:团队熟悉的技术往往是最好的选择
|
||||
- **可逆性**:优先选择容易替换的方案
|
||||
:::
|
||||
|
||||
通过下面的交互组件,了解当前技术生态的全景:
|
||||
|
||||
<TechRadarDemo />
|
||||
|
||||
---
|
||||
|
||||
## 1. 选型维度
|
||||
|
||||
### 1.1 核心评估维度
|
||||
|
||||
| 维度 | 关注点 | 权重建议 |
|
||||
|------|--------|---------|
|
||||
| **团队能力** | 团队是否熟悉?学习成本多高? | 高 |
|
||||
| **社区生态** | 文档质量、第三方库、Stack Overflow 答案数 | 高 |
|
||||
| **性能需求** | 是否满足性能要求? | 中-高 |
|
||||
| **维护状态** | 是否活跃维护?最近一次发布是什么时候? | 中 |
|
||||
| **许可证** | 是否与项目的商业模式兼容? | 中 |
|
||||
| **招聘市场** | 能否招到熟悉这个技术的人? | 中 |
|
||||
|
||||
### 1.2 实际案例:前端框架选型
|
||||
|
||||
```
|
||||
项目:企业内部管理系统
|
||||
团队:5 人,3 人熟悉 Vue,1 人熟悉 React,1 人新手
|
||||
需求:表单密集、权限复杂、不需要 SEO
|
||||
|
||||
分析:
|
||||
- 团队 60% 熟悉 Vue → Vue 优先
|
||||
- 表单密集 → Element Plus 生态成熟
|
||||
- 不需要 SSR → 不需要 Next.js/Nuxt
|
||||
- 结论:Vue 3 + Element Plus
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 2. 决策矩阵
|
||||
|
||||
当多个选项难以直觉判断时,用决策矩阵量化对比。
|
||||
|
||||
通过下面的交互组件,体验决策矩阵的使用方法:
|
||||
|
||||
<DecisionMatrixDemo />
|
||||
|
||||
### 2.1 如何使用决策矩阵
|
||||
|
||||
1. **列出候选方案**:比如 React vs Vue vs Svelte
|
||||
2. **确定评估维度**:团队能力、生态、性能、学习曲线
|
||||
3. **分配权重**:根据项目需求,给每个维度打权重(总和 100%)
|
||||
4. **逐项打分**:每个方案在每个维度上打 1-5 分
|
||||
5. **加权求和**:得出最终得分
|
||||
|
||||
### 2.2 示例
|
||||
|
||||
| 维度 | 权重 | React | Vue | Svelte |
|
||||
|------|------|-------|-----|--------|
|
||||
| 团队能力 | 30% | 3 | 5 | 1 |
|
||||
| 社区生态 | 25% | 5 | 4 | 2 |
|
||||
| 学习曲线 | 20% | 3 | 4 | 5 |
|
||||
| 性能 | 15% | 4 | 4 | 5 |
|
||||
| 招聘市场 | 10% | 5 | 4 | 2 |
|
||||
| **加权总分** | | **3.75** | **4.35** | **2.75** |
|
||||
|
||||
---
|
||||
|
||||
## 3. 常见陷阱
|
||||
|
||||
### 3.1 简历驱动开发
|
||||
|
||||
> "用这个新技术,我简历上又能多写一条"
|
||||
|
||||
选技术应该基于项目需求,而不是个人简历。新技术意味着更多的未知风险和更少的社区支持。
|
||||
|
||||
### 3.2 盲目追新
|
||||
|
||||
| 心态 | 现实 |
|
||||
|------|------|
|
||||
| "新的一定比旧的好" | 新技术可能有未发现的 Bug |
|
||||
| "大厂在用,我们也该用" | 大厂的场景和你的可能完全不同 |
|
||||
| "这个技术 Star 数最多" | Star 数不等于适合你的项目 |
|
||||
|
||||
### 3.3 忽视迁移成本
|
||||
|
||||
选型时不仅要看"用起来怎么样",还要看"如果要换,代价多大"。优先选择:
|
||||
- 遵循标准协议的方案(如 SQL vs 私有查询语言)
|
||||
- 有清晰迁移路径的方案
|
||||
- 不会深度锁定的方案
|
||||
|
||||
---
|
||||
|
||||
## 4. 总结
|
||||
|
||||
1. **技术雷达**:了解技术的成熟度,区分采纳/试验/评估/暂缓
|
||||
2. **选型维度**:团队能力 > 社区生态 > 性能需求 > 维护状态
|
||||
3. **决策矩阵**:量化对比,减少主观偏见
|
||||
4. **避免陷阱**:不追新、不跟风、考虑迁移成本
|
||||
|
||||
::: tip 终极思考
|
||||
最好的技术选型往往是**最无聊的选型**。选择成熟、稳定、团队熟悉的技术,把创新的精力留给业务本身。记住:**技术是手段,不是目的。用户不关心你用了什么框架,他们只关心产品好不好用。**
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **ThoughtWorks 技术雷达**:每半年发布一次,是了解技术趋势的权威参考。
|
||||
- **实践建议**:下次选型时,试着用决策矩阵做一次量化对比。
|
||||
- **架构决策记录(ADR)**:用文档记录每次技术选型的理由和权衡。
|
||||
- **反面教材**:了解一些因技术选型失误导致项目失败的案例。
|
||||
|
||||
@@ -1,3 +1,170 @@
|
||||
# 测试策略
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**你的代码真的"没问题"吗?** 每次改完代码手动点一遍看看有没有坏——这种方式在项目小的时候还能凑合,但当代码量增长到几万行、团队扩展到十几人时,"手动点点看"就是一场灾难。
|
||||
|
||||
本章带你理解软件测试的核心策略,从测试金字塔到 TDD,建立系统化的质量保障思维。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 测试金字塔 | 测试的层次与比例 |
|
||||
| **第 2 章** | 单元测试实战 | 如何写好一个测试 |
|
||||
| **第 3 章** | TDD 驱动开发 | 红绿重构循环 |
|
||||
| **第 4 章** | 测试策略选择 | 不同场景的方案 |
|
||||
|
||||
学完本章,你将理解如何为项目选择合适的测试策略,写出有价值的测试,并通过 TDD 提升代码设计质量。
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么需要自动化测试?
|
||||
|
||||
想象你是一个建筑工程师。每次修改图纸后,你不会亲自爬上每一层楼去检查结构是否安全——你会依赖一套**自动化的检测系统**。软件测试就是代码世界的"结构检测系统"。
|
||||
|
||||
::: tip 自动化测试的价值
|
||||
- **回归保护**:修改 A 功能时,自动检测 B、C、D 功能是否被影响
|
||||
- **重构信心**:有测试覆盖的代码,重构时心里有底
|
||||
- **活文档**:好的测试就是最好的使用说明书
|
||||
- **快速反馈**:几秒钟内知道代码是否正确,而不是等到部署后才发现问题
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. 测试金字塔:测试的层次与比例
|
||||
|
||||
### 1.1 三层金字塔
|
||||
|
||||
Mike Cohn 提出的测试金字塔是测试策略的经典模型。它告诉我们:**不同类型的测试应该有不同的数量比例**。
|
||||
|
||||
通过下面的交互组件,点击金字塔的每一层,了解各层测试的特点:
|
||||
|
||||
<TestPyramidDemo />
|
||||
|
||||
### 1.2 为什么是金字塔形?
|
||||
|
||||
金字塔形状反映了一个核心权衡:**速度与真实度的取舍**。
|
||||
|
||||
- **底层(单元测试)**:速度极快、数量最多、成本最低,但只能验证单个零件
|
||||
- **中层(集成测试)**:速度适中、数量适中,验证零件之间的配合
|
||||
- **顶层(E2E 测试)**:最接近真实用户,但速度慢、维护成本高、容易因环境问题失败
|
||||
|
||||
> **反模式:冰淇淋甜筒** —— 如果你的项目 E2E 测试最多、单元测试最少,那就是倒过来的"冰淇淋甜筒"。这意味着测试套件运行缓慢、经常失败、维护成本极高。
|
||||
|
||||
---
|
||||
|
||||
## 2. 单元测试实战
|
||||
|
||||
### 2.1 什么是好的单元测试?
|
||||
|
||||
好的单元测试遵循 **FIRST** 原则:
|
||||
|
||||
| 原则 | 含义 | 说明 |
|
||||
|------|------|------|
|
||||
| **F**ast | 快速 | 毫秒级完成,开发者愿意频繁运行 |
|
||||
| **I**ndependent | 独立 | 测试之间互不依赖,可以单独运行 |
|
||||
| **R**epeatable | 可重复 | 任何环境下运行结果一致 |
|
||||
| **S**elf-validating | 自验证 | 结果是明确的通过/失败,不需要人工判断 |
|
||||
| **T**imely | 及时 | 在写代码的同时(或之前)写测试 |
|
||||
|
||||
### 2.2 测试的结构:AAA 模式
|
||||
|
||||
每个测试都应该有清晰的三段式结构:
|
||||
|
||||
```javascript
|
||||
test('应该正确计算含税价格', () => {
|
||||
// Arrange(准备)—— 设置测试数据
|
||||
const price = 100
|
||||
const taxRate = 0.13
|
||||
|
||||
// Act(执行)—— 调用被测函数
|
||||
const result = calculateTotalWithTax(price, taxRate)
|
||||
|
||||
// Assert(断言)—— 验证结果
|
||||
expect(result).toBe(113)
|
||||
})
|
||||
```
|
||||
|
||||
### 2.3 测试什么?不测什么?
|
||||
|
||||
**应该测试的:**
|
||||
- 核心业务逻辑(价格计算、权限判断、数据转换)
|
||||
- 边界条件(空值、零、负数、超大数)
|
||||
- 错误处理路径
|
||||
|
||||
**不需要测试的:**
|
||||
- 第三方库的内部实现
|
||||
- 简单的 getter/setter
|
||||
- 框架自身的功能(如 Vue 的响应式系统)
|
||||
|
||||
---
|
||||
|
||||
## 3. TDD:测试驱动开发
|
||||
|
||||
### 3.1 红绿重构循环
|
||||
|
||||
TDD(Test-Driven Development)的核心是一个简单的循环:**先写测试,再写实现,最后重构**。
|
||||
|
||||
通过下面的交互组件,亲自体验 TDD 的完整循环:
|
||||
|
||||
<TDDCycleDemo />
|
||||
|
||||
### 3.2 TDD 的三条规则
|
||||
|
||||
1. **不写任何产品代码,除非是为了让一个失败的测试通过**
|
||||
2. **只写刚好让测试失败的测试代码**(编译不过也算失败)
|
||||
3. **只写刚好让测试通过的产品代码**
|
||||
|
||||
### 3.3 TDD 的真正价值
|
||||
|
||||
TDD 的价值不仅在于"先写测试",更在于它**迫使你思考接口设计**。当你先写测试时,你是站在"使用者"的角度思考:这个函数应该接收什么参数?返回什么结果?这自然会导向更好的 API 设计。
|
||||
|
||||
::: tip TDD 不是银弹
|
||||
TDD 适合逻辑密集的代码(算法、业务规则、数据转换),但对于 UI 布局、探索性原型等场景,强制 TDD 反而会拖慢速度。关键是理解它的思想,灵活运用。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 测试策略选择
|
||||
|
||||
### 4.1 不同项目的测试重点
|
||||
|
||||
| 项目类型 | 测试重点 | 推荐比例 |
|
||||
|----------|----------|----------|
|
||||
| **工具库/SDK** | 单元测试为主 | 90% 单元 + 10% 集成 |
|
||||
| **API 服务** | 集成测试为主 | 30% 单元 + 60% 集成 + 10% E2E |
|
||||
| **Web 应用** | 均衡分布 | 50% 单元 + 30% 集成 + 20% E2E |
|
||||
| **MVP/原型** | 关键路径 E2E | 少量核心测试即可 |
|
||||
|
||||
### 4.2 常用测试工具
|
||||
|
||||
| 工具 | 类型 | 适用场景 |
|
||||
|------|------|----------|
|
||||
| **Vitest** | 单元/集成 | Vite 项目首选,兼容 Jest API |
|
||||
| **Jest** | 单元/集成 | Node.js 生态最流行 |
|
||||
| **Playwright** | E2E | 跨浏览器,微软出品 |
|
||||
| **Cypress** | E2E | 开发体验好,调试方便 |
|
||||
| **Testing Library** | 组件测试 | 以用户视角测试 UI 组件 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 总结
|
||||
|
||||
1. **测试金字塔**:底层多、顶层少,平衡速度与真实度
|
||||
2. **单元测试**:遵循 FIRST 原则和 AAA 模式,测试核心逻辑
|
||||
3. **TDD**:红绿重构循环,用测试驱动设计
|
||||
4. **策略选择**:根据项目类型和阶段,选择合适的测试比例
|
||||
|
||||
::: tip 终极思考
|
||||
测试不是负担,而是**加速器**。短期看,写测试确实多花了时间;长期看,它节省了无数次手动验证、排查回归 Bug、以及深夜紧急修复的时间。好的测试让你有信心说出那句话:**"放心改,测试会告诉我们有没有问题。"**
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- **经典书籍**:Kent Beck《测试驱动开发》是 TDD 的开山之作。
|
||||
- **实用指南**:尝试用 Vitest 为一个小项目写测试,体验从零开始的测试流程。
|
||||
- **测试模式**:了解 Mock、Stub、Spy 的区别和使用场景。
|
||||
- **持续集成**:将测试集成到 CI/CD 流水线中,每次提交自动运行。
|
||||
|
||||
Reference in New Issue
Block a user