2. 工程结构与启动链路
移动端手册工程结构大约 5 分钟约 1459 字
一句话定位
四个根文件决定整个 App 的形态:main.js 造应用、App.vue 管生命周期、pages.json 管路由与外观、manifest.json 管各端打包配置。
启动链路
index.html (仅 H5) 小程序/App:编译器生成入口
│
▼
main.js createApp()
│
┌─────────────┼──────────────────────────┐
▼ ▼ ▼
createSSRApp(App) initRequest() registerAuthDirectives(app)
(挂载 App.vue) ├ 读本地 access-token ├ v-auth / v-auths / v-auth-all
│ 过期则改带 X-Authorization └ 对应 -else 变体
├ http.setConfig(baseURL = env.baseUrl)
├ requestInterceptors:注入 Authorization
└ responseInterceptors:401 跳登录 / 回写新 token
│
▼
App.vue onLaunch / onShow / onHide
│ 全局样式:page{font-size:28rpx;background:#f5f5f5}
▼
pages.json → pages[0] = "pages/login/login" ← 应用启动页
│
▼
登录页:sysAuth.captcha() 取验证码
sm2.doEncrypt(password, env.smKey, 1)
sysAuth.login() → setStorageSync('access-token' / 'x-access-token')
sysAuth.userInfo() → setStorageSync('current-user')
│
▼
uni.switchTab({ url: '/pages/home/home' }) ← tabBar 页只能 switchTab
│
▼
home.vue 挂载 <BusinessOverview/> <OrderOverview/> <SysLogOp/> 以及 <TabBar :current-page="0"/>
TabBar 组件 onMounted → uni.hideTabBar()(因为 tabBar.custom = true)一、main.js:只做三件事
import App from './App'
import { initRequest } from './utils/request'
// #ifdef VUE3
import { createSSRApp } from 'vue'
import { registerAuthDirectives } from '@/utils/authFunction'
export function createApp() {
const app = createSSRApp(App)
initRequest(app) // 全局请求配置 + 拦截器
registerAuthDirectives(app) // v-auth 系列指令
return { app }
}
// #endif- 仓库同时保留了
// #ifndef VUE3的 Vue2 分支(new Vue+App.mpType='app'),但manifest.json已声明"vueVersion": "3",实际走 VUE3 分支。 initRequest在utils/request.js中定义为无参函数(const initRequest = () => {...}),此处传app不会报错但也不生效;想用 vm 可自行扩展参数。initRequest在api/base/request.ts里被重复调用了一次(该模块顶层直接initRequest()),所以即便忘记在 main.js 调用也能工作,但建议保持 main.js 唯一入口。
二、App.vue:生命周期 + 全局样式
<script>
export default {
onLaunch: function() { console.log('App Launch') },
onShow: function() { console.log('App Show') },
onHide: function() { console.log('App Hide') }
}
</script>
<style lang="scss">
page {
margin: 0;
padding: 0;
font-size: 28rpx; // 全局基准字号
background: #f5f5f5;
}
</style>onLaunch 是做「启动即执行」逻辑的位置,例如:检查本地 token、读取主题色、静默刷新用户信息。
三、pages.json:路由表 + 外观 + tabBar
{
"easycom": { "autoscan": true },
"pages": [
{ "path": "pages/login/login", "style": { "navigationBarTitleText": "", "navigationStyle": "custom" } },
{ "path": "pages/home/home", "style": { "navigationBarTitleText": "Admin.NET 开发框架" } },
{ "path": "pages/mine/mine", "style": { "navigationBarTitleText": "个人中心" } },
{ "path": "pages/password/password", "style": { "navigationBarTitleText": "修改密码" } },
{ "path": "pages/about/about", "style": { "navigationBarTitleText": "关于我们", "navigationBarBackgroundColor": "#23212F" } },
{ "path": "pages/feedback/feedback", "style": { "navigationBarTitleText": "意见反馈", "enablePullDownRefresh": false } }
],
"globalStyle": {
"navigationBarTextStyle": "white",
"navigationBarTitleText": "Admin.NET",
"navigationBarBackgroundColor": "#11559c",
"app-plus": { "bounce": "none" }
},
"tabBar": {
"custom": true, // 自定义 tabBar → 需自行 uni.hideTabBar()
"list": [
{ "pagePath": "pages/home/home" },
{ "pagePath": "pages/mine/mine" }
]
}
}约定:
pages[0]即启动页,当前是登录页。- 新增页面必须在
pages数组里注册,否则跳转 404。 - tabBar 页只能用
uni.switchTab,navigateTo打不开。 easycom.autoscan: true开启组件自动引入;但本仓库的pages/component/*.vue并不在components/目录下,实际是显式 import 使用的(见home.vue)。
四、manifest.json:各端打包配置
| 节点 | 作用 | 仓库现状 |
|---|---|---|
appid | DCloud 应用标识 | __UNI__B91D75F |
vueVersion | Vue 版本 | 3 |
mp-weixin.appid | 微信小程序 appid | 空,发布前必填 |
mp-weixin.setting.urlCheck | 是否校验合法域名 | false(开发期友好) |
app-plus.distribute.android.permissions | Android 权限清单 | 已配置网络/相机/振动等 |
app-plus.distribute.icons | App 图标 | 指向 unpackage/res/icons/*.png |
app-plus.splashscreen | 启动图 | alwaysShowBeforeRender:true |
uniStatistics.enable | uni 统计 | false |
五、uni.scss:全局样式变量
$uni-color-primary: #11559c; // 与 globalStyle 导航栏同色
$uni-color-success: #4cd964;
$uni-color-warning: #f0ad4e;
$uni-color-error: #dd524d;
$uni-text-color: #333; // 基本色
$uni-text-color-grey: #999; // 辅助灰
$uni-bg-color: #ffffff;
$uni-bg-color-grey: #f8f8f8;
$uni-border-color: #c8c7cc;
$uni-font-size-sm: 12px; $uni-font-size-base: 14px; $uni-font-size-lg: 16px;
$uni-spacing-row-base: 10px; $uni-spacing-col-base: 8px;
$uni-border-radius-sm: 2px; $uni-border-radius-base: 3px; $uni-border-radius-lg: 6px;uni-app 自动注入该文件,页面里直接写 color: $uni-color-primary; 即可,无需 @import。 单位用 rpx(750rpx = 屏宽),字号类变量是 px,混用不影响编译。
六、页面路由与跳转约定
uni.navigateTo({ url: '/pages/password/password' }) // 保留当前页,入栈(最多 10 层)
uni.redirectTo({ url: '/pages/login/login' }) // 关闭当前页,替换
uni.reLaunch({ url: '/pages/login/login' }) // 关闭所有页,重开(退出登录用这个)
uni.switchTab({ url: '/pages/home/home' }) // 切 tabBar 页
uni.navigateBack() // 返回上一页(delta 可指定层数)传参与取参:
// 跳转
uni.navigateTo({ url: '/pages/password/password?id=123&from=mine' })
// 目标页
import { onLoad } from '@dcloudio/uni-app'
onLoad((options) => { console.log(options.id, options.from) })utils/getUrlParam.js 提供 GetUrlParam(name) 与 getRequestParams(url),仅在 H5 可用(内部用 window.location)。
七、全局能力一览
| 能力 | 文件 | 关键导出 |
|---|---|---|
| HTTP 底层 | utils/http.js | http.request / http.setConfig / http.interceptors / toast |
| 请求初始化与拦截器 | utils/request.js | initRequest / decryptJWT / getJWTDate / clearAccessTokens |
| Token 键名 | utils/request.js | accessTokenKey='access-token'、refreshAccessTokenKey='x-access-token'、currentUserKey='current-user' |
| 权限指令 | utils/authFunction.ts | auth / auths / authAll / registerAuthDirectives / hAuth* |
| 环境配置 | utils/.env.js | env.baseUrl / env.smKey / env.defaultUser |
| 校验正则 | utils/validate.js | PhoneVal / IDcardVal / judgementIdCard |
| AES 加解密 | utils/encrypt.js | AESEncrypt / AESDecrypt(注意依赖 crypto-js,未列入 package.json) |
步骤清单
- 明确启动页:
pages.json的pages[0]。 - 在
main.js的createApp()里注册全局插件/指令(现已有initRequest、registerAuthDirectives)。 - 全局样式写在
App.vue的page{},共享变量写在uni.scss。 - 新增页面:建
pages/xxx/xxx.vue→ 在pages.json的pages数组注册 → 用uni.navigateTo/switchTab跳转。 - 复用组件放
pages/component/,用@/pages/component/xxx.vue显式 import。 - 各端差异化配置统一改
manifest.json,不要散落到代码里。
⚠️ 注意事项与常见坑
- 自定义 tabBar 一定要
uni.hideTabBar():tabBar.custom:true时系统 tabBar 仍在,只是被遮住;tabbar.vue在onMounted里调用了,新页面复用该组件即可。 switchTab不能带参数:tabBar 页之间传值用 storage 或全局状态。navigateTo有 10 层栈限制:深层表单流程用redirectTo替代。about.vue单独改了导航栏背景(#23212F),globalStyle是白字,配深色背景时要同步确认navigationBarTextStyle。utils/encrypt.js依赖crypto-js但未在package.json声明,直接调用AESEncrypt会报模块缺失;需要时自行npm i crypto-js。initRequest()会读本地 token 并写入env.header,所以baseUrl/header必须在首次请求前就绪;不要在页面里重复setConfig。
小结
记住这条链路:main.js(装配)→ App.vue(生命周期/全局样式)→ pages.json(路由与外观)→ 首屏登录页 → switchTab 到首页 → 首页按组件拼装。所有跨页面的约定都以 pages.json 为准。