1. 项目概述与核心技术选型
这个在线考试系统采用Vue.js作为前端框架,Node.js作为后端服务,构建了一个完整的B/S架构应用。Vue的响应式特性和组件化开发模式,配合Node.js的高效I/O处理能力,能够很好地满足在线考试场景中的实时性要求和复杂交互需求。
在实际开发中,我选择了Vue 3的组合式API写法,相比选项式API更利于功能逻辑的组织和复用。前端工程使用Vite作为构建工具,其基于原生ES模块的开发服务器启动速度极快,热更新几乎瞬间完成,大幅提升了开发体验。
后端采用Express框架搭建RESTful API服务,配合MongoDB存储考试数据和用户信息。这种技术栈组合特别适合快速迭代的中小型项目,从原型到上线可以控制在很短的周期内完成。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 系统架构设计与模块划分
2.1 前端架构设计
前端采用经典的Vue单页应用结构,通过Vue Router实现页面导航。核心模块包括:
- 考生认证模块:处理登录、注册、身份验证
- 考试模块:试题展示、答题卡、计时器
- 管理模块:题库管理、考试安排、成绩统计
路由设计采用懒加载方式拆分代码包,显著提升首屏加载速度:
javascript复制const routes = [
{
path: '/exam/:id',
component: () => import('./views/Exam.vue'),
meta: { requiresAuth: true }
}
]
2.2 后端服务架构
Node.js服务采用分层设计:
- 路由层:处理HTTP请求和响应
- 控制器层:业务逻辑处理
- 服务层:数据操作和第三方集成
- 模型层:定义数据结构和数据库交互
这种分层架构使得代码职责清晰,便于团队协作和维护。特别在考试提交高峰期,良好的架构设计能有效应对并发压力。
3. 核心功能实现细节
3.1 实时考试功能实现
考试模块需要解决几个关键技术点:
- 试题加载优化:采用分页加载策略,避免一次性加载全部试题
- 答题状态持久化:使用localStorage暂存答案,防止意外刷新导致数据丢失
- 倒计时同步:通过WebSocket保持服务端和客户端时间同步
核心倒计时组件实现:
javascript复制// 使用Composition API
setup() {
const timeLeft = ref(3600) // 剩余秒数
const timer = setInterval(() => {
if(timeLeft.value <= 0) {
clearInterval(timer)
submitExam()
} else {
timeLeft.value--
}
}, 1000)
// 格式化显示
const formattedTime = computed(() => {
const mins = Math.floor(timeLeft.value / 60)
const secs = timeLeft.value % 60
return `${mins}:${secs < 10 ? '0' : ''}${secs}`
})
return { formattedTime }
}
3.2 防作弊机制设计
在线考试系统的可信度关键在于防作弊措施:
- 页面锁定:通过全屏API限制切换窗口
- 行为监控:记录异常操作如频繁切换标签页
- 随机策略:试题顺序和选项随机排列
- 人脸识别:关键节点进行身份验证
实现全屏监听的代码示例:
javascript复制// 进入全屏
async function enterFullscreen() {
try {
await document.documentElement.requestFullscreen()
document.addEventListener('fullscreenchange', handleFullscreenChange)
} catch (err) {
console.error('全屏模式失败:', err)
}
}
// 处理全屏状态变化
function handleFullscreenChange() {
if (!document.fullscreenElement) {
// 退出全屏视为可疑行为
recordSuspiciousActivity('退出全屏')
}
}
4. 性能优化实践
4.1 前端性能调优
- 代码分割:利用Vue的异步组件和路由懒加载
- 图片优化:将试题中的图片转为WebP格式
- 缓存策略:合理配置HTTP缓存头
- 按需引入:只加载必要的第三方库
vite.config.js中的优化配置示例:
javascript复制export default defineConfig({
build: {
rollupOptions: {
output: {
manualChunks(id) {
if (id.includes('node_modules')) {
return 'vendor'
}
}
}
}
}
})
4.2 后端性能保障
- 数据库索引:为高频查询字段建立合适索引
- 连接池:使用mongoose连接池管理数据库连接
- 缓存策略:Redis缓存热点数据
- 负载均衡:PM2集群模式利用多核CPU
数据库连接优化示例:
javascript复制// mongoose连接配置
mongoose.connect(DB_URI, {
poolSize: 10, // 连接池大小
socketTimeoutMS: 45000, // socket超时
connectTimeoutMS: 30000 // 连接超时
})
5. 安全防护措施
5.1 常见Web安全防护
- XSS防护:使用vue-sanitize处理富文本内容
- CSRF防护:Express中配置csurf中间件
- SQL注入:mongoose的Schema设计避免直接拼接查询
- 数据校验:使用Joi进行请求参数验证
请求验证中间件示例:
javascript复制const examSchema = Joi.object({
examId: Joi.string().hex().length(24).required(),
answers: Joi.array().items(
Joi.object({
questionId: Joi.string().required(),
selected: Joi.alternatives().try(
Joi.string(),
Joi.number(),
Joi.array()
).required()
})
)
})
function validateExamInput(req, res, next) {
const { error } = examSchema.validate(req.body)
if (error) {
return res.status(400).json({ error: error.details[0].message })
}
next()
}
5.2 考试业务安全
- 传输加密:HTTPS + 敏感数据额外加密
- 答案防篡改:提交时包含数字签名
- 时间同步:服务端校验考试时间有效性
- 提交防重放:使用一次性token防止重复提交
答案签名实现示例:
javascript复制function generateAnswerSignature(answers, secret) {
const hmac = crypto.createHmac('sha256', secret)
const sortedAnswers = _.sortBy(answers, 'questionId')
const answerString = sortedAnswers.map(a =>
`${a.questionId}:${a.selected}`
).join('|')
return hmac.update(answerString).digest('hex')
}
6. 部署与运维实践
6.1 前端部署方案
- 静态资源部署:使用Nginx托管构建产物
- CDN加速:配置合适的缓存策略
- 版本控制:文件名包含hash值实现长期缓存
- 监控接入:Sentry捕获前端错误
Nginx配置示例:
nginx复制server {
listen 80;
server_name exam.example.com;
location / {
root /var/www/exam-system/dist;
try_files $uri $uri/ /index.html;
expires 1y;
add_header Cache-Control "public";
}
location /api {
proxy_pass http://localhost:3000;
}
}
6.2 后端服务部署
- 进程管理:使用PM2保持服务稳定运行
- 日志收集:配置日志轮转和集中管理
- 性能监控:接入APM工具
- 容器化:Docker封装实现环境一致性
PM2生态系统配置文件:
javascript复制module.exports = {
apps: [{
name: 'exam-api',
script: 'server.js',
instances: 'max',
exec_mode: 'cluster',
env: {
NODE_ENV: 'production'
},
max_memory_restart: '1G',
error_file: '/var/log/exam-api/err.log',
out_file: '/var/log/exam-api/out.log',
merge_logs: true,
log_date_format: 'YYYY-MM-DD HH:mm Z'
}]
}
7. 项目开发中的经验总结
在开发这个在线考试系统的过程中,有几个关键经验值得分享:
-
状态管理策略:对于复杂表单场景(如考试答题),直接使用Vue的reactive API比引入Pinia/Vuex更轻量高效。全局状态只用于真正的共享数据。
-
错误边界处理:在前端封装错误边界组件捕获子组件异常,避免整个应用崩溃:
javascript复制// ErrorBoundary.vue
onErrorCaptured((err, instance, info) => {
sentryCapture(err)
showErrorMessage()
return false // 阻止错误继续向上传播
})
-
数据库设计技巧:MongoDB的文档设计要平衡查询效率和更新复杂度。例如将高频一起查询的数据放在同一文档中,但避免创建过大的文档影响写入性能。
-
性能监控指标:除了常规的API响应时间,还需要特别关注:
- 考试提交成功率
- 试题加载时间P99值
- WebSocket连接稳定性
- 异常行为告警响应速度
- 压力测试发现:在模拟500并发考试提交的场景下,最初的设计出现了数据库连接耗尽的问题。通过以下优化解决:
- 增加MongoDB连接池大小
- 实现提交队列缓冲
- 优化事务处理逻辑
- 添加限流机制
这个项目从技术选型到最终上线历时3个月,期间遇到了不少挑战,但也积累了宝贵的全栈开发经验。特别是在高并发场景下的性能优化和在线考试特有的防作弊设计方面,有很多实践心得可以分享给类似项目的开发者。
