ARTICLE · 1036197
�� 小程序工程地基:uni-app + Tailwind + Pinia 一把梭
后端跑起来了,数据有地方存了,接下来轮到门口的门店——小程序前端。
工程初始化这件事,决定你后面三个月是「丝滑开发」还是「天天跟配置打架」。
这篇就把我搭 uni-app 前端地基的那八步,原原本本讲给你听。
🤔 你有没有被「从零建前端」劝退过?
新建一个项目,光是配构建、配路由、配状态管理、配网络请求,就能耗掉一整个下午。
更惨的是配完还跑不起来,报一堆看不懂的错,最后只能「面向搜索引擎编程」。
我当初搭这个小程序前端时,就给自己定了一条铁律:地基一定要一次到位,后面只管往上盖。于是有了下面这套八步法。
我是谁,以及我为什么要折腾这个
我是这个项目的作者,一个爱打网球、也爱写代码的人。
平时喜欢把训练里那些零碎的感受记下来,也享受用代码把「想偷懒」变成「真省事」的过程。
如果你也在技术这条路上往前走,关注我,愿我们能彼此陪伴,一起成为更好的自己 🌱
🎯 这篇文章能帮你解决什么
读完你会收获:
🔹 为什么选 uni-app,而不是原生小程序或纯 React
🔹 底部四个 Tab 怎么摆、目录怎么分层才不烂
🔹 Web 版的橄榄绿/青柠主题色怎么一键沿用
🔹 网络层 + 微信登录链路,怎么一次性打通
🧰 核心原理:前端工程化到底在解决啥
说人话,前端地基就四件事:
🔹目录分层:页面、组件、状态、接口、类型,各住各的房间
🔹状态管理:跨页面共享的数据(登录态、日记列表)得有个「中枢」
🔹网络层:所有请求统一出口,JWT 自动带、401 自动踢
🔹登录链路:打开小程序就能无感登录,用户啥都不用点
这四件套齐了,后面写业务页面就是「填空」,爽得很。
🛠️ 实战演示:八步把地基打牢
🔹 第一步:uni-app 工程初始化
用官方脚手架一把拉起 Vue 3 + Vite + TS 的骨架:
# 拉官方 vite-ts 预设,编译目标直接锁微信小程序npx degit dcloudio/uni-preset-vue#vite-ts miniapppnpm install# 起本地编译,丢进微信开发者工具就能预览pnpm dev:mp-weixin
选 uni-app 而不是原生小程序,是因为它用Vue 3 SFC,跟我原来的组件思维最贴近,还能顺手编译个 H5 端给后端联调用。
🔹 第二步:目录分层 + 底部 TabBar
四个 Tab 一锤定音:日记 / 装备 / 统计 / 我的。目录也按职责切干净:
src/├── pages/ # 四个 Tab 页 + 登录页├── components/ # 公共组件(TopBar/Section/Toast…)├── stores/ # Pinia 状态├── types/ # 类型定义(types.ts 迁移)├── services/ # API 封装(uni.request)├── styles/ # 全局样式 / Tailwind└── static/ # TabBar 图标等
pages.json 里把 tabBar 的选中色直接锁成青柠主题,从一开始就统一视觉。
🔹 第三步:Tailwind 集成 + 主题色沿用
Web 版那套橄榄绿/青柠/米白主题色,我可舍不得扔。直接把原 tailwind.config 的色值搬过来:
// tailwind.config.js —— 主色一个字不差地沿用colors: {lime: '#C8DA2B', // 主强调色(青柠)limeSoft:'#F0F5CE', // 青柠浅底olive: '#242B1F', // 主深色(橄榄绿)paper: '#F2F2EF', // 米白背景ink: '#171B14', // 文字墨色}
圆角也定了:card: 20px、hero: 28px。小程序端没 hover: 等伪类,按 preset 约定走就行。
🔹 第四步:放弃 Vant,改用 Tailwind 自定义组件
这里踩过一个坑,得啰嗦一句:Vant 是原生小程序组件,塞不进 Vite/Vue 的编译流,要么复制 500 个文件污染源码,要么依赖微信工具「构建 npm」——后者在纯命令行 CI 里根本跑不了。
所以我干脆不用 Vant,用 Tailwind 把原 UI.tsx 的组件重写一遍:TopBar→原生导航栏、Section→bg-white rounded-card 卡片、Toast→uni.showToast。干净、可控、跨端一致。
🔹 第五步:types.ts 迁移,命名对齐后台
Web 版的 types.ts 直接搬过来当基准,但字段命名要对齐后台 B1 的蛇形风格(createdAt→created_at、buyDate→buy_date)。同时拆出 *Response(带 id)和 *Create/*Update(入参)两套类型,前后端从此对得上话。
🔹 第六步:Pinia 搭起状态中枢
对应原 Web 版的 React Context,我用 Pinia 建了五个 store:
// stores/auth.ts —— 登录态的「中枢」state: () => ({ token: '', user: null })// 负责:取 code → 换 JWT → 取用户 → 持久化
diary / gear / weight 三个 store 先占位,等 Phase 2 填业务逻辑;settings 管主题和金额隐私开关。所有 store 在 main.ts 里 app.use(createPinia()) 一次性注册。
🔹 第七步:网络层封装,JWT 自动带
所有请求走一个出口,省得每个页面手写 uni.request:
// services/request.ts —— 统一出口async function request(options) {// 每次请求自动从 store 里掏 JWT 塞进请求头header['Authorization'] = `Bearer ${authStore.token}`const res = await uni.request(options)if (res.statusCode === 401) {// 登录失效:清态 + 提示,让用户重新登录authStore.logout()uni.showToast({ title: '登录已失效', icon: 'none' })}return res.data}
🔹 第八步:对接登录,打开即登录
最后把链路焊死:wx.login() 拿临时 code → 发给 /api/auth/login → 后端换 JWT → 存起来。
// stores/auth.ts 的 ensureLogin()async function ensureLogin() {if (token) return // 已有 token,直接复用const { code } = await uni.login() // 小程序里就是 wx.loginconst { token, user } = await login(code) // 换 JWTpersist(token, user) // 持久化,重启也不用重登}
App.vue 的 onLaunch 里先恢复登录态,没 token 就自动 ensureLogin()——用户全程无感,打开就是已登录。
⚠️ 再说几个容易翻车的点
🔹 坑 1:Vant 引入污染工程
前面说了,Vant 硬塞进 uni-app 要么污染源码要么破坏 CI。老老实实用 Tailwind 自定义组件,后期反而更省心。
🔹 坑 2:TabBar 图标与选中色不匹配
四个 Tab 的图标得准备「正常/选中」两套,选中色锁死主题青柠。漏了选中态,用户根本不知道自己在哪。
🔹 坑 3:JWT 头名前后端对不上
后端后来因为魔搭网关占用了 Authorization,统一改用 X-Auth-Token。网络层注入的头部必须跟着后端走,否则所有请求 401。(这个坑在后面部署篇细说。)
🔹 坑 4:静默登录的时机
wx.login 的 code 只有 5 分钟有效期,onLaunch 里先恢复旧 token、再决定要不要重新登录,顺序搞反就会频繁弹登录。
💡 我到底该不该用 uni-app?
🔸适合用的情况:你本来就是 Vue 技术栈、想要一套代码多端编译、不想写原生小程序的双线程模板。
🔸可以不选的情况:应用重度依赖原生小程序能力(自定义相机、复杂 Canvas)、团队是 React 栈(那选 Taro 更顺)、或者对包体积极致敏感。
我这套是 Vue 栈 + 要兼顾 H5 联调,uni-app 是最舒服的那条路。
🎁 总结一下
前端地基看着琐碎,其实就是八步:建工程 → 分目录/TabBar → 接 Tailwind → 弃 Vant → 迁类型 → 搭 Pinia → 封网络层 → 通登录。
把这八步走稳,后面写日记页、装备页就是「填空游戏」。地基牢,楼上才稳。
如果你觉得这篇「人话版」前端搭建有点用,别藏着掖着,点赞收藏加关注,防止下次想找的时候找不到~
你建前端工程时还踩过哪些坑?评论区唠一唠,一起避坑才是真朋友 🎯