下面我将为你生成一个 完整的 Express + Vue3 一体化项目模板,采用 pnpm 作为包管理器,支持:
✅ 开发模式:Vite 热更新 + Express API + 请求日志
✅ 代理功能:Vite 代理 API 到 Express
✅ 构建模式:打包 Vue3,由 Express 托管静态文件
✅ 请求日志:Express 记录所有 API 和页面访问
✅ 使用 pnpm 管理依赖,高效省空间
📁 项目结构
fullstack-vue-express/
├── backend/
│ ├── server.mjs # Express 服务器(ESM)
│ ├── routes/ # API 路由
│ │ └── api.js
│ └── package.json
├── frontend/
│ ├── src/
│ │ ├── App.vue
│ │ └── main.js
│ ├── index.html
│ ├── vite.config.js # Vite 配置(含代理)
│ └── package.json
├── dist/ # 构建后前端文件(git 忽略)
├── pnpm-workspace.yaml # 启用 pnpm workspace
├── .npmrc # 指定包管理器
└── package.json # 根目录脚本
🛠️ 第一步:初始化项目
# 创建项目根目录
mkdir fullstack-vue-express
cd fullstack-vue-express
# 初始化 pnpm workspace
echo "packages:
- 'backend'
- 'frontend'" > pnpm-workspace.yaml
# 设置包管理器
echo "package-manager=pnpm@9.0.0" > .npmrc
# 根目录 package.json(统一脚本)
cat > package.json <<EOF
{
"name": "fullstack-vue-express",
"private": true,
"scripts": {
"dev": "pnpm --filter backend start & pnpm --filter frontend dev",
"build": "pnpm --filter frontend build && pnpm --filter backend build",
"preview": "node backend/server.mjs"
}
}
EOF
🎯 第二步:创建前端(Vue3 + Vite)
mkdir frontend
cd frontend
# 初始化 Vue3 项目(Vite + Vue)
pnpm create vite@latest . -- --template vue
# 安装依赖
pnpm install
# 修改 vite.config.js 支持代理
cat > vite.config.js <<EOF
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
host: 'localhost',
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
configure: (proxy, options) => {
proxy.on('proxyReq', (proxyReq, req, res) => {
console.log('[Vite Proxy] → API:', req.method, req.url)
})
}
}
}
}
})
EOF
# 修改 App.vue 简化示例
cat > src/App.vue <<'EOF'
<script setup>
import { ref, onMounted } from 'vue'
const message = ref('')
onMounted(async () => {
const res = await fetch('/api/hello')
const data = await res.json()
message.value = data.message
})
</script>
<template>
<div class="container">
<h1>🚀 Express + Vue3 一体化项目</h1>
<p>API 返回: <strong>{{ message }}</strong></p>
</div>
</template>
<style>
.container {
text-align: center;
margin-top: 100px;
font-size: 1.5em;
color: #2c3e50;
}
</style>
EOF
🖥️ 第三步:创建后端(Express + ESM)
cd ..
mkdir backend
cd backend
# 初始化
pnpm init -y
# 安装依赖
pnpm add express cors
pnpm add -D http-proxy-middleware
# 创建 server.mjs
cat > server.mjs <<'EOF'
import express from 'express'
import cors from 'cors'
import { createProxyMiddleware } from 'http-proxy-middleware'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
const app = express()
const PORT = 3000
// __dirname 兼容 ESM
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
// 日志中间件
app.use((req, res, next) => {
console.log(
'[Request]',
new Date().toISOString().split('T')[1].split('.')[0],
req.method.padEnd(4),
req.path,
'←',
req.ip.replace('::1', 'localhost')
)
next()
})
// API 路由
app.use('/api', (req, res, next) => {
console.log(`[API] ${req.method} ${req.path} 来自 ${req.ip}`)
next()
}, cors())
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Express! 🎉' })
})
// 开发模式:代理静态资源到 Vite(用于 dev)
if (process.env.NODE_ENV !== 'production') {
app.use(
createProxyMiddleware({
target: 'http://localhost:5173',
changeOrigin: true,
bypass: (req) => {
// API 请求不代理
if (req.path.startsWith('/api')) return req
},
onProxyReq: (proxyReq, req, res) => {
console.log('[Proxy] → Frontend:', req.method, req.path)
}
})
)
} else {
// 生产模式:托管构建后的静态文件
app.use(express.static(join(__dirname, '../dist')))
app.get('*', (req, res) => {
res.sendFile(join(__dirname, '../dist', 'index.html'))
})
}
app.listen(PORT, () => {
console.log(`\n🚀 Express 服务器运行在 http://localhost:${PORT}`)
console.log(` 开发模式: 代理请求到 Vite`)
console.log(` 生产模式: 托管 dist/ 静态文件\n`)
})
EOF
# 创建 package.json 脚本
cat > package.json <<'EOF'
{
"name": "backend",
"type": "module",
"scripts": {
"start": "node server.mjs",
"build": "echo 'Backend build placeholder'"
}
}
EOF
🚀 第四步:启动项目
1. 启动开发模式(热更新)
# 在项目根目录运行
pnpm dev
会同时启动:
- Vite 前端:
http://localhost:5173 - Express 后端:
http://localhost:3000
🔍 访问
http://localhost:3000,所有请求都会经过 Express,你会看到完整日志!
2. 构建生产版本
pnpm build
会:
- 构建 Vue3 到
dist/ - 准备 Express 托管
3. 预览生产版本
pnpm preview
访问 http://localhost:3000,Express 直接 serve 静态文件。
📊 日志示例(开发模式)
[Request] 14:30:21 GET /api/hello ← ::1
[API] GET /api/hello 来自 ::1
[Request] 14:30:22 GET / ← ::1
[Proxy] → Frontend: GET /
[Vite Proxy] → API: GET /api/hello
✅ 功能总结
| 功能 | 是否支持 |
|---|---|
使用 pnpm | ✅ |
| Vue3 热更新 | ✅ |
| Express API | ✅ |
| 请求日志 | ✅ |
| Vite 代理 API | ✅ |
| 生产构建 + 托管 | ✅ |
| 一体化启动脚本 | ✅ pnpm dev |
📦 下一步建议
- ✅ 将项目推送到 GitHub
- ✅ 添加
.gitignore
node_modules/
dist/
.env
- ✅ 添加
README.md说明
🎁 附加:一键复制完整命令(Linux/Mac)
# 全自动创建项目(复制粘贴即可)
mkdir fullstack-vue-express && cd fullstack-vue-express
echo "packages:
- 'backend'
- 'frontend'" > pnpm-workspace.yaml
echo "package-manager=pnpm@9.0.0" > .npmrc
cat > package.json <<EOF
{
"name": "fullstack-vue-express",
"private": true,
"scripts": {
"dev": "pnpm --filter backend start & pnpm --filter frontend dev",
"build": "pnpm --filter frontend build && pnpm --filter backend build",
"preview": "node backend/server.mjs"
}
}
EOF
mkdir frontend && cd frontend
pnpm create vite@latest . -- --template vue
pnpm install
cat > vite.config.js <<'EOF'
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
server: {
port: 5173,
host: 'localhost',
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
configure: (proxy, options) => {
proxy.on('proxyReq', (proxyReq, req, res) => {
console.log('[Vite Proxy] → API:', req.method, req.url)
})
}
}
}
}
})
EOF
cat > src/App.vue <<'EOF'
<script setup>
import { ref, onMounted } from 'vue'
const message = ref('')
onMounted(async () => {
const res = await fetch('/api/hello')
const data = await res.json()
message.value = data.message
})
</script>
<template>
<div class="container">
<h1>🚀 Express + Vue3 一体化项目</h1>
<p>API 返回: <strong>{{ message }}</strong></p>
</div>
</template>
<style>
.container {
text-align: center;
margin-top: 100px;
font-size: 1.5em;
color: #2c3e50;
}
</style>
EOF
cd ..
mkdir backend && cd backend
pnpm init -y
pnpm add express cors
pnpm add -D http-proxy-middleware
cat > server.mjs <<'EOF'
import express from 'express'
import cors from 'cors'
import { createProxyMiddleware } from 'http-proxy-middleware'
import { fileURLToPath } from 'url'
import { dirname, join } from 'path'
const app = express()
const PORT = 3000
const __filename = fileURLToPath(import.meta.url)
const __dirname = dirname(__filename)
app.use((req, res, next) => {
console.log('[Request]', new Date().toISOString().split('T')[1].split('.')[0], req.method.padEnd(4), req.path, '←', req.ip.replace('::1', 'localhost'))
next()
})
app.use('/api', (req, res, next) => {
console.log(\`[API] \${req.method} \${req.path} 来自 \${req.ip}\`)
next()
}, cors())
app.get('/api/hello', (req, res) => {
res.json({ message: 'Hello from Express! 🎉' })
})
if (process.env.NODE_ENV !== 'production') {
app.use(createProxyMiddleware({
target: 'http://localhost:5173',
changeOrigin: true,
bypass: (req) => req.path.startsWith('/api') ? req : null,
onProxyReq: (proxyReq, req, res) => {
console.log('[Proxy] → Frontend:', req.method, req.path)
}
}))
} else {
app.use(express.static(join(__dirname, '../dist')))
app.get('*', (req, res) => {
res.sendFile(join(__dirname, '../dist', 'index.html'))
})
}
app.listen(PORT, () => {
console.log(`\\n🚀 Express 服务器运行在 http://localhost:\${PORT}`)
console.log(' 开发模式: 代理请求到 Vite')
console.log(' 生产模式: 托管 dist/ 静态文件\\n')
})
EOF
cat > package.json <<'EOF'
{
"name": "backend",
"type": "module",
"scripts": {
"start": "node server.mjs",
"build": "echo 'Backend build placeholder'"
}
}
EOF
echo "✅ 项目创建完成!运行 pnpm dev 启动开发服务器"
🎉 恭喜!你现在拥有了一个 现代化、一体化、日志齐全、生产可用的 Express + Vue3 项目模板,可以直接用于学习、开发或部署!