1. 项目概述
"day06"这个看似简单的标题背后,其实隐藏着一个完整的项目开发周期记录。作为一名从业多年的全栈开发者,我习惯用"day+数字"的形式来记录项目开发日志。这种命名方式简洁高效,既能清晰标记开发进度,又能方便后期回溯。今天要分享的就是我在某个中型Web应用开发项目中第六天的完整工作记录和技术思考。
在实际开发中,第六天往往是个关键节点 - 这时基础架构已经搭建完毕,核心功能开始成型,但同时也进入了最容易出现技术债务的阶段。我将通过这篇日志,详细还原当天的开发内容、技术决策过程以及那些只有踩过坑才知道的实战经验。
2. 开发环境与技术栈
2.1 基础环境配置
项目使用的是标准的MERN技术栈(MongoDB + Express + React + Node.js),这在当前全栈开发中非常普遍。第六天开始时,我们的环境配置如下:
- Node.js v16.14.2 (LTS版本,稳定性优先)
- MongoDB Atlas集群(开发环境使用免费的M0实例)
- React 18.2 + Vite构建工具(放弃了传统的create-react-app,追求更快的热更新)
- VS Code作为主IDE,配合ESLint和Prettier保证代码风格统一
提示:即使在个人项目中,也建议从一开始就配置好lint规则。我在day03时就因为没做这个,导致day06不得不花时间重构代码格式。
2.2 当天的核心依赖更新
第六天新增了几个关键依赖:
bash复制# 后端
npm install mongoose-unique-validator # 增强数据验证
npm install express-rate-limit # API限流
# 前端
npm install react-query @tanstack/react-query # 替代redux管理服务端状态
npm install zod # 表单验证库
这些选择背后都有深思熟虑:
- mongoose-unique-validator:在基础模型验证之外提供更友好的唯一性错误提示
- react-query:简化数据获取和缓存逻辑,减少不必要的全局状态
- zod:在运行时进行类型检查,弥补TypeScript编译时检查的不足
3. 核心开发内容
3.1 用户认证模块增强
第六天主要完善了用户系统。原先的基础注册/登录功能存在几个问题:
- 密码重置流程缺失
- 邮箱验证不可靠
- 会话管理过于简单
解决方案采用了分步实施策略:
步骤1:重构密码处理中间件
javascript复制// 原先简单的哈希处理
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
this.password = await bcrypt.hash(this.password, 10);
next();
});
// 增强后的版本
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(12); // 增加计算成本
this.password = await bcrypt.hash(this.password, salt);
this.passwordChangedAt = Date.now() - 1000; // 防止新token立即失效
} catch (err) {
return next(new AppError('密码处理失败', 500));
}
next();
});
步骤2:实现JWT刷新机制
原先的token过期处理很粗暴 - 直接让用户重新登录。第六天实现了无感的刷新流程:
javascript复制// 新增refreshToken路由
router.post('/refresh-token', (req, res) => {
const { refreshToken } = req.body;
try {
const decoded = jwt.verify(refreshToken, process.env.JWT_REFRESH_SECRET);
const newAccessToken = generateAccessToken(decoded.userId);
res.json({
accessToken: newAccessToken,
expiresIn: process.env.JWT_ACCESS_EXPIRES_IN
});
} catch (err) {
throw new AppError('无效的刷新令牌', 401);
}
});
3.2 性能优化实践
随着功能增加,day06开始出现明显的性能问题。通过Chrome DevTools发现主要瓶颈在于:
- 未优化的MongoDB查询
- 前端重复渲染
- 过大的初始加载包
解决方案:
后端优化:
- 为常用查询添加复合索引
javascript复制// 在用户模型添加索引
userSchema.index({ email: 1, active: 1 });
- 实现数据投影,减少不必要字段返回
javascript复制// 改造前
const users = await User.find();
// 改造后
const users = await User.find().select('name email avatar');
前端优化:
- 使用React.memo优化组件
- 按需加载第三方库
javascript复制// 动态导入heavy library
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
- 配置代码分割
vite复制// vite.config.js
export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'axios']
}
}
}
}
})
4. 问题排查与解决
4.1 MongoDB连接泄漏
第六天下午突然发现开发服务器的内存使用量异常增长。通过以下步骤定位问题:
- 使用
process.memoryUsage()监控内存变化 - 发现每次API调用后内存不释放
- 最终定位到未正确关闭MongoDB游标
错误示例:
javascript复制// 有问题的代码
const getUsers = async () => {
const cursor = User.find().cursor();
const users = [];
for await (const doc of cursor) {
users.push(doc);
}
// 忘记关闭cursor
return users;
};
修复方案:
javascript复制// 正确做法
const getUsers = async () => {
const cursor = User.find().cursor();
try {
const users = [];
for await (const doc of cursor) {
users.push(doc);
}
return users;
} finally {
await cursor.close(); // 确保游标关闭
}
};
4.2 React状态管理混乱
随着组件增多,props drilling问题开始显现。原先的方案是提升状态到最顶层组件,但这导致重渲染问题。
优化方案:
- 使用Context API + useReducer管理全局状态
- 对局部状态使用React Query缓存
- 实现自定义hook封装复杂逻辑
javascript复制// 创建用户上下文
const UserContext = createContext();
function UserProvider({ children }) {
const [state, dispatch] = useReducer(userReducer, initialState);
// 封装常用操作
const value = {
...state,
login: (credentials) => dispatch({ type: 'LOGIN', payload: credentials }),
logout: () => dispatch({ type: 'LOGOUT' })
};
return <UserContext.Provider value={value}>{children}</UserContext.Provider>;
}
// 使用自定义hook
function useAuth() {
const context = useContext(UserContext);
if (!context) {
throw new Error('useAuth必须在UserProvider内使用');
}
return context;
}
5. 开发心得与最佳实践
经过第六天的密集开发,总结出以下几点关键经验:
-
数据库操作安全:
- 永远不要相信客户端传入的查询参数
- 对MongoDB操作使用try-catch包裹
- 查询时明确指定字段投影
-
前端性能黄金法则:
- 测量后再优化(使用DevTools的Performance面板)
- 虚拟化长列表(react-window或react-virtualized)
- 避免内联函数定义导致的不必要重渲染
-
全栈开发工作流:
mermaid复制graph TD A[编写测试用例] --> B[实现功能] B --> C[本地测试] C --> D[代码审查] D --> E[部署到staging] E --> F[端到端测试] -
调试技巧:
- 使用
debug模块替代console.log - 在VS Code中配置Mocha调试配置
- 对异步错误使用
longjohn获取完整堆栈
- 使用
第六天的开发让我深刻体会到:项目进行到中期,技术债务会像滚雪球一样增长。这时候必须放慢脚步做好重构和优化,否则后期将付出更大代价。特别要注意的是测试覆盖率 - 在day06结束时,我们的测试覆盖率从75%下降到了68%,这是个危险的信号。于是我立即安排了day07上午的测试修复计划。
