简介:这是基于 Vue 3 与 Webpack(非 Vite)构建的若依后台管理前端项目源码包,适合希望快速搭建中后台界面、或想深入学习 Vue3 工程化实践的前端开发者。压缩包内共 2004 个文件,其中 js 文件 966 个、md 文档 975 个,另有 json、html、txt 等类型;js 覆盖业务逻辑与组件封装,md 提供说明文档,json 为项目配置,整体 57.1MB,目录结构清晰,便于按模块阅读。资源完整呈现了若依框架在 Vue3 中的典型用法,包括单文件组件、Webpack 构建配置、路由与状态管理等关键环节,并附带常用插件与构建脚本,读者既能对照源码理解 Composition API 的落地方式,也能依据现有配置快速改造出可用的管理后台。目前已有 675 人学习下载,适合具备一定 Vue 基础、想通过真实项目提升工程能力的开发者。
1. 若依vue3的webpack分支:为什么不用vite反而省事
如果你的搜索记录里同时出现 vue.js 和 webpack,那大概率你面对的是同一个困惑:官方都在推 vite,为什么还有人在用若依 vue3 的 webpack 版本。我的判断是——当团队里老成员居多、之前写过大量 vue2 时代的业务代码、或者内网环境连 npm 源都要走镜像时,webpack 版本反而落地更快。它没有 vite 那套 esbuild 原生依赖和浏览器兼容性要求,构建结果更像传统 SPA,部署时不用额外给服务器配原生运行环境。这版资源适合三类人:维护旧系统想平滑迁移到 vue3 的团队;内网开发环境不能随意升级 node 版本的人;对 vite 启动快没刚需、更看重配置透明可控的人。接下来我从环境、构建配置、二次开发、避坑和打包优化五个方向,把这套东西从头到尾拆一遍。
2. 环境准备与前后端启动:配置文件版本匹配是第一道坎
2.1 node、java、redis、mysql 的版本怎么选
这个后台管理框架的前端依赖 node 环境,后端依赖 java,中间还挂着 redis 和 mysql。最常见的问题不是代码跑不起来,而是版本细节导致启动一半直接崩掉。
前端这边,webpack 5 对 node 版本的要求是 12.13 以上,但如果你直接用 node 18 或更高版本,有概率遇到 OpenSSL 相关的报错。常见做法是装 node 16 LTS 版本,稳定性和 webpack 5 的兼容性最好。npm 用 6.14 或 8.x 都行,别用太新的 npm 9 去配合老 lockfile,那会在安装依赖时把整个 node_modules 重新排一遍,过程中容易触发无关紧要的警告。
后端要求 java 8 以上,我一般直接上 JDK 8,避免上了 JDK 11 之后某些依赖的反射代码出问题。redis 和 mysql 版本没太多讲究,redis 5+、mysql 5.7 或 8.0 都行。关键在配置文件里的连接串对不对。
2.2 克隆代码并安装前端依赖
拉代码下来之后,前端目录一般是ruoyi-ui。进入目录执行安装:
cd ruoyi-ui npm install --registry=https://registry.npmmirror.com这里我特意加了镜像源。如果你在公司内网,可能还要配置公司自己的 registry。装依赖的时候注意观察有没有 node-sass 编译失败的报错,那个会在后面避坑章节单独讲。
安装完成后启动开发服务器:
npm run dev默认端口是 80,如果被占用会看到类似Port 80 is already in use的提示,解决办法是去vue.config.js里把端口改掉。启动成功的标志是控制台出现App running at字样,这时候浏览器访问http://localhost就能看到登录页。
2.3 后端启动:改完三个文件再运行
后端是多模块的 maven 工程,启动前必须改的地方有三处。
第一处是ruoyi-admin/src/main/resources/application.yml,里面配置了端口、上下文路径和文件上传路径。默认端口是 8080,上下文路径为空,文件上传路径建议改成一个绝对路径,比如/home/ruoyi/upload,不然打 jar 包部署后上传的文件会落在临时目录里,重启就被清理。
第二处是application-druid.yml,这里填数据库的连接地址、账号密码。mysql 8.0 和 mysql 5.7 的驱动配置略有区别,如果用的是 mysql 8.0,要注意驱动类名是否带cj:
spring: datasource: driverClassName: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/ry-vue3?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8 username: root password: 你的密码第三处是 redis 的连接配置,一般在application.yml里,默认是 localhost:6379,密码为空。如果你的 redis 设置了密码,这里必须填上,否则后面验证码接口直接报错。
改完之后,在 IDE 里找到RuoYiApplication这个主类,直接运行。如果启动时报Failed to configure a DataSource,百分之九十九是刚才的数据库连接串写错了,或者 mysql 服务没起来。用命令行启动也一样,maven 打包后执行:
mvn clean package -Dmaven.test.skip=true java -jar ruoyi-admin/target/ruoyi-admin.jar后端的日志里出现Started RuoYiApplication才算真的启动成功,然后访问http://localhost:80,能看到登录页就说明前后端通了。第一次登录用的验证码需要 redis 支持,如果 redis 没启动,验证码图片能显示但永远提示验证码错误,这是新手最容易误判的一个点。
3. 前后端分离的联调:接口、代理与 token 的三层依赖
3.1 前端代理:vue.config.js 的 proxy 配置
开发环境下,前端请求后端接口走的是 webpack devServer 的代理。这个配置在vue.config.js里,核心代码长这样:
devServer: { host: '0.0.0.0', port: 80, proxy: { '/dev-api': { target: 'http://localhost:8080', changeOrigin: true, pathRewrite: { '^/dev-api': '' } } } }如果你的后端端口不是 8080,把 target 改掉就行。/dev-api这个前缀在前端请求里到处都是,比如封装 axios 的request.js里就写了baseURL: '/dev-api'。你要是改了前缀,这里必须同步改。pathRewrite的作用是请求发出前把/dev-api这个前缀去掉,因为后端接口路径里并没有这个前缀。
还有一点容易被忽略:changeOrigin: true是必须的。它会把请求头里的 Host 字段改成 target 的域名,后端如果做了域名白名单校验,不设置这个就会被拦。这个参数在很多老项目的代理配置里被漏掉,表现就是前端请求能发出去、后端能收到,但返回的 code 是 401 或者其他鉴权类错误。
3.2 登录接口的 token 机制
若依用的是 JWT,登录成功之后后端返回 token,前端存在 cookie 里。这个 cookie 的 key 在utils/auth.js里定义,默认是Admin-Token。
联调时最常见的坑是登录成功但马上又跳回登录页。通常是后端返回的 token 没有正确写入 cookie,或者 cookie 的 domain 不对。cookie 写在 localhost 下没问题,但如果你用 IP 地址访问前端,要把 cookie 的 domain 配置成 IP 才行。具体来说,修改utils/auth.js里的setToken方法,把 cookie 的 domain 参数指到你的 IP 上。
另外,登录页的验证码校验通过之后,后端返回的数据结构一般是:
{ "code": 200, "msg": "操作成功", "token": "eyJhbGciOiJIUzI1NiJ9.xxx" }注意 token 是放在根级别的字段里,不是放在data里。如果你自己封装登录接口,很容易把 token 的取值路径写错,导致登录后没有 token 可用。这个细节不仔细看后端代码根本发现不了。
3.3 接口鉴权与 401 的处理
前后端分离后,所有请求都要带着 token,否则会被后端的拦截器拦下来。你可以在浏览器的 Network 面板里看到,请求头里有一个Authorization: Bearer xxx的字段,那就是前端请求拦截器加上去的。
看 Network 如果发现没有这个请求头,检查一下request.js里的拦截器是不是被注释掉了。另外,如果后台返回 401,前端会清掉本地 cookie 然后跳转登录页,这是正常逻辑,别当成 bug 去改。不过有一个值得留意的点:如果用户长时间停留在页面,token 过期后任意一次操作都会强制跳登录页,而且不会提示具体原因。实际使用时可以在响应拦截器里加一个友好提示,我一般会这么处理:
service.interceptors.response.use( response => { const res = response.data if (res.code === 401) { MessageBox.confirm('登录状态已过期,请重新登录', '提示', { confirmButtonText: '重新登录', type: 'warning' }).then(() => { location.href = '/index' }) } return res }, error => { return Promise.reject(error) } )这段代码的作用是在 401 的时候弹窗提示,而不是直接跳转。好处是用户知道发生了什么,不是页面莫名其妙跳走。参数方面注意MessageBox.confirm引入自 element-plus,别漏了导入。
4. 二次开发实战:从建表到页面挂菜单的完整流程
4.1 用代码生成器生成前后端代码
若依最值钱的功能就是代码生成器。你只需要在数据库里建好表,然后在系统工具 -> 代码生成菜单里导入表,配置好字段、类型、查询方式,就能一键生成全套代码。
生成的代码包括前端和后端两部分,结构如下:
ruoyi-system/src/main/java/com/ruoyi/system/domain/SysXxx.java ruoyi-system/src/main/java/com/ruoyi/system/mapper/SysXxxMapper.java ruoyi-system/src/main/java/com/ruoyi/system/service/ISysXxxService.java ruoyi-system/src/main/java/com/ruoyi/system/controller/SysXxxController.java ruoyi-ui/src/api/system/xxx.js ruoyi-ui/src/views/system/xxx/index.vue ruoyi-ui/src/views/system/xxx/form.vue生成之后需要把文件放到对应目录,然后重启后端。这里有个细节:如果你在代码生成器里勾选了生成菜单选项,它还会生成一条 SQL,里面是菜单和权限的插入语句。这条 SQL 要手动执行,不是自动执行的。很多新手在这步漏掉,结果页面不显示,还以为代码放错位置了。
执行 SQL 的方式在命令行或者数据库客户端里都行:
-- 这条语句由代码生成器自动产出 INSERT INTO sys_menu (menu_name, parent_id, order_num, path, component, is_frame, is_cache, menu_type, visible, status, perms, icon, create_by, create_time, update_by, update_time, remark) VALUES ('客户管理', 1, 1, 'customer', 'system/customer/index', 1, 1, 'C', '0', '0', 'system:customer:list', '#', 'admin', sysdate(), '', NULL, '客户管理菜单');4.2 菜单挂载与路由注册
页面能不能在侧边栏里看到,取决于sys_menu表里的数据,以及前端路由是否注册了对应组件。若依的路由是动态路由,登录后根据当前用户拥有的菜单权限动态生成,不是像普通 vue 项目那样在 router 里静态写死的。
如果你手动加了一个 vue 文件放在views/system/xxx/index.vue,但菜单管理里没有对应记录,侧边栏就不会出现这个页面。反过来,菜单管理里配了记录,但组件路径写错了,点击菜单会白屏,控制台报Failed to resolve component。
所以排查顺序应该固定为:先确认数据库菜单记录存在 -> 再确认component字段的路径和实际 vue 文件位置一致 -> 最后刷新页面看侧边栏。
4.3 按钮级别的权限控制
若依的按钮权限指令是v-hasPermi,用法很简单:
<el-button v-hasPermi="['system:user:add']" type="primary">新增用户</el-button>这里的字符串对应后端接口的权限标识。如果你把权限字符写错了,按钮会直接消失,注意是消失而不是报错。排查的时候先看一下菜单管理里该按钮的权限字符是否和后端@PreAuthorize注解里的字符串一致。我遇到过几次都是复制粘贴的时候多打了个空格,这种问题不仔细看根本发现不了。也可以说若依这套权限体系本身并不复杂,但它的容错性很差,一个字符对不上就静默失败。
5. 避坑指南:webpack 版若依最常见的六个事故现场
5.1 编译时内存溢出:JavaScript heap out of memory
现象:编译到一半报错,控制台提示JavaScript heap out of memory,在 Windows 上尤其常见。
原因:webpack 打包时默认 node 内存上限约 1.5GB,项目一大就容易爆。vue3 版本因为用了更多运行时依赖,比 vue2 版本更容易触发。
解决:在package.json的 build 脚本里加参数:
"scripts": { "build:prod": "node --max_old_space_size=4096 build/index.js" }我一般直接设成 4096,够用也不会显得太夸张。如果项目里还跑了 eslint 和 terser,建议把 dev 脚本也加上同样的参数,避免本地编译到一半就崩。
5.2 node-sass 编译失败
现象:npm install的时候 node-sass 下载二进制文件失败,报错信息里有node-sass字样,后面跟着一串 download 失败或者 checksum mismatch。
原因:node 版本和 node-sass 版本对不上,或者网络问题导致二进制下载不下来。node-sass 是每次安装最容易翻车的依赖。
解决:换用 sass 的 dart-sass 实现,也就是sass包。改法很粗暴,删掉node_modules和package-lock.json,然后在package.json里把sass-loader的 sass 实现替换掉。最彻底的办法是给.npmrc配好镜像源:
sass_binary_site=https://npmmirror.com/mirrors/node-sass/配完之后重新执行npm install。这里值得多说一句:webpack 版若依几乎必然带 sass 相关的依赖,只要内网安装,这步大概率踩坑,提前配好镜像能省大量时间。
5.3 热更新失效
现象:改了代码浏览器不更新,或者必须手动刷新才能看到效果,再严重一点是保存后整个页面崩溃,只有删掉node_modules/.cache才能恢复。
原因:webpack 的文件监听机制出问题,尤其是在网络驱动器、虚拟机和 Docker 挂载目录上开发时最容易触发。
解决:把项目挪到本地磁盘,关闭 Windows Defender 对项目目录的实时扫描,或者直接重启 dev server。如果是虚拟机里跑项目,把项目放在共享文件夹里是百分百会出问题的,这是 Webpack 官方都承认的已知限制。
另外还有一个比较隐蔽的原因:项目里有多个vue.config.js文件同时生效,比如根目录一个、子目录一个,webpack 会去 merge,但 merge 出的 watch 配置可能不是你预期的。我遇到过一次,是构建脚本里引入了另一个配置文件导致 watch 开关被覆盖成 false。
5.4 打包后白屏
现象:npm run build:prod之后把 dist 部署到服务器,访问只有白屏,控制台是资源 404。
原因:webpack 默认打包出的资源路径是绝对路径/js/chunk-xxx.js,部署在子目录下就会 404。常见做法是把资源路径改成相对路径。
解决:修改vue.config.js里的publicPath:
module.exports = { publicPath: './' }注意改完这个之后,路由的 history 模式也可能会受影响。如果用的是createWebHistory,刷新二级页面会出现 404,需要后端配置 fallback,或者切换成createWebHashHistory。这属于部署方案的取舍,但很多人在白屏修复之后又踩进 history 的坑,这里提前说清楚。
5.5 登录成功但页面空白,控制台报 Cannot read properties of undefined
现象:登录成功,地址栏跳转了,但页面空白,控制台报Cannot read properties of undefined,有时候报错指向store/modules/user.js。
原因:路由懒加载的 chunk 加载失败,或者用户信息为空时渲染了不存在的属性。若依在动态生成路由时会依赖用户权限数据,如果用户表里的角色或菜单数据被清理过,登录后拿到的权限集合为空,渲染侧边栏时就会出错。
解决:检查store/modules/user.js里的getInfo方法,确认后端返回的user和roles字段都有正常值。再查一下数据库里sys_user_role和sys_role_menu的关系数据是否完整。
5.6 验证码接口报错:Could not get a resource from the pool
现象:登录页验证码能显示,但提交时提示验证码错误,后端日志报Could not get a resource from the pool。
原因:redis 连接池耗尽,大概率是 redis 服务没开,或者后端配置的 redis 密码不对。因为验证码的保存和校验走的是 redis,不是数据库。数据库正常不等于 redis 正常。
解决:确认 redis 服务进程存在,用命令行连一下:
redis-cli ping返回PONG才说明 redis 没问题。如果 redis 设置了密码,确保application.yml里的配置一致。
6. 一个实用技巧:用 webpack-bundle-analyzer 看谁在拖体积
webpack 版若依打包出来大概有十几 MB,gzip 之后也有 2MB 以上。如果你觉得首屏加载太慢,第一步不是上 CDN,而是先看看包里到底是什么在占地方。
安装分析器:
npm install webpack-bundle-analyzer --save-dev在vue.config.js里加一个插件配置:
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer') module.exports = { configureWebpack: { plugins: process.env.NODE_ENV === 'production' ? [new BundleAnalyzerPlugin()] : [] } }跑一次npm run build:prod,浏览器会自动打开一个图形化报告,每个依赖占多大体积一目了然。看报告的时候,先盯三个区域:
第一看vendor里是不是把没用的组件库整个打进去了,比如引入了element-plus但只用了几张表格组件,体积却占了快 800KB。第二看echarts是不是全量打包,这是另一个体积大户。如果项目里只用柱状图和折线图,完全没必要把整个 echarts 打进去。第三看有没有重复打包的依赖,比如moment和dayjs同时存在,或者多个 lodash 副本。
常见做法是到main.js里把全量引入改成按需引入:
// 改造前:全量引入 // import ElementPlus from 'element-plus' // import 'element-plus/dist/index.css' // app.use(ElementPlus) // 改造后:按需引入 import { ElButton, ElTable, ElForm } from 'element-plus' const components = [ElButton, ElTable, ElForm] components.forEach(component => app.use(component))注意按需引入之后,样式也要同步调整。element-plus 的按需样式导入如果没配好,页面会变成无样式状态,这是很多人改完按需引入后遇到的新问题。
echarts 也建议从全量改成按需:
import * as echarts from 'echarts/core' import { BarChart, LineChart } from 'echarts/charts' import { GridComponent, TooltipComponent, LegendComponent } from 'echarts/components' import { CanvasRenderer } from 'echarts/renderers' echarts.use([BarChart, LineChart, GridComponent, TooltipComponent, LegendComponent, CanvasRenderer])这样改完,echarts 的体积能砍掉一半以上。分析器报告里最大的一块依赖如果从 1.2MB 降到 400KB,首屏加载时间的变化是肉眼可见的。
另外还有一个不太被人注意但很有效的操作:把vue、vue-router这类核心库用 externals 配置拆出去,改成 CDN 引入。具体来说,在vue.config.js里加:
module.exports = { configureWebpack: { externals: { vue: 'Vue', 'vue-router': 'VueRouter' } } }然后在index.html里用 script 标签引入 CDN 地址。这样打包产物里不再包含这些框架代码,体积瞬间少一大块。缺点是部署环境必须能访问到你选的 CDN 地址,内网部署时得把 CDN 文件下载到本地服务器上。从那以后我每次给项目上线前,都会强制自己跑一遍 analyzer,不为别的,就为了提前知道自己可能被哪个资源坑。这工具已经陪我做了十来个项目,希望帮到你。
本文还有配套的精品资源,点击获取