资讯动态

若依App版项目结构全解析:从目录到二次开发

发布时间:2026/10/18 7:15:09 来源:尧图企业网站定制
拿到若依(RuoYi)的App版工程很多人第一反应是这不就是RuoYi-Vue的移动端换壳吗目录结构应该差不多吧。真把工程克隆下来一打开往往又有点懵——pages.json、manifest.json、uni_modules、条件编译再加上一堆以#ifdef开头的注释整个结构和后台管理端完全是两套思路。这篇文章就围绕RuoYiApp版的项目结构做一次完整的拆解把每个目录的职责、页面之间的跳转逻辑、token在整个应用里的流动路径以及二次开发时从哪里下手讲透。适合刚入手若依App版准备改业务的人也适合从后台管理转移动端开发、想快速理解这套工程的人。1. RuoYiApp版到底是什么它在若依技术栈里的真实位置1.1 若依家族的版本划分若依不是一个单一项目而是一整套基于Spring Boot的快速开发解决方案。常见的有这么几条线单体版RuoYi前后端分离版RuoYi-Vue微服务版RuoYi-Cloud以及移动端方向上的RuoYiApp版。这几个版本的后端逻辑大体同源区别主要在于前端形态和部署方式。RuoYiApp版并不是一套全新的后台系统它的真正身份是一个移动端前端工程。后端还是复用若依那套Spring Boot接口只是把页面载体从浏览器里的vue-element-admin换成了uni-app。换句话说RuoYiApp版解决的是若依系统怎么长出移动端的问题。一套代码编译出来可以跑成Android App、iOS App、H5网页也能发布到各种小程序平台。这个定位非常关键因为它决定了你看项目结构时的基本心态不用纠结后端的UserController、RoleService这些类在哪儿你手里这个工程里根本没有Java代码。你要处理的全部内容都是View层、状态层和网络请求层。1.2 技术栈的本质uni-app Vue3 ViteRuoYiApp版依赖的移动端底座是uni-app。这个框架的核心理念是一套代码、多端发布写法上以Vue为中心页面、组件、路由、状态管理都沿用Vue的思维但额外引入了一套生命周期和内置API。近几年的RuoYiApp版本普遍基于Vue3和Vite构建目录风格和Vue3单页应用很像但又有自己的调整。比如路由不再是vue-router那套而是交给pages.json统一声明全局生命周期也不写在router里而是放在App.vue的onLaunch、onShow中状态管理有的版本用Vuex新版本则逐步转到了Pinia。从后台管理端转过来的读者最容易踩的第一个坑就是把vue-router的路由思维带进来。在RuoYiApp版里根本没有router目录也没有路由守卫文件。页面跳转靠的是uni.navigateTo这类API页面注册靠pages.json。这个差异是后续读代码时的一道坎理解了之后整个项目结构就顺了。2. 顶层那几个文件别跳过启动入口和全局配置的线是怎么串起来的2.1 顶层目录文件清单打开RuoYiApp版工程先不急着进pages顶层这几个文件反而是整个应用的起点RuoYiApp ├── App.vue ├── main.js ├── manifest.json ├── pages.json ├── uni.scss └── pages/ # 业务页面这几个文件数量不多但每一个都在干一个不可替代的活。很多人觉得App.vue无非是模板根组件改两下就算完事实际线上排查问题的时候登录失效、启动白屏、页面跳错最后往往都能追回到这几个文件里。2.2 App.vue整个移动端的应用级生命周期在uni-app里App.vue承载的是应用级逻辑对应后台管理端main.js里那套全局初始化的活。常见的做法是在onLaunch里做三件事读取本地缓存的登录状态、拉取系统配置、判断要跳转到哪个页面。这里有个天然的顺序问题onLaunch触发时页面还没渲染出来所以这里适合做需要提前完成的初始化如果业务配置要在页面里用那就得等异步回调回来后再更新store。很多启动阶段的偶发问题本质都是初始化过程和页面渲染并行执行导致的竞态。实际开发中我会把登录状态检查和用户信息拉取拆开登录状态检查同步读缓存用户信息拉取异步执行避免页面一进来数据为空。2.3 main.js与manifest.json、pages.json的分工main.js负责创建Vue实例并挂载全局依赖以Vue3版本为例大致是import { createSSRApp } from vue import App from ./App.vue import * as Pinia from pinia export function createApp() { const app createSSRApp(App) app.use(Pinia.createPinia()) return { app, Pinia } }这里不需要手动注册路由表也不需要把每个页面import进来页面注册统一在pages.json里完成。pages.json实际扮演的是路由表页面配置导航栏配置的合并角色每个页面要在这里声明path和styletabBar页面同样在这里指定。第一次在某个页面里修改了导航栏标题却没生效多半就是只改了vue文件里的页面数据忘了同步pages.json里的对应配置。manifest.json则负责工程级和应用市场级配置应用名称、版本号、各平台appid、图标、启动图、SDK配置等。代码逻辑整体可以迁移但打包发版时manifest.json里的平台标识经常被忽略导致本地跑得好好的真机扫码一打开就出现白屏或API不可用排查半天发现是某个平台模块没勾选。3. pages目录移动端页面是怎么按业务模块组织起来的3.1 业务页面的典型分类方式RuoYiApp版里页面不是平铺的而是按业务域分了目录。常见的主模块包括登录注册、工作台、任务、流程、我的个人中心以本项目实际页面为准但目录思路基本都是一个业务场景收敛到一块。这样组织的好处很直接要改登录逻辑只需要打开pages/login下的相关文件不会牵扯到业务页面要删掉一个模块整个目录一起移除即可。每个页面在uni-app里是一个vue文件也可以是一个带子组件的目录。页面的生命周期和普通Vue组件不同比如onLoad对应页面加载、onShow对应页面每次展示。这和后端管理页面的created、mounted不一样后台管理里页面一般是常驻的但移动端页面频繁进栈出栈很多逻辑必须放在onShow里才能保证每次进入都重新执行刷新。3.2 跳转、tabBar和导航栏的三个经验点页面之间跳转用的是uni-app内置API。普通页面用uni.navigateTo它会保留当前页面并把新页面压入栈tabBar页面则必须用uni.switchTab否则会报错。这是个很典型的初学者问题明明页面存在跳过去却提示页面不存在或直接静默失败多数时候是把switchTab该用的场景写成了navigateTo。导航栏有两种选择一种是原生导航栏配置在pages.json的style里性能和系统一致性最好另一种是自定义导航栏把pages.json里对应页面的navigationStyle设成custom然后页面内自己写头部组件。RuoYiApp版里很多页面为了统一风格会走自定义导航栏这样Android和iOS的状态栏高度、胶囊按钮位置都要自己做适配。如果你在某个页面发现导航栏上面空出一块或者遮住状态栏优先检查status-bar-height的处理逻辑。关于下拉刷新和上拉加载页面配置里开启enablePullDownRefresh后页面里要监听onPullDownRefresh和onReachBottom。一个容易忽略的细节是每次下拉刷新收尾必须调用uni.stopPullDownRefresh()否则刷新圈会一直转这种问题在真机上很显眼模拟器里倒是不容易发现。3.3 页面文件与组件的拆法RuoYiApp版里页面文件通常保持轻量复杂交互抽到components里。推荐的标准是页面内只保留当前页面特有的数据加载和事件编排下拉选项、卡片、弹窗这类可复用的UI做成组件。这样当业务页面多了以后改一个公共组件就能全局生效不用逐个页面去翻。页面在目录里怎么放也有讲究。单个页面只有业务代码时直接是一个xxx.vue如果有专属子组件则收进一个pages/xxx/目录再在子目录里放components。从维护角度看目录方式更好新增页面体积大起来后不至于让pages目录变成一长串文件堆。4. api目录与统一请求封装多端接口调用的关键枢纽4.1 request.js是整条通信链路的心脏RuoYiApp版的所有后端通信几乎都收敛在api目录下。它和后台管理端有点类似但底层不是一个封装好的axios实例而是一层针对uni.request的统一封装核心文件一般是utils/request.js或者api/request.js。统一请求封装要做的事情比较集中拼接基础地址、注入token、处理响应体、统一报错、遇到特定状态码时触发登录失效逻辑。简化后的核心逻辑大致是这样const BASE_URL http://192.168.1.100:8080 function request(options) { // 从store里取出token注入请求头 const token store.state.token header[Authorization] Bearer token return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header, success: (res) { const code res.data.code if (code 401) { // 登录失效清理缓存、跳转登录页 logout() return } if (code 200) { resolve(res.data.data) } else { uni.showToast({ title: res.data.msg, icon: none }) reject(res.data) } }, fail: (err) reject(err) }) }) }这套逻辑的核心价值是把每个页面都要处理的鉴权和错误提示收敛到了一处。页面里只管调接口、拿数据出了问题统一弹提示不用每段业务代码都去判断返回码。4.2 api目录的模块划分方式api目录下通常会按后端Controller维度继续拆文件比如登录认证、个人用户、任务、流程、公告等每个文件里导出若干个方法页面层直接import方法名调用。这样做的对齐价值非常明显后端一个REST接口对应前端一个api方法改接口时顺着方法名就能找到所有调用方。我看到很多团队二次开发时接口一多就直接在页面里写uni.request看起来快但后续后端改了url、加了鉴权页面代码就得翻遍全工程去改。RuoYiApp版的模块化结构本身已经铺好了路正确的增量方式是后端新加接口 - api目录下新增方法 - 页面统一调用方法。4.3 多端下的baseURL差异多端适配在请求层就能体现出来。H5端存在跨域问题后端需要做跨域处理或者用开发代理转发App端不存在浏览器同源策略同一局域网下直接填IP地址即可小程序端对域名有强约束只能是HTTPS且需要在平台后台配置合法域名。所以baseURL最好不要硬编码写死在request.js里而是根据编译平台做条件取值。常见做法是在不同位置用条件编译区分不同端各配一个环境地址。签过这套坑的人都知道App在模拟器上能跑、真机就请求失败多半就是baseURL写成了localhost换成了电脑的局域网IP才正常。5. store与登录态token和用户信息在App里是怎么流动的5.1 登录状态的完整流转过程RuoYiApp版的store里最核心的模块是登录状态和用户信息。打开App后先从本地缓存把token读出来放入store有token就维持登录状态没有token就进登录流程。登录接口返回后token写入store同时用uni.setStorageSync持久化到本地之后getUserInfo再拉个人信息也一并缓存到本地。这个store内存态 storage持久态的双写设计是从后台管理端转过来时最容易忽略的。后台管理系统里刷新页面Vuex状态全部重建所以token一定要存在cookie或localStorage里。移动端也一样App冷启动后store是空的能维持登录身份的只有storage里的缓存。退出登录时双写清理要同时做只清store不清storage会出现界面已退出重启又自动登录的诡异问题。5.2 401拦截与重新登录的闭环登录态失效的处理很能检验项目结构是否清晰。正常链路是某个接口返回401 - 统一请求封装里触发登出 - 清空store和storage - 跳转到登录页。这个逻辑放在request.js里而不是每个页面单独处理是RuoYiApp版结构里很值得借鉴的设计。实际应用中要注意一个细节多个接口同时401时会触发多次跳转和多次清理。更稳妥的做法是设置一个标记位只有第一次401才执行登出动作其余请求直接reject避免登录页被重复打开。这个问题在弱网和接口慢的场景下很容易出现调试时会看到页面疯狂跳动。5.3 用户信息与权限控制的取舍移动端App对权限控制的颗粒度和后台管理系统完全不同。后台管理端习惯于根据角色、按钮权限动态渲染页面但移动端如果完全按这套走页面渲染会非常卡且移动端页面数量通常远少于后台。RuoYiApp版更务实的做法是用户基本信息进store全局共享细粒度的操作权限在具体业务接口由后端返回并校验前端只在必要的入口做粗略判断。这样设计后store里不建议塞太多东西。凡是页面可以临时请求、用完就丢的数据放进页面局部状态即可只有全局都要用、且频繁读取的数据才值得放入store。6. components、utils、static与uni_modules辅助层的分工边界6.1 公共组件与页面组件的边界components目录收集的是跨页面复用的组件。判断一个组件该不该进这里标准很简单有没有至少两个页面会用到。只有当前页面使用的组件留在页面目录下更合理。RuoYiApp版里常见的公共组件有头像选择、状态标签、空数据占位、自定义弹窗等。在uni-app里组件引用的方式比传统Vue更灵活。配置了easycom规则后只要组件放在固定目录下页面里无需import和注册直接用标签名就能渲染。这个机制在写公共组件时特别顺手但也会带来一个排查难点页面里出现了一个组件标签全局搜索不到import语句容易误判为未定义组件。遇到这种问题先看项目根目录或pages.json里是否配置了easycom规则。6.2 utils目录纯函数与可复用逻辑utils目录放的是不依赖页面、不依赖Vue实例的纯逻辑工具。RuoYiApp版里除了前文提到的request.js还有处理token读写、时间格式化、字典翻译、字符串校验等工具文件。判断一个函数应不应该放utils有一个很直观的标准如果它在两个以上页面做同样的事情就应该抽出来。这里想强调一个原则utils里的函数尽量保持无状态、无副作用。尤其是请求封装不应该直接操作某个页面的UI。多人协作时常见的问题是有人在utils里塞了一个弹窗提示导致纯工具函数携带了UI耦合测试和复用都变困难。6.3 static与uni_modules的定位差异static目录是静态资源专区图片、字体、音频等放在这里。多端环境下图片路径的写法有讲究小程序端对本地图片的使用限制比H5更多开发时要留意。如果一个资源必须依赖CDN地址建议统一配置在常量文件里避免散落进各页面写死。uni_modules是uni-app的插件生态目录RuoYiApp版中常见的插件形式有扩展组件、SDK封装、平台能力桥接等。这个目录的维护有版本概念升级插件时要注意版本兼容。一个稳妥的习惯是凡是从插件市场引入的能力尽量再包一层业务适配这样以后换插件时不用把业务代码全部重写一遍。7. 条件编译与多端差异同一套代码是怎么同时跑App、H5、小程序的7.1 条件编译不是魔法是预处理uni-app实现多端的一个核心武器是条件编译。它的用法不是运行时判断而是编译期处理编译到某个平台时只保留该平台对应的代码块其余代码直接剔除。写法上通过特殊注释实现!-- #ifdef MP-WEIXIN -- view只在微信小程序端显示/view !-- #endif --// #ifdef H5 const baseUrl /prod-api // #endif // #ifndef H5 const baseUrl https://api.example.com // #endif理解这一点就能解释很多奇怪现象。比如你代码里明明写了一段App端才执行的逻辑H5端怎么完全没反应原因是编译到H5时那段代码根本就没进编译产物。反过来如果你把一段平台的API无条件写在公共文件里编译到另一个平台时IDE可能不报错运行时才会因为API不存在而中断。7.2 各平台最容易踩的差异点先看登录。微信小程序里登录流程和App端不一样小程序通常要拿code换openid再拿openid去后端换自定义tokenApp端通用做法是用手机号验证码或账号密码。RuoYiApp版的结构中登录逻辑一般收敛在login页面和对应的auth api文件里条件编译主要用来处理当前在哪个平台、需要走哪条登录链路。再看网络。H5端打包后部署在Nginx下接口地址通常考虑用相对路径加代理转发避免跨域App端则没有跨域概念但Android 9及以上对明文HTTP请求有默认限制开发时需要调整网络安全配置。小程序端最麻烦生产环境必须使用HTTPS域名且要提前在平台后台配置合法域名上线前一天才发现域名没加白名单的事业界真不少见。最后看打包。App端打包涉及图标、启动图、应用证书等配置这些都在manifest.json里。真机调试时需要注意同一局域网内App可以直接访问电脑的IP端口但手机如果连的是移动数据或另一个Wi-Fi自然就访问不到后端服务报错也往往是网络异常。8. 从目录到上手二次开发的读码顺序、踩坑记录与配置细节8.1 推荐的代码阅读路径如果第一次接触RuoYiApp版建议不要按目录从上往下扫那没什么效果。我自己摸索下来比较顺的顺序是先跑起来然后按下面这条线走。第一步打开pages.json把整个页面清单过一遍知道这个App有哪几个一级模块、tabBar选了哪些。第二步打开request.js只看baseURL、token注入、401处理三个地方弄清楚一个网络请求从发出到落地会经过哪几道关卡。第三步打开store相关文件和登录页面把token登录写入、启动恢复、退出清理这条链路走通。第四步以某一个具体业务页面为样本从事件触发开始定位它调的是哪个api方法然后顺着api方法回到request.js再把返回值在页面里如何渲染看一遍。这套顺序只用半天左右但比漫无目的地翻文件高效得多。核心目的只有一个先建立页面 - api - request - 后端这条主链路目录里的其他细节以后遇到再补。8.2 新增一个业务页面的完整动作很多人改完工程加页面时只创建了vue文件运行起来却找不到页面。这里把完整动作列一遍在pages目录下创建对应的页面vue文件。在pages.json的pages数组里注册页面路径并配置导航栏标题、是否允许下拉刷新等样式。如果这个页面要在tabBar平级切换还要在tabBar配置里同步处理并留意页面是否已经存在冲突。在api目录下新增或复用接口方法把请求参数和返回结构理清。在页面中引入api方法处理加载态、失败提示和返回数据渲染。真机或模拟器上自测登录状态、下拉刷新、返回跳转几个基础动作。第2步是最容易漏的。uni-app的页面不会自动注册pages.json相当于路由表漏了它编译不报错但运行时怎么跳都是一片空白。这个问题我见过不止一两次每次都是在pages.json里找到解决方案。8.3 二次开发阶段的高频问题记录把常见问题做成一个简表方便排查时直接对号入座现象根因处理方向登录成功但首页请求401新token没写入storage检查登录流程里setStorageSync是否执行存储key是否和request读取的key一致页面跳转提示不存在pages.json漏注册或路径写错核对页面路径和pages数组中的pathApp调用接口报URL无效baseURL写了localhost或端口错误改为电脑局域网IP确认后端允许局域网访问Web端跨域后端未配置CORS或代理未生效优先用代理转发避免明文轻易暴露接口真机可打开但H5白屏manifest或构建配置平台信息不完整检查平台配置和应用标识这套问题清单的价值在于大部分诡异问题最后都落在请求、路由、baseURL这三类基础环节上项目结构分析阶段如果能把这三点吃透实际调试时能少走很多弯路。二次开发另一个值得注意的地方是版本升级。工程目录里的pages.json、manifest.json、request.js这几个关键文件在升级前最好先备份一份。因为新版工程往往在这几处调整了配置项和默认值直接整体覆盖业务页面大概率能保留但请求封装和平台配置可能被冲掉届时又把问题归因到自己的代码上白白浪费时间。我个人实际改这个项目时最深的体会是RuoYiApp版的目录结构并不复杂真正决定开发效率的是你对整个请求链路和登录态流转的理解程度。如果把这两条主线摸透再加多少个页面、换多少个模块本质都是不断重复同一个动作在正确的位置调用正确的方法。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑