1. 项目概述:全栈Web开发实战全景
十年前我刚入行时,完成一个完整的Web应用需要前后端团队反复对接,部署流程更是需要专门的运维人员。如今全栈开发已成为行业标配,但很多初学者在从技术学习到项目落地的过程中仍会遇到各种断层。这个实战项目将带你体验从架构设计到上线部署的完整闭环,所有代码都经过生产环境验证且可复用。
这个项目特别适合以下人群:
- 掌握基础编程但缺乏完整项目经验的自学者
- 需要快速交付全栈产品的创业团队
- 希望统一技术栈的中小型企业技术负责人
- 准备面试全栈岗位的求职者
我们将采用主流技术组合:Vue3+TypeScript前端配合Nest.js后端,使用Docker容器化部署。这套组合在开发效率、性能表现和可维护性之间取得了很好的平衡,也是目前招聘市场上最受欢迎的技术栈之一。
2. 架构设计核心思路
2.1 分层架构设计
现代Web应用典型的分层架构如下:
code复制表示层(View) → 业务逻辑层(Service) → 数据访问层(DAO)
↓
基础设施层(DB/Cache/MQ等)
在实际项目中,我推荐采用改良版的分层架构:
- 表现层:处理HTTP请求/响应,包含:
- 路由控制
- 参数校验
- 响应格式化
- 应用层:核心业务逻辑,包含:
- 领域服务
- DTO转换
- 事务管理
- 基础设施层:技术细节实现,包含:
- 数据库操作
- 缓存处理
- 第三方服务集成
提示:分层架构的关键是保持单向依赖,严禁下层调用上层,这是很多项目后期难以维护的根本原因。
2.2 数据库设计实践
以用户管理系统为例,分享几个实际设计技巧:
typescript复制// 用户实体示例
@Entity()
export class User {
@PrimaryGeneratedColumn()
id: number;
@Column({ length: 50, unique: true })
username: string;
@Column({ length: 100, select: false }) // 查询时默认不返回密码
password: string;
@CreateDateColumn()
createdAt: Date;
@UpdateDateColumn()
updatedAt: Date;
@Column({ default: true })
isActive: boolean;
}
设计要点:
- 所有表必须包含创建/更新时间戳
- 敏感字段需要设置select: false
- 布尔类型用is前缀更清晰
- 字符串字段必须显式指定长度
2.3 接口设计规范
RESTful API设计建议:
- 资源使用复数形式:/users 而非 /user
- 状态码严格匹配语义:
- 200 OK - 成功请求
- 201 Created - 创建成功
- 400 Bad Request - 参数错误
- 401 Unauthorized - 未认证
- 403 Forbidden - 无权限
- 404 Not Found - 资源不存在
- 分页响应格式:
json复制{
"data": [],
"pagination": {
"page": 1,
"pageSize": 10,
"total": 100
}
}
3. 前后端开发实战
3.1 前端工程化配置
现代前端项目推荐配置:
bash复制# 创建Vue3项目
npm init vue@latest my-web-app
# 必要依赖
npm install -D typescript vite-plugin-checker unplugin-auto-import
vite.config.ts关键配置:
typescript复制export default defineConfig({
plugins: [
vue(),
AutoImport({ // 自动导入常用API
imports: ['vue', 'vue-router'],
dts: 'src/auto-imports.d.ts'
}),
checker({ // 类型检查
typescript: true,
vueTsc: true
})
]
})
3.2 后端核心模块实现
用户认证模块示例:
typescript复制// auth.module.ts
@Module({
imports: [
JwtModule.register({
secret: process.env.JWT_SECRET,
signOptions: { expiresIn: '7d' }
}),
UsersModule
],
providers: [AuthService],
controllers: [AuthController]
})
export class AuthModule {}
// auth.service.ts
@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private usersService: UsersService
) {}
async validateUser(username: string, pass: string) {
const user = await this.usersService.findOne(username);
if (user && compareSync(pass, user.password)) {
const { password, ...result } = user;
return result;
}
return null;
}
login(user: User) {
const payload = { username: user.username, sub: user.id };
return {
access_token: this.jwtService.sign(payload)
};
}
}
3.3 前后端联调技巧
调试工具推荐组合:
- Postman - API调试
- Wireshark - 网络包分析
- Chrome DevTools - 前端调试
常见跨域解决方案:
typescript复制// Nest.js CORS配置
app.enableCors({
origin: process.env.FRONTEND_URL,
methods: 'GET,HEAD,PUT,PATCH,POST,DELETE',
credentials: true
});
4. 部署上线全流程
4.1 容器化配置
Dockerfile最佳实践:
dockerfile复制# 前端Dockerfile
FROM node:18-alpine as builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /usr/share/nginx/html
COPY nginx.conf /etc/nginx/conf.d/default.conf
EXPOSE 80
dockerfile复制# 后端Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm ci --only=production
COPY . .
RUN npm run build
EXPOSE 3000
CMD ["node", "dist/main.js"]
4.2 持续集成配置
GitHub Actions示例:
yaml复制name: CI/CD Pipeline
on:
push:
branches: [ main ]
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Build frontend
run: |
cd frontend
npm ci
npm run build
docker build -t frontend-image .
- name: Build backend
run: |
cd backend
npm ci
npm run build
docker build -t backend-image .
- name: Login to Docker Hub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKER_HUB_USERNAME }}
password: ${{ secrets.DOCKER_HUB_TOKEN }}
- name: Push images
run: |
docker tag frontend-image username/web-app-frontend:latest
docker push username/web-app-frontend:latest
docker tag backend-image username/web-app-backend:latest
docker push username/web-app-backend:latest
4.3 生产环境配置
Nginx关键配置:
nginx复制server {
listen 80;
server_name example.com;
location / {
root /usr/share/nginx/html;
try_files $uri $uri/ /index.html;
}
location /api {
proxy_pass http://backend:3000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
}
}
5. 常见问题排查指南
5.1 部署问题排查
容器网络问题诊断步骤:
docker ps检查容器状态docker logs <container_id>查看日志docker exec -it <container_id> sh进入容器调试docker network inspect bridge检查网络配置
5.2 性能优化建议
数据库查询优化技巧:
typescript复制// 错误示例 - N+1查询问题
const users = await userRepository.find();
const results = users.map(user => {
const profile = await profileRepository.findOne({ userId: user.id });
return { ...user, profile };
});
// 正确做法 - 使用关联查询
const users = await userRepository.find({
relations: ['profile']
});
5.3 安全防护措施
必备安全中间件:
typescript复制// main.ts 安全配置
app.use(helmet()); // 安全头设置
app.use(rateLimit({ // 限流
windowMs: 15 * 60 * 1000,
max: 100
}));
app.use(csurf()); // CSRF防护
6. 可复用代码架构解析
6.1 通用异常处理
全局异常过滤器:
typescript复制@Catch()
export class AllExceptionsFilter implements ExceptionFilter {
catch(exception: unknown, host: ArgumentsHost) {
const ctx = host.switchToHttp();
const response = ctx.getResponse<Response>();
const request = ctx.getRequest<Request>();
let status = HttpStatus.INTERNAL_SERVER_ERROR;
let message = 'Internal server error';
if (exception instanceof HttpException) {
status = exception.getStatus();
message = exception.message;
} else if (exception instanceof Error) {
message = exception.message;
}
response.status(status).json({
statusCode: status,
timestamp: new Date().toISOString(),
path: request.url,
message
});
}
}
6.2 通用响应封装
统一响应格式:
typescript复制// response.interceptor.ts
@Injectable()
export class ResponseInterceptor implements NestInterceptor {
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
return next.handle().pipe(
map(data => ({
code: 200,
message: 'success',
data,
timestamp: new Date().toISOString()
}))
);
}
}
6.3 通用工具函数
常用工具类示例:
typescript复制// utils.ts
export function paginate<T>(
items: T[],
page: number,
pageSize: number
): PaginatedResult<T> {
const start = (page - 1) * pageSize;
const end = start + pageSize;
return {
data: items.slice(start, end),
pagination: {
page,
pageSize,
total: items.length
}
};
}
在实际项目开发中,我特别建议建立自己的代码片段库。经过多个项目的积累,我现在拥有超过200个经过验证的代码片段,可以快速组合出新的业务系统。比如用户管理模块,通过组合认证、授权、CRUD等片段,1天内就能搭建出生产可用的模块。
