ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

LikeShop 小程序端二开:前端工程结构、接口封装与登录态处理

LikeShop 小程序端二开:前端工程结构、接口封装与登录态处理 一、前言在之前的系列文章中我从服务端视角写了 LikeShop 的分层架构、支付模块和数据库设计。但后台收到不少读者留言说“服务端搞明白了但小程序端的前端代码还是不知道怎么改”。这让我意识到移动端作为用户直接接触的入口它的工程结构和数据流转同样值得单独讲一篇。LikeShop 的移动端基于 uni-app 构建一套代码可以编译出微信小程序、H5、安卓 App、iOS App 等多个终端。这种“一套代码多端发布”的能力既是它的优势也是二开时的难点——你需要理解哪些代码是多端共用的哪些需要做条件编译。这篇文章就从 uniapp 工程的目录结构出发把接口封装机制和登录态处理链路拆开讲清楚。内容基于 LikeShop 单商户版 v3.5.1 的 uniapp 源码。二、uniapp 工程整体结构根目录布局移动端源码位于根目录的uniapp/文件夹下与server/服务端、admin/管理后台前端、pc/PC 前台并列。在完整源码中只有server/需要部署到服务器其他几个前端工程是前后端分离的独立项目本地开发时通过 npm 运行编译产物再部署到对应的静态资源目录。uniapp/内部的结构大致如下uniapp/ ├── config/ │ └── app.js # 服务端地址等全局配置 ├── api/ # 接口定义层 ├── components/ # 全局公共组件 ├── pages/ # 页面文件 ├── static/ # 静态资源图片、字体等 ├── store/ # 状态管理 ├── utils/ # 工具函数 ├── .env.development # 开发环境变量 ├── .env.production # 生产环境变量 ├── main.js # 应用入口 ├── App.vue # 应用根组件 ├── pages.json # 页面路由与导航栏配置 ├── manifest.json # 多端打包配置 └── package.json # 依赖与脚本这里有两个文件需要特别关注pages.json定义了所有页面的路由、导航栏样式和 tabBar 配置manifest.json则存储了小程序 AppID、H5 基础路径、APP 图标等各端打包参数。二开时如果新增页面必须先在pages.json中注册路由否则页面无法被访问。多端条件编译uni-app 的条件编译是二开时最常用的特性之一。在 LikeShop 的代码中你会看到大量类似这样的写法// #ifdef MP-WEIXIN// 微信小程序专属逻辑// #endif// #ifdef H5// H5 专属逻辑// #endif微信小程序的登录流程和 H5 完全不同——小程序通过wx.login()获取 code 再换取 openid而 H5 在微信环境下走公众号授权。条件编译让两套逻辑可以共存于同一个文件编译到不同平台时自动裁剪。二开时添加平台专属逻辑也应该用条件编译包裹不要直接写 if/else 判断平台那样会导致代码在编译后被完整打包进所有端。环境变量配置uniapp/目录下有两个环境变量文件.env.development和.env.production。官方仓库中提供的是.env.development.example和.env.production.example需要复制并去掉.example后缀才能生效。关键配置项是服务端接口地址VITE_APP_BASE_URLhttp://你的服务端域名官方文档特别强调不要使用 localhost因为 uni-app 在多端运行时对本地地址的解析不一致。本地开发时也要用一个可访问的域名可以通过 hosts 映射到 127.0.0.1。三、接口封装机制为什么需要封装LikeShop 的移动端有几十个接口调用场景——商品列表、订单详情、用户信息、支付请求等。如果每个页面都直接用uni.request()会带来三个问题请求头不统一token 漏传、错误处理重复、后端接口变更时改动分散。LikeShop 的解决方案是在utils/目录下封装一个统一的请求模块所有接口调用都经过它。请求封装的核心结构LikeShop 的请求封装不同版本文件名可能不同通常在utils/request.js或common/request.js中大致遵循以下结构// utils/request.js示意结构constrequest(options){// 1. 拼接基础 URLconsturlconfig.baseUrloptions.url// 2. 构建请求头自动注入 tokenconstheader{Content-Type:application/json,version:APP_VERSION,...options.header}consttokenuni.getStorageSync(token)if(token){header[token]token}// 3. 发起请求returnnewPromise((resolve,reject){uni.request({url,method:options.method||GET,data:options.data,header,success:(res){// 4. 统一处理响应if(res.data.code1){resolve(res.data)}elseif(res.data.code-1){// token 失效跳转登录uni.navigateTo({url:/pages/login/login})reject(res.data)}else{uni.showToast({title:res.data.msg,icon:none})reject(res.data)}},fail:reject})})}exportdefaultrequest这段代码体现了几个关键设计Token 自动注入。每次请求前从本地存储读取 token 并放入请求头业务页面不需要关心 token 的存在。统一响应处理。LikeShop 的接口返回格式为{ code, show, msg, data }其中code为 1 表示成功0 表示失败-1 表示需要重新登录。请求封装层统一处理这三种情况-1时自动跳转登录页业务页面只需要处理成功逻辑。Promise 风格。封装返回 Promise页面中可以用await request({...})的方式调用代码更简洁。接口定义层的组织方式在请求封装之上LikeShop 通常还有一个API 定义层api/目录把接口按模块分类组织api/ ├── goods.js # 商品相关接口 ├── order.js # 订单相关接口 ├── user.js # 用户相关接口 ├── cart.js # 购物车接口 └── pay.js # 支付接口以订单模块为例api/order.js中会定义importrequestfrom/utils/requestexportfunctiongetOrderList(params){returnrequest({url:/shopapi/order/orderList,data:params})}exportfunctiongetOrderDetail(id){returnrequest({url:/shopapi/order/orderDetail,data:{id}})}exportfunctioncreateOrder(data){returnrequest({url:/shopapi/order/create,method:POST,data})}页面中调用时只需要import { getOrderList } from /api/order不关心 URL 拼接和请求头处理。二开时如果后端接口路径变了只需要改api/目录下的定义不需要全项目搜索替换 URL。四、登录态处理登录流程LikeShop 支持多种登录方式手机号码密码登录、手机短信验证码登录、微信授权登录等。其中微信小程序授权登录是移动端最常用的方式也是二开时最容易踩坑的环节。小程序端的登录流程如下用户点击登录 → wx.login() 获取 code ↓ 将 code 发送到服务端 /shopapi/user/mnpLogin ↓ 服务端调用微信接口换取 openid 和 session_key ↓ 服务端查找或创建用户生成 token 返回 ↓ 前端将 token 存储到本地 ↓ 后续请求自动携带 token这里的关键是code 只能使用一次且有效期很短。前端拿到 code 后应立刻发送给服务端不要在本地缓存。二开时如果遇到“登录返回 401”或“code 无效”的报错通常是因为 code 被重复使用或超时了。Token 的存储与管理LikeShop 使用Pinia作为状态管理库Vue 2 版本使用 Vuex在 store 中管理用户信息和 token。典型的 Pinia store 结构如下// store/user.js示意结构import{defineStore}frompiniaimport{ref}fromvueexportconstuseUserStoredefineStore(user,(){consttokenref(uni.getStorageSync(token)||)constuserInforef(uni.getStorageSync(userInfo)||{})functionsetToken(val){token.valueval uni.setStorageSync(token,val)// 持久化}functionsetUserInfo(info){userInfo.valueinfo uni.setStorageSync(userInfo,info)}functionlogout(){token.valueuserInfo.value{}uni.removeStorageSync(token)uni.removeStorageSync(userInfo)}return{token,userInfo,setToken,setUserInfo,logout}})Token 的持久化存储在uni.setStorageSync中。小程序端的 Storage 是持久化的除非用户主动删除小程序或系统清理缓存。H5 端则存储在 localStorage 中。不要只把 token 存在 Pinia 的 ref 里页面刷新H5 场景会导致内存状态丢失用户会被迫重新登录。Token 失效的自动处理Token 失效是二开时最常见的登录态问题。LikeShop 的接口在 token 过期时会返回code: -1请求封装层捕获到-1后会执行以下操作清除本地存储的 token 和用户信息跳转到登录页登录成功后返回原页面这里有一个容易被忽略的细节并发请求时的重复跳转。如果页面同时发起了 3 个请求token 都失效了3 个请求都会收到-1如果不加控制登录页会被跳转 3 次。解决方案是加一个全局状态锁letisRedirectingfalseif(res.data.code-1!isRedirecting){isRedirectingtrueuni.removeStorageSync(token)uni.navigateTo({url:/pages/login/login,complete:(){isRedirectingfalse}})}多端登录态的差异LikeShop 的一个核心设计是“全终端数据打通”——用户在 H5 下的订单小程序端的订单列表里能同步看到。这意味着登录态需要在服务端统一管理而不是绑定在某个终端上。服务端的UserTokenService负责管理 token 的生成和更新。当用户在某个终端登录时服务端会在ls_user_session表中记录该终端的 token同一用户在不同终端登录时会生成不同的 token但都指向同一个 user_id。这个设计对二开的影响是如果你在某个端修改了登录逻辑要确保服务端的 token 校验机制不受影响。比如在小程序端新增了“一键登录”功能服务端生成的 token 仍然需要符合统一的格式和有效期规则。五、二开常见问题与避坑坑一环境变量文件没有去掉 .example 后缀这是 uniapp 开发中最常见的启动失败原因。uniapp/目录下提供的是.env.development.example必须复制一份并去掉.example后缀否则编译时会报错“请参考官方文档在 .env 文件下配置请求域名”。坑二新增页面忘记注册路由uni-app 的页面必须在pages.json中注册才能访问。二开时新增页面后检查pages.json的pages数组中是否包含了新页面的路径。如果是 tabBar 页面还需要在tabBar.list中同步配置。坑三token 失效后登录页循环跳转如果登录页本身也需要请求接口比如获取验证码当 token 失效时登录页的请求也会返回-1导致登录页反复跳转自己。解决方案是在请求封装中排除登录相关的接口或者在登录页的onLoad中先清除失效 token。坑四小程序端 wx.login 的 code 复用wx.login()获取的 code 只能使用一次。如果二开时在多个地方调用了wx.login()或者把 code 缓存起来多次使用都会导致登录失败。每次登录都需要重新调用wx.login()获取新的 code。坑五H5 端刷新后状态丢失H5 端页面刷新会重置 Pinia 的内存状态。如果 token 只存在 Pinia 的 ref 中而没有持久化到 localStorage刷新后用户会变成未登录状态。确保 token 和关键用户信息都通过uni.setStorageSync持久化。六、总结LikeShop 小程序端的工程结构遵循了 uni-app 的标准组织方式pages/存页面api/定义接口utils/request.js封装请求store/管理登录态。二开时只要记住三条原则第一接口调用走api/层。不要在页面中直接写uni.request所有接口定义集中在api/目录下便于维护。第二token 通过请求封装自动注入。业务页面不需要手动处理 token请求封装层负责从 Storage 读取并放入请求头。第三登录态状态存在 Pinia Storage 双层。内存状态用于响应式渲染Storage 用于持久化。页面刷新后从 Storage 恢复。把这套机制理解清楚小程序端的二开就会顺畅很多。本文基于 LikeShop 单商户版 v3.5.1 uniapp 源码及官方开发文档整理不同版本的文件路径和命名可能略有差异请以实际源码为准。
返回列表