1. 初始化企业级项目结构

初始化企业级项目结构 学习卡片

任务说明

本案例目标为:搭建一个包含前端、后端、共享模块的多层项目结构,并完成版本控制与依赖管理的初始化配置。最终交付一个可直接克隆并运行的最小可复现工程骨架,不包含业务逻辑代码。

项目结构设计

采用 pnpm workspace 管理 monorepo,目录划分如下:

  • apps/web:前端(Vite + Vue 3 + TypeScript)
  • apps/server:后端(Express + TypeScript)
  • packages/shared:共享类型与工具函数

版本控制使用 Git,依赖管理使用 pnpm(锁文件 pnpm-lock.yaml 保证依赖一致性)。共享模块通过 workspace 协议被前后端引用,实现类型与代码复用。

实现步骤

以下文件构成完整工程骨架,按路径创建即可。

1. 根目录配置

// package.json(根)
{
  "name": "enterprise-monorepo",
  "private": true,
  "scripts": {
    "dev:web": "pnpm --filter web dev",
    "dev:server": "pnpm --filter server dev",
    "build": "pnpm -r build",
    "typecheck": "pnpm -r typecheck"
  },
  "devDependencies": {
    "typescript": "^5.4.0"
  },
  "packageManager": "pnpm@9.0.0"
}
// pnpm-workspace.yaml
packages:
  - "apps/*"
  - "packages/*"
// .gitignore
node_modules/
dist/
*.log
.env

2. 共享模块 packages/shared

// packages/shared/package.json
{
  "name": "@company/shared",
  "version": "1.0.0",
  "main": "src/index.ts",
  "types": "src/index.ts",
  "scripts": {
    "typecheck": "tsc --noEmit"
  },
  "devDependencies": {
    "typescript": "^5.4.0"
  }
}
// packages/shared/src/index.ts
export interface ApiResponse<T> {
  code: number;
  data: T;
  message: string;
}

export function createSuccessResponse<T>(data: T): ApiResponse<T> {
  return { code: 0, data, message: "ok" };
}

export const API_VERSION = "v1";
// packages/shared/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "skipLibCheck": true
  },
  "include": ["src"]
}

3. 后端 apps/server

// apps/server/package.json
{
  "name": "server",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "tsx watch src/index.ts",
    "build": "tsc",
    "typecheck": "tsc --noEmit"
  },
  "dependencies": {
    "@company/shared": "workspace:*",
    "express": "^4.19.0"
  },
  "devDependencies": {
    "@types/express": "^4.17.21",
    "tsx": "^4.7.0",
    "typescript": "^5.4.0"
  }
}
// apps/server/src/index.ts
import express from "express";
import { createSuccessResponse, API_VERSION } from "@company/shared";

const app = express();
const port = 3000;

app.get(`/api/${API_VERSION}/health`, (_req, res) => {
  res.json(createSuccessResponse({ status: "healthy" }));
});

app.listen(port, () => {
  console.log(`Server listening on http://localhost:${port}`);
});
// apps/server/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "outDir": "dist",
    "rootDir": "src",
    "skipLibCheck": true
  },
  "include": ["src"]
}

4. 前端 apps/web

// apps/web/package.json
{
  "name": "web",
  "version": "1.0.0",
  "type": "module",
  "scripts": {
    "dev": "vite",
    "build": "vue-tsc --noEmit && vite build",
    "typecheck": "vue-tsc --noEmit"
  },
  "dependencies": {
    "@company/shared": "workspace:*",
    "vue": "^3.4.0"
  },
  "devDependencies": {
    "@vitejs/plugin-vue": "^5.0.0",
    "typescript": "^5.4.0",
    "vite": "^5.2.0",
    "vue-tsc": "^2.0.0"
  }
}
// apps/web/index.html
<!DOCTYPE html>
<html lang="zh-CN">
  <head>
    <meta charset="UTF-8" />
    <title>Web App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.ts"></script>
  </body>
</html>
// apps/web/src/main.ts
import { createApp } from "vue";
import App from "./App.vue";
import { API_VERSION } from "@company/shared";

console.log(`Frontend using API version: ${API_VERSION}`);

createApp(App).mount("#app");
// apps/web/src/App.vue
<template>
  <div>企业级前端应用已启动</div>
</template>

<script setup lang="ts">
import { createSuccessResponse } from "@company/shared";

const demo = createSuccessResponse({ message: "shared module works" });
console.log(demo);
</script>
// apps/web/tsconfig.json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "ESNext",
    "moduleResolution": "Bundler",
    "strict": true,
    "jsx": "preserve",
    "skipLibCheck": true
  },
  "include": ["src/**/*.ts", "src/**/*.vue"]
}
// apps/web/vite.config.ts
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";

export default defineConfig({
  plugins: [vue()],
  server: {
    port: 5173
  }
});

依赖安装与版本控制初始化

# 在项目根目录执行
git init
pnpm install

执行后,pnpm 会根据 workspace 配置自动链接 @company/shared 到前后端,并生成 pnpm-lock.yaml 锁文件。

验证方法与判定标准

  1. 类型检查:在根目录运行 pnpm typecheck,若所有包均无 TypeScript 错误,说明共享模块引用与类型配置正确。
  2. 后端启动:运行 pnpm dev:server,控制台输出 Server listening on http://localhost:3000,访问 http://localhost:3000/api/v1/health,预期返回 JSON:{"code":0,"data":{"status":"healthy"},"message":"ok"}。该输出由代码逻辑直接确定,非实测结果。
  3. 前端启动:运行 pnpm dev:web,浏览器打开 http://localhost:5173,页面显示“企业级前端应用已启动”,且浏览器控制台输出 Frontend using API version: v1 与 {code: 0, data: {message: 'shared module works'}, message: 'ok'},证明共享模块在前端生效。
  4. 版本控制:执行 git status,确认 node_modules 与 dist 未被跟踪,pnpm-lock.yaml 已被跟踪。
判定标准:上述 4 项验证全部通过,即视为本案例目标达成。
Monorepo 工程结构依赖关系 根目录 (pnpm workspace) apps/web Vue 3 + Vite + TS apps/server Express + TS packages/shared 类型 + 工具函数 workspace:* 协议依赖 @company/shared Git 版本控制:根目录 .gitignore 排除 node_modules/ 与 dist/ pnpm-lock.yaml 锁定全部依赖版本,保证团队一致性