📌 本文定位: 面向 iOS/Android/鸿蒙原生工程师,系统梳理 uni-app 项目的完整目录结构。基于 uni-app 官方文档 并大幅补充官方未覆盖的工程化细节、隐藏配置和实战经验。
一、标准项目目录全景图
使用 HBuilderX 或 CLI (npx degit dcloudio/uni-preset-vue#vite-ts my-project) 创建的标准项目结构如下:
1my-uni-app/ 2├── pages/ # 📄 页面目录(核心) 3│ ├── index/ 4│ │ └── index.vue # 首页 5│ └── detail/ 6│ └── detail.vue # 详情页 7├── components/ # 🧩 可复用组件目录 8│ ├── uni-card/ 9│ │ └── uni-card.vue 10│ └── my-button/ 11│ └── my-button.vue 12├── static/ # 🖼️ 静态资源目录(不参与编译) 13│ ├── images/ 14│ ├── fonts/ 15│ └── tabbar/ 16├── assets/ # 🎨 需编译处理的资源目录 17│ ├── styles/ 18│ │ ├── variables.scss # SCSS 变量 19│ │ └── global.css # 全局样式 20│ └── icons/ # SVG 图标等 21├── utils/ # 🔧 工具函数目录 22│ ├── request.ts # 网络请求封装 23│ ├── auth.ts # 登录鉴权 24│ └── format.ts # 格式化工具 25├── api/ # 🌐 API 接口定义目录 26│ ├── user.ts 27│ └── product.ts 28├── store/ # 🗃️ 状态管理目录 29│ ├── index.ts 30│ ├── modules/ 31│ │ ├── user.ts 32│ │ └── cart.ts 33│ └── types.ts 34├── composables/ # 🪝 Vue3 组合式函数目录 35│ ├── useAuth.ts 36│ └── usePagination.ts 37├── types/ # 📝 TypeScript 类型定义目录 38│ ├── global.d.ts 39│ ├── api.d.ts 40│ └── env.d.ts 41├── uni_modules/ # 📦 uni-app 插件目录 42│ ├── uni-popup/ 43│ ├── z-paging/ 44│ └── uni-scss/ 45├── hybrid/ # 🔀 App端本地HTML资源目录 46│ └── html/ 47├── nativeplugins/ # 🔌 App原生插件目录 48│ └── MyPlugin/ 49├── platform/ # 🏗️ 平台专属配置目录(CLI项目) 50│ ├── app/ 51│ ├── mp-weixin/ 52│ └── h5/ 53├── App.vue # 🏠 应用根组件 54├── main.ts / main.js # 🚀 应用入口文件 55├── manifest.json # ⚙️ 应用配置清单(最重要) 56├── pages.json # 📋 页面路由与窗口配置 57├── uni.scss # 🎨 全局 SCSS 变量文件 58├── index.html # 🌐 H5 模板文件 59├── vite.config.ts # ⚡ Vite 构建配置 60├── tsconfig.json # 📘 TypeScript 配置 61├── package.json # 📦 项目依赖与脚本 62├── .env / .env.production # 🌍 环境变量文件 63├── .gitignore # 🙈 Git 忽略规则 64└── README.md # 📖 项目说明 65
二、逐目录/逐文件深度解析
2.1 pages/ — 页面目录
📖 官方定义: "存放所有页面的目录,每个页面以文件夹形式组织。"
作用
- 存放所有业务页面,每个子文件夹对应一个路由页面
- 文件夹名即为路由路径(如
pages/detail/detail→/pages/detail/detail) - 页面文件名建议与文件夹同名
关键规则
| 规则 | 说明 |
|---|---|
| 首页必须是第一个 | pages.json 中 pages 数组第一项为启动页 |
| 页面必须在 pages.json 注册 | 未注册的页面无法通过 navigateTo 跳转 |
| 分包页面放子包目录 | 如 pagesA/detail/detail,在 subPackages 中配置 |
| 页面内可包含私有组件 | 但推荐放到 components/ 统一管理 |
原生对照
| 平台 | 对应概念 |
|---|---|
| iOS | Storyboard/XIB + ViewController 文件组 |
| Android | Activity/Fragment + layout XML |
| 鸿蒙 | Page + Ability |
2.2 components/ — 可复用组件目录
📖 官方定义: "存放可复用组件的目录。"
作用
- 存放跨页面复用的 UI 组件和业务组件
- 支持 easycom 自动导入(无需手动 import)
easycom 规范(重要!)
uni-app 内置了组件自动导入机制,符合以下目录结构的组件无需 import 即可直接使用:
1components/ 2├── uni-card/ 3│ └── uni-card.vue ✅ 自动导入(组件名=文件夹名=文件名) 4├── my-button/ 5│ └── my-button.vue ✅ 自动导入 6├── CustomHeader.vue ❌ 不符合规范,需手动 import 7└── nested/ 8 └── deep-comp/ 9 └── deep-comp.vue ✅ 支持多级嵌套 10
easycom 匹配规则: components/组件名称/组件名称.vue
也可在 pages.json 中自定义 easycom 规则:
1{ 2 "easycom": { 3 "autoscan": true, 4 "custom": { 5 "^my-(.*)": "@/components/my-$1/my-$1.vue", 6 "^uni-(.*)": "@/uni_modules/uni-$1/components/uni-$1/uni-$1.vue" 7 } 8 } 9} 10
组件分类建议
1components/ 2├── base/ # 基础UI组件(按钮、输入框、弹窗) 3├── business/ # 业务组件(商品卡片、订单列表项) 4├── layout/ # 布局组件(导航栏、TabBar、侧边栏) 5└── third-party/ # 第三方封装组件 6
2.3 static/ — 静态资源目录
📖 官方定义: "存放不参与编译过程的静态资源。"
作用
- 存放图片、字体、视频、音频等二进制资源
- 原样复制到输出目录,不经过 webpack/vite 处理
- 可通过绝对路径
/static/xxx.png直接引用
⚠️ 关键注意事项
| 要点 | 说明 |
|---|---|
| 文件大小限制 | 小程序端主包 static 总大小 ≤ 2MB |
| 引用方式 | 必须用 /static/xxx 或 ../../static/xxx,不能用 @/static/ |
| CSS 中引用 | url(/static/images/bg.png) |
| JS 中引用 | '/static/images/avatar.png' |
| 不要放代码文件 | JS/CSS/JSON 等不应放在 static 中 |
| 大文件走 CDN | 超过 200KB 的图片建议上传 CDN |
为什么需要区分 static 和 assets?
| 维度 | static/ | assets/ |
|---|---|---|
| 编译处理 | ❌ 原样复制 | ✅ 经打包工具处理 |
| 路径引用 | 绝对路径 /static/xxx | 相对路径/import |
| 哈希指纹 | ❌ 无 | ✅ 有(缓存友好) |
| Tree Shaking | ❌ 不支持 | ✅ 支持 |
| 适用场景 | TabBar图标、固定背景图 | 主题图片、SVG图标、样式文件 |
2.4 assets/ — 编译资源目录(官方未强调,实战必备)
📖 官方未明确定义此目录,但这是社区和工程化实践中的标准约定。
作用
- 存放需要被构建工具(Vite/Webpack)处理的资源
- 支持 import 导入、SCSS 编译、PostCSS 处理、图片压缩等
典型结构
1assets/ 2├── styles/ 3│ ├── variables.scss # 全局 SCSS 变量(颜色、间距、字号) 4│ ├── mixins.scss # SCSS Mixin 5│ ├── reset.css # 样式重置 6│ ├── theme-light.scss # 亮色主题 7│ └── theme-dark.scss # 暗色主题 8├── icons/ # SVG 图标(可用 unplugin-icons) 9│ ├── home.svg 10│ └── user.svg 11└── images/ # 需要压缩/哈希处理的图片 12 ├── banner.webp 13 └── logo.png 14
在 uni.scss 中引入
1/* uni.scss - 全局自动注入,无需手动 import */ 2@import '@/assets/styles/variables.scss'; 3@import '@/assets/styles/mixins.scss'; 4
2.5 utils/ — 工具函数目录
作用
- 存放纯逻辑的工具函数,与 UI 无关
- 通常不包含 Vue 响应式逻辑(那是 composables 的职责)
推荐结构
1utils/ 2├── request.ts # uni.request 二次封装(拦截器、Token注入、错误处理) 3├── auth.ts # Token 存取、登录态判断 4├── storage.ts # uni.setStorageSync 类型安全封装 5├── format.ts # 日期格式化、金额格式化、手机号脱敏 6├── validate.ts # 表单校验规则 7├── platform.ts # 平台判断工具(条件编译的运行时补充) 8├── crypto.ts # 加密解密 9└── constants.ts # 常量定义(枚举、配置值) 10
request.ts 示例(高频使用)
1// utils/request.ts 2interface RequestOptions { 3 url: string; 4 method?: 'GET' | 'POST' | 'PUT' | 'DELETE'; 5 data?: Record<string, any>; 6 header?: Record<string, string>; 7 showLoading?: boolean; 8} 9 10export const request = <T = any>(options: RequestOptions): Promise<T> => { 11 return new Promise((resolve, reject) => { 12 if (options.showLoading !== false) { 13 uni.showLoading({ title: '加载中...' }); 14 } 15 16 uni.request({ 17 url: [`${import.meta.env.VITE_BASE_URL}${options.url}`](https://xplanc.org/primers/document/zh/10.Bash/90.%E5%B8%AE%E5%8A%A9%E6%89%8B%E5%86%8C/EX.env.md), 18 method: options.method || 'GET', 19 data: options.data, 20 header: { 21 'Content-Type': 'application/json', 22 Authorization: `Bearer ${uni.getStorageSync('token')}`, 23 ...options.header, 24 }, 25 success: (res) => { 26 if (res.statusCode === 200) { 27 resolve(res.data as T); 28 } else if (res.statusCode === 401) { 29 uni.reLaunch({ url: '/pages/login/login' }); 30 reject(new Error('Unauthorized')); 31 } else { 32 uni.showToast({ title: '请求失败', icon: 'none' }); 33 reject(res); 34 } 35 }, 36 fail: (err) => { 37 uni.showToast({ title: '网络异常', icon: 'none' }); 38 reject(err); 39 }, 40 complete: () => { 41 if (options.showLoading !== false) { 42 uni.hideLoading(); 43 } 44 }, 45 }); 46 }); 47}; 48
2.6 api/ — 接口定义目录(官方未提及,工程化必备)
作用
- 按业务模块集中管理所有后端接口
- 与
utils/request配合,实现接口调用的类型安全和统一管理
1// api/user.ts 2import { request } from '@/utils/request'; 3 4export interface UserInfo { 5 id: number; 6 nickname: string; 7 avatar: string; 8} 9 10export const getUserInfo = () => 11 request<UserInfo>({ url: '/user/info' }); 12 13export const updateUserProfile = (data: Partial<UserInfo>) => 14 request({ url: '/user/profile', method: 'PUT', data }); 15
1<!-- 页面中使用 --> 2<script setup lang="ts"> 3import { getUserInfo, type UserInfo } from '@/api/user'; 4 5const user = ref<UserInfo>(); 6 7onLoad(async () => { 8 user.value = await getUserInfo(); 9}); 10</script> 11
2.7 store/ — 状态管理目录
作用
- 管理跨页面共享的全局状态
- 推荐使用 Pinia(Vue3)或 Vuex(Vue2)
1store/ 2├── index.ts # Store 实例创建与导出 3├── modules/ 4│ ├── user.ts # 用户状态 5│ ├── cart.ts # 购物车状态 6│ └── settings.ts # 应用设置 7└── types.ts # Store 相关类型定义 8
1// store/modules/user.ts (Pinia) 2import { defineStore } from 'pinia'; 3import { getUserInfo, type UserInfo } from '@/api/user'; 4 5export const useUserStore = defineStore('user', { 6 state: () => ({ 7 userInfo: null as UserInfo | null, 8 token: uni.getStorageSync('token') || '', 9 }), 10 getters: { 11 isLoggedIn: (state) => !!state.token, 12 displayName: (state) => state.userInfo?.nickname || '游客', 13 }, 14 actions: { 15 async fetchUserInfo() { 16 this.userInfo = await getUserInfo(); 17 }, 18 logout() { 19 this.userInfo = null; 20 this.token = ''; 21 uni.removeStorageSync('token'); 22 uni.reLaunch({ url: '/pages/login/login' }); 23 }, 24 }, 25}); 26
2.8 composables/ — 组合式函数目录(Vue3 专属)
作用
- 封装可复用的响应式逻辑(区别于 utils 中的纯函数)
- 命名约定以
use开头
1// composables/usePagination.ts 2import { ref, onMounted } from 'vue'; 3 4export function usePagination<T>(fetchFn: (page: number) => Promise<T[]>) { 5 const list = ref<T[]>([]) as Ref<T[]>; 6 const page = ref(1); 7 const loading = ref(false); 8 const hasMore = ref(true); 9 10 const loadMore = async () => { 11 if (loading.value || !hasMore.value) return; 12 loading.value = true; 13 try { 14 const data = await fetchFn(page.value); 15 list.value.push(...data); 16 hasMore.value = data.length >= 20; 17 page.value++; 18 } finally { 19 loading.value = false; 20 } 21 }; 22 23 const refresh = async () => { 24 page.value = 1; 25 list.value = []; 26 hasMore.value = true; 27 await loadMore(); 28 }; 29 30 return { list, loading, hasMore, loadMore, refresh }; 31} 32
2.9 types/ — TypeScript 类型定义目录
作用
- 存放全局类型声明、API 响应类型、环境类型等
.d.ts文件会被 TS 编译器自动识别
1// types/global.d.ts 2declare namespace UniApp { 3 // 扩展 globalData 类型 4 interface GlobalData { 5 userInfo: UserInfo | null; 6 isDarkMode: boolean; 7 } 8} 9 10// types/env.d.ts 11/// <reference types="vite/client" /> 12interface ImportMetaEnv { 13 readonly VITE_BASE_URL: string; 14 readonly VITE_APP_TITLE: string; 15} 16
2.10 uni_modules/ — uni-app 插件目录
📖 官方定义: "uni_modules 是 uni-app 的插件模块化规范,支持组件、JS SDK、云函数、原生插件的统一管理。"
作用
- 从 DCloud 插件市场 下载的插件自动安装到此目录
- 支持组件自动导入(easycom)、API 调用、原生能力扩展
典型插件
| 插件 | 用途 |
|---|---|
| uni-popup | 弹出层 |
| uni-icons | 图标库 |
| z-paging | 高性能分页列表 |
| uni-scss | 官方 SCSS 变量体系 |
| luch-request | HTTP 请求库 |
| uni-read-pages | 读取 pages.json 配置 |
⚠️ 注意事项
- 不要手动修改
uni_modules内的插件源码(升级会被覆盖) - 如需定制,fork 后放入
components/自行维护 - 部分插件仅支持特定平台,使用前查阅兼容表
2.11 hybrid/ — App 端本地 HTML 资源目录
📖 官方定义: "存放 App 端 web-view 加载的本地 HTML 文件。"
作用
- 仅在 App 端有效,用于
web-view组件加载本地网页 - H5/小程序端忽略此目录
1hybrid/ 2└── html/ 3 ├── agreement.html # 用户协议 4 ├── privacy.html # 隐私政策 5 └── chart.html # ECharts 图表页 6
1<web-view src="/hybrid/html/agreement.html" /> 2
2.12 nativeplugins/ — App 原生插件目录
作用
- 存放自定义的原生插件(iOS/Android/鸿蒙原生代码)
- 用于调用 uni-app 未提供的原生能力
1nativeplugins/ 2└── MyFaceDetector/ 3 ├── ios/ # iOS 原生代码 4 ├── android/ # Android 原生代码 5 ├── harmony/ # 鸿蒙原生代码 6 └── package.json # 插件描述文件 7
💡 大多数原生需求可通过插件市场解决,仅在必要时自行开发。
2.13 platform/ — 平台专属配置目录(CLI 项目)
📖 官方文档较少提及,这是 CLI 项目中用于存放各平台差异化配置的目录。
1platform/ 2├── app/ 3│ └── Info.plist # iOS 权限配置 4├── mp-weixin/ 5│ └── project.config.json # 微信小程序项目配置 6└── h5/ 7 └── favicon.ico # H5 网站图标 8
三、核心配置文件详解
3.1 manifest.json — 应用配置清单(最重要的配置文件)
📖 官方定义: "应用的配置文件,用于指定应用名称、appid、版本、权限、SDK 配置等。"
核心配置项
1{ 2 "name": "我的应用", // 应用名称 3 "appid": "__UNI__XXXXXXX", // DCloud 分配的唯一ID 4 "description": "应用描述", 5 "versionName": "1.0.0", // 显示版本号 6 "versionCode": "100", // 内部版本号(整数) 7 8 // ✅ App 端配置 9 "app-plus": { 10 "usingComponents": true, 11 "splashscreen": { // 启动页配置 12 "alwaysShowBeforeRender": true, 13 "waiting": true, 14 "autoclose": true 15 }, 16 "modules": { // 原生模块开关 17 "OAuth": {}, // 登录 18 "Push": {}, // 推送 19 "Maps": {}, // 地图 20 "Payment": {} // 支付 21 }, 22 "distribute": { 23 "android": { 24 "permissions": [ // Android 权限声明 25 "<uses-permission android:name="android.permission.CAMERA"/>" 26 ], 27 "minSdkVersion": 21, 28 "targetSdkVersion": 33 29 }, 30 "ios": { 31 "privacyDescription": { // iOS 权限用途说明 32 "NSCameraUsageDescription": "用于拍照上传头像", 33 "NSLocationWhenInUseUsageDescription": "用于定位附近门店" 34 } 35 } 36 } 37 }, 38 39 // ✅ 微信小程序配置 40 "mp-weixin": { 41 "appid": "wxXXXXXXXXXXXXXX", 42 "setting": { 43 "urlCheck": false, 44 "es6": true, 45 "postcss": true, 46 "minified": true 47 }, 48 "usingComponents": true, 49 "permission": { 50 "scope.userLocation": { 51 "desc": "用于获取您的位置信息" 52 } 53 } 54 }, 55 56 // ✅ H5 配置 57 "h5": { 58 "title": "我的应用", 59 "router": { 60 "mode": "history", // hash | history 61 "base": "/" 62 }, 63 "devServer": { 64 "port": 8080, 65 "proxy": { // 开发代理 66 "/api": { 67 "target": "http://localhost:3000", 68 "changeOrigin": true 69 } 70 } 71 }, 72 "optimization": { 73 "treeShaking": { 74 "enable": true // 按需引入 uni API 75 } 76 } 77 }, 78 79 // ✅ 鸿蒙 NEXT 配置 80 "app-harmony": { 81 "package": "com.example.myapp", 82 "icons": { 83 "foreground": "static/harmony/icon_foreground.png", 84 "background": "static/harmony/icon_background.png" 85 } 86 } 87} 88
原生对照
| uni-app manifest.json | iOS | Android | 鸿蒙 |
|---|---|---|---|
| name | CFBundleDisplayName | app_name (strings.xml) | bundleName |
| appid | Bundle Identifier | applicationId | bundleName |
| versionName/Code | CFBundleShortVersionString/CFBundleVersion | versionName/versionCode | versionName/versionCode |
| distribute.ios.privacyDescription | Info.plist NSxxxUsageDescription | — | module.json5 requestPermissions |
| distribute.android.permissions | — | AndroidManifest.xml | module.json5 requestPermissions |
| mp-weixin.appid | — | — | — |
3.2 pages.json — 页面路由与窗口配置
📖 官方定义: "对 uni-app 进行全局配置,决定页面文件的路径、窗口表现、导航条样式等。"
完整配置示例
1{ 2 // ✅ 全局窗口配置 3 "globalStyle": { 4 "navigationBarTextStyle": "black", 5 "navigationBarTitleText": "我的应用", 6 "navigationBarBackgroundColor": "#FFFFFF", 7 "backgroundColor": "#F5F5F5", 8 "backgroundTextStyle": "dark", 9 "app-plus": { 10 "titleNView": false // App端隐藏原生导航栏 11 } 12 }, 13 14 // ✅ 页面路由列表(第一项为首页) 15 "pages": [ 16 { 17 "path": "pages/index/index", 18 "style": { 19 "navigationBarTitleText": "首页", 20 "enablePullDownRefresh": true 21 } 22 }, 23 { 24 "path": "pages/detail/detail", 25 "style": { 26 "navigationBarTitleText": "详情", 27 "navigationStyle": "custom" // 自定义导航栏 28 } 29 } 30 ], 31 32 // ✅ TabBar 配置 33 "tabBar": { 34 "color": "#999999", 35 "selectedColor": "#007AFF", 36 "backgroundColor": "#FFFFFF", 37 "borderStyle": "white", 38 "list": [ 39 { 40 "pagePath": "pages/index/index", 41 "text": "首页", 42 "iconPath": "static/tabbar/home.png", 43 "selectedIconPath": "static/tabbar/home-active.png" 44 }, 45 { 46 "pagePath": "pages/mine/mine", 47 "text": "我的", 48 "iconPath": "static/tabbar/mine.png", 49 "selectedIconPath": "static/tabbar/mine-active.png" 50 } 51 ] 52 }, 53 54 // ✅ 分包配置 55 "subPackages": [ 56 { 57 "root": "pagesA", 58 "pages": [ 59 { "path": "order/list", "style": { "navigationBarTitleText": "订单列表" } } 60 ] 61 } 62 ], 63 64 // ✅ 预下载分包 65 "preloadRule": { 66 "pages/index/index": { 67 "network": "all", 68 "packages": ["pagesA"] 69 } 70 }, 71 72 // ✅ easycom 自定义规则 73 "easycom": { 74 "autoscan": true, 75 "custom": {} 76 } 77} 78
3.3 uni.scss — 全局 SCSS 变量文件
📖 官方定义: "uni-app 内置的常用样式变量,会自动注入到每个 scss 文件中。"
作用
- 定义全局设计令牌(Design Tokens)
- 无需手动 @import,编译器自动注入到每个
<style lang="scss">中 - 可直接使用 uni-ui 内置变量
1/* uni.scss */ 2 3/* 品牌色 */ 4$brand-primary: #007AFF; 5$brand-success: #4CD964; 6$brand-warning: #F0AD4E; 7$brand-error: #DD524D; 8 9/* 文字色 */ 10$text-main: #333333; 11$text-secondary: #666666; 12$text-placeholder: #999999; 13 14/* 间距 */ 15$spacing-sm: 8rpx; 16$spacing-md: 16rpx; 17$spacing-lg: 32rpx; 18 19/* 圆角 */ 20$radius-sm: 4rpx; 21$radius-md: 8rpx; 22$radius-lg: 16rpx; 23 24/* uni-ui 内置变量可直接使用 */ 25/* $uni-color-primary, $uni-font-size-base 等 */ 26
3.4 main.ts — 应用入口文件
1// main.ts 2import { createSSRApp } from 'vue'; 3import { createPinia } from 'pinia'; 4import App from './App.vue'; 5 6export function createApp() { 7 const app = createSSRApp(App); 8 9 // 注册 Pinia 10 const pinia = createPinia(); 11 app.use(pinia); 12 13 // 注册全局组件(非 easycom 的) 14 // app.component('MyGlobalComp', MyGlobalComp); 15 16 // 注册全局指令 17 // app.directive('focus', { mounted: (el) => el.focus() }); 18 19 return { app }; 20} 21
⚠️ 注意: uni-app 使用
createSSRApp而非createApp,这是为了支持服务端渲染和多实例隔离。
3.5 index.html — H5 模板文件
1<!DOCTYPE html> 2<html lang="zh-CN"> 3 <head> 4 <meta charset="UTF-8" /> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0" /> 6 <title></title> 7 <!--preload-links--> 8 <!--app-context--> 9 </head> 10 <body> 11 <div id="app"><!--app-html--></div> 12 <script type="module" src="/main.ts"></script> 13 </body> 14</html> 15
💡
<!--app-context-->和<!--app-html-->是 SSR 占位符,开发时可忽略。
3.6 .env 环境变量文件(官方未详述,实战必备)
1# .env.development 2VITE_BASE_URL=http://localhost:3000/api 3VITE_APP_TITLE=我的应用(开发) 4 5# .env.production 6VITE_BASE_URL=https://api.example.com 7VITE_APP_TITLE=我的应用 8
在代码中使用:
1const baseUrl = import.meta.env.VITE_BASE_URL; 2
⚠️ 只有以
VITE_开头的变量才会暴露给客户端代码。
四、目录结构设计原则总结
| 原则 | 说明 |
|---|---|
| 按职责分层 | pages(视图)、api(接口)、store(状态)、utils(工具)、composables(逻辑) 各司其职 |
| 就近原则 | 仅某页面使用的组件/工具放在页面同级目录 |
| 命名规范 | 文件夹 kebab-case,组件 PascalCase,工具 camelCase |
| 平台隔离 | 用条件编译 #ifdef 而非分目录管理差异代码 |
| 资源分离 | 编译资源放 assets/,静态资源放 static/ |
| 类型先行 | API 响应类型、Store 类型、全局类型统一放 types/ |
| 插件优先 | 先查插件市场,避免重复造轮子 |
五、原生工程师快速映射表
| 原生概念 | uni-app 对应 | 位置 |
|---|---|---|
| Xcode Project / DevEco Module | 项目根目录 | / |
| Info.plist / module.json5 | manifest.json | /manifest.json |
| Navigation Controller / Router | pages.json | /pages.json |
| Storyboard / Layout XML | pages/*.vue | /pages/ |
| Custom View / Component | components/*.vue | /components/ |
| Assets.xcassets / resource | static/ + assets/ | /static/ /assets/ |
| AppDelegate / EntryAbility | App.vue | /App.vue |
| Singleton / DataManager | store/ | /store/ |
| NetworkManager / Retrofit | utils/request.ts + api/ | /utils/ /api/ |
| Build Settings / build.gradle | vite.config.ts | /vite.config.ts |
| CocoaPods / ohpm | uni_modules/ + package.json | /uni_modules/ |
| Entitlements / Permissions | manifest.json distribute | /manifest.json |
💡 一句话总结: uni-app 的目录结构是 "Vue 前端工程化 + 原生多端配置" 的融合体。
pages/components/store/composables来自 Vue 生态最佳实践,manifest.json/pages.json/static/hybrid/nativeplugins来自多端原生适配需求。理解了这两条线索,整个项目结构就一目了然了。
📚 参考资料:
