App 端初始化:UniApp 项目骨架与目录约定
uni-preset-vue · pages.json · uview-plus easycom · 目录约定 · 路径别名 · CSS 兼容
后端 pig 跑起来后,App 端就可以并行推进了。这一篇记录我用 HBuilderX 创建 uni-preset-vue 项目、引入 uview-plus、定下目录约定的全过程。
重点不是「怎么装 HBuilderX」,而是几个关键决定:为什么选 uni-preset-vue 而不是 cli 脚手架、pages.json 的「地图即路由」哲学、uview-plus 的 easycom 自动按需引入、目录约定、config.js 的 baseUrl 切换。
一、为什么是 UniApp + uni-preset-vue
1.1 选 UniApp 的原因
一套代码三端跑:H5、小程序、App 全覆盖
Vue 3 语法:跟管理后台技术栈统一,不用切换思维
HBuilderX 真机调试丝滑:扫码即装,比 react-native 强太多
1.2 为什么选 uni-preset-vue 模板
UniApp 创建项目有两种方式:
我选的是第二种 cli 方式,但用 HBuilderX 打开。原因:HBuilderX 可视化创建的项目用 webpack 构建,慢;cli 方式用 Vite,开发热更新快很多。但有个前提:cli 创建的项目必须用 HBuilderX 运行到手机/模拟器。
1.3 前置条件
HBuilderX 4.x+(必须是 App 开发版,Alpha 版也行)
Node.js 20 LTS(跟 pig-ui 对齐,避免版本跳跃)
Android 模拟器或真机(用真机更靠谱,扫码调试省事)
二、创建项目
2.1 用 cli 创建 uni-preset-vue + vite 项目

为什么用 degit 而不是 vue create:degit 比直接 clone 快,不会带 git 历史。
#vite-ts是指定模板分支,表示「vite + TypeScript」版本。
2.2 项目结构预览

2.3 验证项目能跑

浏览器打开 http://localhost:5173,看到 UniApp 默认首页,说明项目骨架 OK。
小坑:第一次 npm run dev:h5 可能会报「vite-plugin-uni」找不到,再 npm install 一次就好。
三、pages.json:UniApp 的「地图即路由」哲学
3.1 什么是「地图即路由」
Vue Router 是「路由表 + 组件映射」的模式。UniApp 的 pages.json 不一样——它既是路由表,又是页面元信息载体。每个页面的导航栏样式、tabBar、标题、是否需要登录,全部写在 pages.json 里。这就是「地图即路由」:pages.json 就是整个 App 的地图,看一眼就知道有多少页面、每个页面长什么样。
3.2 pages.json 的核心结构

3.3 三个关键概念
① pages 数组的第一项是启动页
pages 数组的第一个元素,就是 App 启动后默认打开的页面。我的做法是:首页(index)放第一个,登录判断在首页里做。这样启动快,不会被登录页阻塞。
② globalStyle 是全局默认样式
所有页面共享的导航栏样式、背景色,写在 globalStyle 里。单个页面要覆盖时,在 pages 数组对应项的 style 里改。
③ tabBar 最多 5 个
tabBar 是底部导航栏,最少 2 个、最多 5 个。我用了 2 个:笔记、我的。每个 tab 的 pagePath 必须在 pages 数组里存在。
3.4 我的 pages.json 配置策略

注意:tabBar 里的 pagePath 必须出现在 pages 数组里,但不一定排在前面。tabBar 页面会被预加载,跳转速度快。
四、引入 uview-plus:easycom 自动按需引入的魔法
4.1 uview-plus 是什么
uview-plus 是 uni-app 生态里最全的 Vue3 组件库,提供按钮、表单、列表、弹窗等 80+ 组件,跨端兼容。
4.2 安装
npm install uview-plus
4.3 配置 easycom(核心)
UniApp 的 easycom 机制是:只要你装了 uview-plus,在 template 里直接写 <u-button> 就能用,不需要 import。但这需要在 pages.json 里配置:

这三条正则的含义:
^u--(.*)→ 匹配<u--xxx>,某些特殊组件^up-(.*)→ 匹配<up-xxx>,另一种命名风格^u-([^-].*)→ 匹配<u-xxx>(注意-后面不能是-),最常见的用法
配置完后,所有 u- 开头的组件都会被自动 import,按需打包,不会因为没用而增加包体积。
4.4 引入样式和工具函数
在 src/main.ts 里加:

在 src/App.vue 里加全局样式:

在 src/uni.scss 里引入 uview-plus 主题变量:

4.5 验证 easycom 生效

跑起来看到紫色按钮,说明 easycom 生效。
4.6 easycom 的坑
坑一:组件没生效,控制台报「Failed to resolve component」
99% 是 pages.json 的 easycom 配置没生效。检查点:easycom 必须在 pages.json 的最外层(跟 pages 同级),不能写在 pages 数组里;正则写错。
坑二:H5 能跑,App 端报错
某些 uview-plus 组件依赖 H5 环境,App 端不支持。例如 <u-parse> 的某些高级用法。写跨端代码时一定看官方文档的「平台差异说明」。
坑三:easycom 配置后 npm install 重装,组件全失效
这是缓存问题。解决:停止 dev server → 删除 node_modules → npm install → 重启。
五、目录约定:可扩展的 App 端结构
5.1 我推荐的目录结构

5.2 几个关键约定
① pages 目录按业务模块组织
不要把所有页面平铺在 pages 下。用 pages/note/detail/index.vue、pages/note/edit/index.vue 这种「模块/子页面/index.vue」结构,扩展性强。
② api 目录按业务模块拆分
每个业务模块一个 api 文件:api/user.ts、api/note.ts。接口多了再拆 api/note/detail.ts、api/note/edit.ts。
③ components 只放全局组件
只在某一个页面用到的组件,放页面目录下:pages/index/components/xxx.vue。components 只放跨页面复用的。
④ composables 用 use 前缀
Vue 3 组合式函数约定用 use 开头:useAuth、useNote。看到名字就知道是 hook。
六、config.js:环境配置
6.1 为什么需要 config.js
开发时连本地后端 http://192.168.1.100:9999,生产时连线上 https://api.brainypipe.com。不能在代码里写死,必须用配置文件统一管理。
6.2 创建 src/config/index.ts

6.3 关键点解释
① import.meta.env.PROD
这是 Vite 内置的环境变量,生产环境(npm run build:xxx)返回 true,开发环境(npm run dev:xxx)返回 false。不需要自己配置 .env 文件。
② 为什么不用 process.env.NODE_ENV
Vite 不暴露 process.env,用 import.meta.env 是 Vite 的标准。
③ 怎么加自定义环境变量
在项目根目录创建 .env、.env.development、.env.production,里面用 VITE_XXX 开头的变量,代码里用 import.meta.env.VITE_XXX 读。但我这种小项目,直接判断 PROD 够用了。
七、路径别名配置:告别 ../../../
7.1 为什么要配路径别名
项目大了之后,深层目录的文件 import 时会出现 ../../../api/user 这种相对路径,难读、改路径就崩。配了别名后变成 @/api/user,清爽。
7.2 配置 vite.config.ts

说明:@ 和 /@ 都指向 src,前者用于 JS import,后者用于 template 里的静态资源引用。
7.3 配置 tsconfig.json(让 TS 识别别名)

配完后,IDE 才不会报错,跳转也能用。
八、CSS 兼容性踩坑与全局样式约定
8.1 不用 gap 属性
CSS gap 在 H5 现代浏览器支持很好,但 Android 4.x WebView 不支持。App 端某些低版本 WebView 跑 flex + gap 会无效。统一用 margin 替代:

8.2 全部用 rpx 单位
rpx 是 UniApp 的响应式像素,设计稿 750px 宽度下 1rpx = 1px。在 H5、小程序、App 三端都能自动适配。固定像素用 px,需要响应式用 rpx。
8.3 在 uni.scss 定义全局变量

在 .vue 文件里 @import '@/uni.scss' 后就能用这些变量。
九、ZPaging:列表渲染神器
9.1 为什么不用 scroll-view
UniApp 自带的 scroll-view 在数据量大时性能差,长列表会卡顿。ZPaging 是 uni-app 生态里性能最好的分页列表组件,支持虚拟列表、下拉刷新、上拉加载。
9.2 安装
npm install z-paging
9.3 在 pages.json 注册 easycom

9.4 基础用法

ZPaging 会自动管理分页、下拉刷新、上拉加载、空数据展示,你只需要把请求到的数据 complete 给它。
9.5 ZPaging 使用规范
禁止事项
不要在 z-paging 外层再套 scroll-view
不要在 z-paging 内部用 v-if 大量切换 DOM
不要在 queryList 回调里同步赋值 dataList(z-paging 自己管)
推荐做法
每个列表页用一个独立的 z-paging 实例
列表项用 :key 绑定唯一 id(不要用 index)
空数据自定义
#empty插槽
十、真机调试:从「能跑」到「真跑」
10.1 为什么要真机调试
H5 跑得通不代表 App 端跑得通。真机上会暴露三类问题:WebView 兼容性(CSS、JS API)、原生组件差异(input、video)、网络/权限(HTTPS、存储)。
10.2 真机调试步骤
HBuilderX 打开项目 → 菜单「运行」→「运行到手机或模拟器」→「运行到 Android App 基座」
手机和电脑同一 Wi-Fi → HBuilderX 自动检测到设备 → 点「运行」
手机自动安装「HBuilder 标准基座」 → 启动 App
修改代码 → 保存 → 自动热更新到手机
常见问题:手机检测不到 → 检查手机是否开启「USB 调试」和「允许 ADB 调试」。小米/红米要额外开「USB 调试(安全设置)」。
10.3 连接本地后端
真机访问电脑 IP,要保证两点:
手机和电脑同一 Wi-Fi(不是插 USB 就能联网)
电脑防火墙放行 9999 端口(pig 后端端口)
在 config/index.ts 里把 baseUrl 改成电脑内网 IP:http://192.168.1.100:9999(localhost 在手机里指手机自己)。
十一、踩坑总结
11.1 HBuilderX 版本必须对齐
cli 创建的项目用的 uni-app 依赖版本,必须和 HBuilderX 版本一致。否则真机运行会报「uni-app 编译器版本和运行时版本不一致」。解决:package.json 里的 @dcloudio/* 全部指定为 HBuilderX 对应版本号。
11.2 easycom 配置顺序
easycom 必须在 pages.json 的最外层,且写在 pages 之前。配置后必须重启 dev server,热更新不生效。
11.3 TypeScript 严格模式
uni-preset-vue vite-ts 模板默认开了 TS 严格模式,any 会报警告。建议前期保持严格,定义好接口类型,后期维护轻松。真嫌烦可以 tsconfig.json 里把 strict: false。
11.4 安卓打包体积
第一次云打包安卓 apk 出来 30+ MB,正常现象(含 uni-app 运行时 + V8 引擎)。要瘦身:用「自有证书」打包、按需配置 manifest.json 的模块(不要全选)。
十二、写在最后
这一篇把 App 端的项目骨架搭起来,配置好 uview-plus、ZPaging、路径别名、环境配置,目录约定也定下来了。这套结构能撑住至少到 v1.0 发布,业务增长再按需拆分。
回顾一下这一篇的关键决定:
uni-preset-vue vite-ts:Vite + TS,开发体验最好
uview-plus easycom:写组件名即用,按需打包
pages.json 是地图:路由 + 元信息一体
config/index.ts:用
import.meta.env.PROD切换 baseUrlZPaging:长列表用 ZPaging,别用 scroll-view
rpx + margin:不用 gap,跨端兼容
下一篇开始写第一个页面:登录页。涉及 uview-plus 表单组件、表单校验、token 存取、跳转逻辑。
夜雨聆风