在跨端开发成为主流的今天,Uniapp + Vue3 + PHP的组合已经成为中小团队交付小程序项目的“工业级标配”。一套代码,编译到微信小程序、H5、App,后端用 PHP 承接业务逻辑与数据,既能满足快速上线,又能保证后期可维护。本文将以一份标准的Uniapp 小程序源码为核心,系统性拆解其工程结构、Vue3 组合式 API 实战、微信小程序适配要点,以及 PHP 后端如何设计一套“多端通用”的接口体系。
源码及演示:y.wxlbyx.icu
一、为什么选择 Uniapp + Vue3 + PHP 这套技术栈
很多团队在小程序起步阶段会在“原生开发”和“跨端框架”之间犹豫,而 Uniapp 在以下几个维度上非常契合商业项目:
- 多端复用:同一套
pages/、components/、store/,一键编译到微信小程序、支付宝小程序、H5、Android/iOS App,极大降低维护成本。 - Vue3 的开发体验:
<script setup>、Composition API、响应式系统(ref/reactive),让复杂业务的状态管理更清晰,组件复用性更强。 - PHP 后端的普适性:LAMP/LNMP 环境成熟,Laravel / ThinkPHP / Webman 等框架生态完善,中小项目部署成本低,招人成本也低。
- 源码交付友好:Uniapp 源码结构清晰,PHP 接口分层明确,非常适合作为“成品源码”进行二次销售、二开或 SaaS 化改造。
因此,一套标注为“Uniapp小程序源码 - Vue3+PHP多端通用”的工程,本质上应该是一个“开箱即用、可跨端、可私有化部署”的商业级解决方案。
二、Uniapp 小程序源码的标准目录结构(Vue3 版)
拿到一份规范的 Uniapp 源码,根目录通常呈现如下结构(以微信小程序为主要编译目标):
/root ├── src/ # 源码主目录(uniapp 标准) │ ├── pages/ # 业务页面 │ │ ├── index/ # 首页 │ │ ├── user/ # 用户中心 │ │ ├── goods/ # 商品/内容模块 │ │ └── order/ # 订单流程 │ ├── components/ # 公共组件(Vue3 SFC) │ │ ├── base/ # 基础组件:按钮、卡片、弹窗 │ │ └── biz/ # 业务组件:商品卡片、地址选择器等 │ ├── composables/ # 组合式函数(hooks) │ │ ├── useUser.ts # 用户信息管理 │ │ ├── useRequest.ts # 请求封装 │ │ └── usePayment.ts # 支付逻辑 │ ├── store/ # Pinia 状态管理 │ │ ├── user.ts │ │ ├── cart.ts │ │ └── index.ts │ ├── utils/ # 工具方法 │ │ ├── request.ts # uni.request 封装 │ │ ├── auth.ts # token / openid 处理 │ │ └── env.ts # 多端环境变量 │ ├── static/ # 静态资源 │ │ ├── images/ │ │ └── icons/ │ ├── manifest.json # uniapp 应用配置 │ ├── pages.json # 页面路由 & 窗口样式 │ ├── uni.scss # 全局样式变量 │ └── App.vue # 应用入口(生命周期) │ ├── server-php/ # PHP 后端工程 │ ├── app/ │ │ ├── Controllers/ │ │ ├── Models/ │ │ ├── Middleware/ │ │ └── Routes/ │ ├── config/ │ ├── public/index.php │ └── .env └── package.json源码验收标准:
pages.json中是否配置了usingComponents(原生小程序组件兼容)- 是否存在
composables/目录(Vue3 项目的重要标志)- PHP 端是否有统一的
BaseController与返回格式
三、Vue3 在 Uniapp 中的工程化实践
3.1<script setup>成为主流
在 Uniapp 的 Vue3 工程中,<script setup>基本替代了 Vue2 的export default。以一个简单的商品列表页为例:
<!-- pages/goods/list.vue --> <template> <view class="goods-list"> <GoodsCard v-for="item in list" :key="item.id" :data="item" /> <uni-load-more :status="loadStatus" /> </view> </template> <script setup lang="ts"> import { ref, onMounted } from 'vue' import { onReachBottom } from '@dcloudio/uni-app' import { getGoodsList } from '@/api/goods' import GoodsCard from '@/components/biz/GoodsCard.vue' const list = ref([]) const page = ref(1) const loadStatus = ref<'more'|'loading'|'noMore'>('more') const loadData = async () => { if (loadStatus.value === 'noMore') return loadStatus.value = 'loading' const res = await getGoodsList({ page: page.value }) list.value.push(...res.data.list) page.value++ loadStatus.value = res.data.hasMore ? 'more' : 'noMore' } onMounted(loadData) onReachBottom(loadData) </script>优势:逻辑聚合、类型推导友好、生命周期与 uniapp 钩子(onLoad,onShow,onReachBottom)天然融合。
3.2 Composables:业务逻辑的“积木”
将通用逻辑抽离为composables,是 Vue3 工程化的核心。例如useRequest.ts:
// composables/useRequest.tsimport{ref}from'vue'exportfunctionuseRequest<T>(api:(...args:any[])=>Promise<any>){constdata=ref<T|null>(null)constloading=ref(false)consterror=ref<string|null>(null)construn=async(...args:any[])=>{loading.value=trueerror.value=nulltry{constres=awaitapi(...args)data.value=res.datareturnres}catch(err:any){error.value=err.message||'请求失败'throwerr}finally{loading.value=false}}return{data,loading,error,run}}在页面中使用:
const{data:userInfo,run:fetchUser}=useRequest(getUserInfo)fetchUser()这种写法在多端(微信小程序 / H5 / App)中完全一致,极大降低了二开门槛。
3.3 Pinia 替代 Vuex
Uniapp Vue3 项目中,Pinia 已成为状态管理首选:
// store/user.tsimport{defineStore}from'pinia'import{ref}from'vue'exportconstuseUserStore=defineStore('user',()=>{consttoken=ref('')constuserInfo=ref(null)functionsetToken(val:string){token.value=val uni.setStorageSync('token',val)}return{token,userInfo,setToken}})相比 Vuex,Pinia 去除了mutations,更符合 Vue3 的响应式心智模型,且在 H5 和小程序中的表现一致。
四、微信小程序适配的关键点(Uniapp 特有)
虽然 Uniapp 抹平了大量差异,但在微信小程序中仍有一些必须处理的细节:
4.1 用户登录与code2Session
微信小程序必须通过wx.login获取code,再由后端换取openid:
// utils/auth.tsexportfunctionwxLogin(){returnnewPromise((resolve,reject)=>{uni.login({provider:'weixin',success:(res)=>{// 发送 res.code 到 PHP 后端resolve(res.code)},fail:reject})})}PHP 后端需要调用微信接口:
// 伪代码$url="https://api.weixin.qq.com/sns/jscode2session";$params=['appid'=>env('WX_APPID'),'secret'=>env('WX_SECRET'),'js_code'=>$code,'grant_type'=>'authorization_code'];// 返回 openid / session_key4.2 支付统一下单
微信小程序支付必须由后端发起:
// composables/usePayment.tsexportfunctionusePayment(){constcreateOrder=async(orderId:string)=>{constres=awaituni.request({url:'/api/pay/create',method:'POST',data:{order_id:orderId}})// 调起微信支付uni.requestPayment({...res.data.payment_params,success:()=>uni.showToast({title:'支付成功'}),fail:()=>uni.showToast({title:'支付失败',icon:'error'})})}return{createOrder}}PHP 端需生成prepay_id,并按微信规则签名返回前端所需参数。
4.3 分包加载与体积控制
微信小程序主包限制 2MB(实际开发中建议控制在 1.5MB 内),Uniapp 通过pages.json配置分包:
{"subPackages":[{"root":"pages/sub/","pages":[{"path":"detail","style":{"navigationBarTitleText":"详情"}}]}]}同时,将大型第三方库(如echarts)放入分包,或使用小程序专用版本(如ec-canvas)。
五、PHP 后端:多端通用的接口设计
一套“多端通用”的 PHP 后端,核心在于接口与平台解耦。
5.1 统一返回格式
// app/Controllers/BaseController.phpclassBaseController{protectedfunctionjson($data=[],$code=0,$msg='success'){returnjson_encode(['code'=>$code,'msg'=>$msg,'data'=>$data,'timestamp'=>time()],JSON_UNESCAPED_UNICODE);}}前端无论来自微信小程序、H5 还是 App,均按此格式解析。
5.2 平台识别与路由适配
通过请求头或参数区分平台:
$platform=$_SERVER['HTTP_PLATFORM']??$_GET['platform']??'unknown';switch($platform){case'weixin':// 微信小程序逻辑(openid)break;case'h5':// H5 逻辑(session/cookie)break;case'app':// App 逻辑(device_id)break;}5.3 鉴权中间件
使用 JWT 或自定义 Token,在中间件中统一校验:
// app/Middleware/AuthMiddleware.phppublicfunctionhandle($request,Closure$next){$token=$request->header('Authorization');if(!$this->verifyToken($token)){return$this->json([],401,'未授权');}return$next($request);}5.4 数据库设计与多端兼容
- 用户表:
users(id, openid, unionid, phone, platform) - 订单表:
orders(id, user_id, amount, status, platform) - 日志表:
logs(id, user_id, action, platform, created_at)
通过platform字段区分来源,方便后续统计与对账。
六、多端编译与条件编译实战
Uniapp 提供了强大的条件编译能力,解决各端差异:
<template> <!-- #ifdef MP-WEIXIN --> <button open-type="getPhoneNumber" @getphonenumber="onGetPhone">授权手机号</button> <!-- #endif --> <!-- #ifdef H5 --> <button @click="onInputPhone">输入手机号</button> <!-- #endif --> </template> <script setup> // #ifdef MP-WEIXIN const onGetPhone = (e) => { /* 微信逻辑 */ } // #endif // #ifdef H5 const onInputPhone = () => { /* H5 逻辑 */ } // #endif </script>在utils/env.ts中封装平台判断:
exportconstisWeixin=process.env.VUE_APP_PLATFORM==='mp-weixin'exportconstisH5=process.env.VUE_APP_PLATFORM==='h5'七、常见坑点与调试技巧
- 微信小程序不支持 DOM/BOM:不能使用
document、window,需改用uni.createSelectorQuery。 - 样式隔离问题:微信小程序默认样式隔离,Uniapp 中建议使用
scoped+deep选择器。 - API 差异:
uni.navigateTo在小程序中受页面栈限制(最多 10 层),H5 中无此限制。 - 真机调试:微信开发者工具无法完全模拟真机表现,务必使用
vConsole或在 PHP 后端记录详细日志。 - HTTPS 强制:微信小程序要求所有接口必须为 HTTPS,PHP 后端需配置 SSL 证书。
八、源码交付与二开建议
一份合格的Uniapp 小程序源码交付物应包括:
- 前端源码:完整
src/目录,包含composables/、store/、条件编译示例。 - 后端源码:PHP 工程,含
.env.example、数据库 SQL、部署文档。 - 接口文档:Markdown 或 Swagger 格式的 API 说明。
- 环境配置:
manifest.json、pages.json、微信小程序appid占位配置。 - 换肤指南:颜色变量位置、图标替换路径、广告位 ID 配置说明。
Uniapp 小程序源码 - Vue3+PHP多端通用不仅仅是一个标题,它代表了一种工程化、标准化的交付形态:
- 前端:以 Vue3 组合式 API 为核心,通过
composables和 Pinia 实现逻辑复用与状态管理,利用条件编译适配多端。 - 后端:PHP 采用分层架构,接口统一返回,通过平台标识实现多端兼容,保障数据安全与扩展性。
- 交付:结构清晰、文档完备,既适合直接上线,也适合二次开发与源码交易。
对于开发者而言,掌握这套架构,意味着你不仅能快速交付一个微信小程序,更能以最小的成本覆盖 H5、App 等多个流量入口;对于购买源码的用户而言,这意味着更低的学习成本、更高的可维护性,以及真正的“一次开发,多端运行”。