以下是一个基于 Node.js + Vue 的官网前端和管理后台模板从0到1的完整搭建指南,包含代码示例和关键步骤:

一、技术栈选择

模块 技术方案 说明
前端框架 Vue 3 + Composition API 官网和管理台统一技术栈
UI组件库 Element Plus 管理台专用
状态管理 Pinia Vue官方推荐状态库
路由 Vue Router 支持动态路由和权限控制
HTTP库 Axios + 拦截器 统一请求管理
构建工具 Vite 极速启动 + 热更新
后端框架 Express/Koa 提供API接口
数据库 MongoDB/Mysql 按业务需求选择

二、项目结构设计

project/
├── client/                 # 前端代码
│   ├── public/             # 静态资源
│   ├── src/
│   │   ├── assets/         # 图片/字体等
│   │   ├── components/     # 公共组件
│   │   ├── layouts/        # 页面布局
│   │   │   ├── MainLayout.vue   # 官网布局
│   │   │   └── AdminLayout.vue  # 管理后台布局
│   │   ├── router/         # 路由配置
│   │   ├── store/          # 状态管理
│   │   ├── views/          # 页面组件
│   │   │   ├── front/      # 官网页面
│   │   │   └── admin/      # 管理后台页面
│   │   ├── utils/          # 工具函数
│   │   └── App.vue         # 根组件
│   └── vite.config.js      # Vite配置
│
├── server/                 # 后端代码
│   ├── config/             # 配置文件
│   ├── controllers/        # 控制器
│   ├── models/             # 数据库模型
│   ├── routes/             # API路由
│   ├── middlewares/        # 中间件
│   └── app.js              # 入口文件
│
├── .env                    # 环境变量
└── package.json            # 项目配置

三、核心代码实现

1. 前端路由配置 (src/router/index.js)
import { createRouter, createWebHistory } from 'vue-router'
import MainLayout from '@/layouts/MainLayout.vue'
import AdminLayout from '@/layouts/AdminLayout.vue'

// 官网路由
const frontRoutes = [
  { path: '/', name: 'Home', component: () => import('@/views/front/Home.vue') },
  { path: '/about', name: 'About', component: () => import('@/views/front/About.vue') },
  { path: '/contact', name: 'Contact', component: () => import('@/views/front/Contact.vue') }
]

// 管理后台路由(需鉴权)
const adminRoutes = [
  {
    path: '/admin',
    component: AdminLayout,
    meta: { requiresAuth: true },
    children: [
      { path: 'dashboard', name: 'Dashboard', component: () => import('@/views/admin/Dashboard.vue') },
      { path: 'users', name: 'UserList', component: () => import('@/views/admin/UserList.vue') },
      { path: 'settings', name: 'Settings', component: () => import('@/views/admin/Settings.vue') }
    ]
  },
  { path: '/admin/login', name: 'AdminLogin', component: () => import('@/views/admin/Login.vue') }
]

const router = createRouter({
  history: createWebHistory(),
  routes: [
    {
      path: '/',
      component: MainLayout,
      children: frontRoutes
    },
    ...adminRoutes,
    { path: '/:pathMatch(.*)*', redirect: '/' } // 404处理
  ]
})

// 路由守卫
router.beforeEach((to, from, next) => {
  const isAuthenticated = localStorage.getItem('token')
  
  if (to.matched.some(record => record.meta.requiresAuth) {
    if (!isAuthenticated) {
      next({ name: 'AdminLogin' })
    } else {
      next()
    }
  } else {
    next()
  }
})

export default router
2. 管理后台布局 (src/layouts/AdminLayout.vue)
<template>
  <div class="admin-layout">
    <el-container>
      <!-- 侧边栏 -->
      <el-aside width="200px">
        <div class="logo">管理后台</div>
        <el-menu router :default-active="$route.path">
          <el-menu-item index="/admin/dashboard">
            <el-icon><dashboard /></el-icon>
            <span>仪表盘</span>
          </el-menu-item>
          <el-menu-item index="/admin/users">
            <el-icon><user /></el-icon>
            <span>用户管理</span>
          </el-menu-item>
          <el-submenu index="3">
            <template #title>
              <el-icon><setting /></el-icon>
              <span>系统设置</span>
            </template>
            <el-menu-item index="/admin/settings/basic">基础设置</el-menu-item>
            <el-menu-item index="/admin/settings/security">安全设置</el-menu-item>
          </el-submenu>
        </el-menu>
      </el-aside>

      <!-- 主内容区 -->
      <el-container>
        <el-header>
          <div class="header-right">
            <el-dropdown>
              <span class="el-dropdown-link">
                <el-avatar :size="30" src="https://avatar.png" />
                Admin
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>个人中心</el-dropdown-item>
                  <el-dropdown-item @click="logout">退出登录</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </div>
        </el-header>
        
        <el-main>
          <router-view />
        </el-main>
      </el-container>
    </el-container>
  </div>
</template>

<script setup>
import { useRouter } from 'vue-router'
import { Dashboard, User, Setting } from '@element-plus/icons-vue'

const router = useRouter()

const logout = () => {
  localStorage.removeItem('token')
  router.push({ name: 'AdminLogin' })
}
</script>

<style scoped>
.admin-layout {
  height: 100vh;
}
.el-header {
  background: #fff;
  border-bottom: 1px solid #eee;
  display: flex;
  align-items: center;
  justify-content: flex-end;
}
.el-aside {
  background: #304156;
}
.logo {
  height: 60px;
  line-height: 60px;
  text-align: center;
  color: #fff;
  font-size: 18px;
  font-weight: bold;
}
</style>
3. 用户管理页面 (src/views/admin/UserList.vue)
<template>
  <div class="user-list">
    <el-card>
      <template #header>
        <div class="card-header">
          <span>用户管理</span>
          <el-button type="primary" @click="handleAdd">新增用户</el-button>
        </div>
      </template>

      <el-table :data="userList" v-loading="loading">
        <el-table-column prop="id" label="ID" width="80" />
        <el-table-column prop="username" label="用户名" />
        <el-table-column prop="email" label="邮箱" />
        <el-table-column prop="role" label="角色" />
        <el-table-column prop="createdAt" label="创建时间" />
        <el-table-column label="操作" width="180">
          <template #default="scope">
            <el-button size="small" @click="handleEdit(scope.row)">编辑</el-button>
            <el-button size="small" type="danger" @click="handleDelete(scope.row.id)">删除</el-button>
          </template>
        </el-table-column>
      </el-table>

      <el-pagination
        class="pagination"
        v-model:current-page="currentPage"
        :page-size="pageSize"
        :total="total"
        @current-change="fetchUsers"
        layout="prev, pager, next"
      />
    </el-card>
  </div>
</template>

<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import api from '@/api'

const userList = ref([])
const loading = ref(false)
const currentPage = ref(1)
const pageSize = ref(10)
const total = ref(0)

const fetchUsers = async () => {
  try {
    loading.value = true
    const res = await api.getUsers({
      page: currentPage.value,
      pageSize: pageSize.value
    })
    userList.value = res.data.list
    total.value = res.data.total
  } catch (error) {
    ElMessage.error('获取用户列表失败')
  } finally {
    loading.value = false
  }
}

const handleAdd = () => {
  // 打开添加用户对话框
}

const handleEdit = (user) => {
  // 打开编辑用户对话框
}

const handleDelete = (id) => {
  ElMessageBox.confirm('确定删除该用户?', '警告', {
    confirmButtonText: '确定',
    cancelButtonText: '取消',
    type: 'warning'
  }).then(async () => {
    await api.deleteUser(id)
    ElMessage.success('删除成功')
    fetchUsers()
  })
}

onMounted(() => {
  fetchUsers()
})
</script>

<style scoped>
.card-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
}
.pagination {
  margin-top: 20px;
  justify-content: flex-end;
}
</style>
4. API 封装 (src/api/index.js)
import axios from 'axios'
import { ElMessage } from 'element-plus'
import router from '@/router'

// 创建axios实例
const service = axios.create({
  baseURL: import.meta.env.VITE_API_BASE_URL,
  timeout: 10000
})

// 请求拦截器
service.interceptors.request.use(
  config => {
    const token = localStorage.getItem('token')
    if (token) {
      config.headers['Authorization'] = `Bearer ${token}`
    }
    return config
  },
  error => {
    return Promise.reject(error)
  }
)

// 响应拦截器
service.interceptors.response.use(
  response => {
    const res = response.data
    
    // 业务错误处理
    if (res.code !== 200) {
      ElMessage({
        message: res.message || 'Error',
        type: 'error',
        duration: 5 * 1000
      })
      
      // 未认证处理
      if (res.code === 401) {
        localStorage.removeItem('token')
        router.push({ name: 'AdminLogin' })
      }
      
      return Promise.reject(new Error(res.message || 'Error'))
    } else {
      return res
    }
  },
  error => {
    ElMessage({
      message: error.message,
      type: 'error',
      duration: 5 * 1000
    })
    return Promise.reject(error)
  }
)

// API方法封装
export default {
  // 用户相关
  login(data) {
    return service.post('/auth/login', data)
  },
  getUsers(params) {
    return service.get('/users', { params })
  },
  getUser(id) {
    return service.get(`/users/${id}`)
  },
  createUser(data) {
    return service.post('/users', data)
  },
  updateUser(id, data) {
    return service.put(`/users/${id}`, data)
  },
  deleteUser(id) {
    return service.delete(`/users/${id}`)
  },
  
  // 其他业务API...
}
5. 后端Express基础框架 (server/app.js)
require('dotenv').config()
const express = require('express')
const mongoose = require('mongoose')
const cors = require('cors')
const authRoutes = require('./routes/auth')
const userRoutes = require('./routes/users')

const app = express()
const PORT = process.env.PORT || 3000

// 数据库连接
mongoose.connect(process.env.MONGODB_URI, {
  useNewUrlParser: true,
  useUnifiedTopology: true
})
.then(() => console.log('MongoDB connected'))
.catch(err => console.error(err))

// 中间件
app.use(cors())
app.use(express.json())
app.use(express.urlencoded({ extended: true }))

// 路由
app.use('/api/auth', authRoutes)
app.use('/api/users', userRoutes)

// 错误处理
app.use((err, req, res, next) => {
  console.error(err.stack)
  res.status(500).json({ message: 'Internal Server Error' })
})

app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`)
})
6. 用户模型 (server/models/User.js)
const mongoose = require('mongoose')
const bcrypt = require('bcryptjs')

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    required: true,
    unique: true,
    trim: true
  },
  email: {
    type: String,
    required: true,
    unique: true,
    trim: true,
    lowercase: true
  },
  password: {
    type: String,
    required: true
  },
  role: {
    type: String,
    enum: ['admin', 'editor', 'viewer'],
    default: 'viewer'
  },
  createdAt: {
    type: Date,
    default: Date.now
  }
})

// 密码加密
userSchema.pre('save', async function(next) {
  if (!this.isModified('password')) return next()
  
  try {
    const salt = await bcrypt.genSalt(10)
    this.password = await bcrypt.hash(this.password, salt)
    next()
  } catch (error) {
    next(error)
  }
})

// 密码验证方法
userSchema.methods.comparePassword = async function(candidatePassword) {
  return bcrypt.compare(candidatePassword, this.password)
}

module.exports = mongoose.model('User', userSchema)

四、开发流程步骤

步骤1:初始化项目
# 创建项目目录
mkdir my-project && cd my-project

# 初始化前端项目
npm create vite@latest client -- --template vue
cd client
npm install axios pinia vue-router element-plus @element-plus/icons-vue

# 初始化后端项目
cd ..
mkdir server && cd server
npm init -y
npm install express mongoose bcryptjs cors dotenv jsonwebtoken
步骤2:配置环境变量 (.env)
# 前端
VITE_API_BASE_URL=http://localhost:3000/api

# 后端
PORT=3000
MONGODB_URI=mongodb://localhost:27017/myapp
JWT_SECRET=myapp_secret_key
步骤3:实现核心功能模块
  1. 官网前端

    • 首页:产品介绍、特色功能
    • 关于页面:公司介绍、团队信息
    • 联系页面:联系方式、反馈表单
  2. 管理后台

    • 登录认证:JWT实现
    • 仪表盘:数据概览
    • 用户管理:CRUD操作
    • 角色权限:RBAC控制
    • 系统设置:基础配置
步骤4:API接口开发
// server/routes/users.js
const express = require('express')
const router = express.Router()
const User = require('../models/User')
const authMiddleware = require('../middlewares/auth')

// 获取用户列表
router.get('/', authMiddleware('admin'), async (req, res) => {
  try {
    const { page = 1, pageSize = 10 } = req.query
    const skip = (page - 1) * pageSize
    
    const [users, total] = await Promise.all([
      User.find().skip(skip).limit(Number(pageSize)),
      User.countDocuments()
    ])
    
    res.json({
      code: 200,
      data: {
        list: users,
        total,
        page: Number(page),
        pageSize: Number(pageSize)
      }
    })
  } catch (error) {
    res.status(500).json({ code: 500, message: '服务器错误' })
  }
})

// 其他用户操作接口...

五、优化建议

1. 性能优化
// Vite配置优化 (vite.config.js)
export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          element: ['element-plus'],
          vendor: ['vue', 'vue-router', 'pinia']
        }
      }
    }
  },
  server: {
    proxy: {
      '/api': {
        target: 'http://localhost:3000',
        changeOrigin: true
      }
    }
  }
})
2. 安全加固
// 后端安全中间件 (server/middlewares/security.js)
const helmet = require('helmet')
const rateLimit = require('express-rate-limit')

// 设置安全头部
app.use(helmet())

// API请求限流
const apiLimiter = rateLimit({
  windowMs: 15 * 60 * 1000, // 15分钟
  max: 100, // 每个IP限制100次请求
  message: '请求过于频繁,请稍后再试'
})
app.use('/api/', apiLimiter)
3. 部署方案
# 前端构建
cd client
npm run build

# 部署到Nginx (示例配置)
server {
  listen 80;
  server_name example.com;
  
  location / {
    root /var/www/client/dist;
    try_files $uri $uri/ /index.html;
  }
  
  location /api {
    proxy_pass http://localhost:3000;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
  }
}

# PM2管理Node进程
pm2 start server/app.js --name "myapp-backend"

六、模板扩展方向

  1. 多主题支持:通过CSS变量实现主题切换
  2. 国际化:集成vue-i18n支持多语言
  3. 微前端架构:使用qiankun拆分模块
  4. 低代码平台:集成可视化表单/流程设计器
  5. 数据分析:集成ECharts数据可视化

通过以上方案,您可以从零开始搭建一个功能完善的前后台管理系统模板。开发过程中注意保持代码模块化,便于后续扩展和维护。

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐