基于Vue的uniapp微信小程序初版本搭建实践指南
简介基于Vue.js与uniapp打造的微信小程序前端初版设计源码面向小程序入门开发者或有跨端项目需求的工程师提供一套可直接借鉴的前端工程骨架与组件化开发思路能帮助快速理解uniapp项目的目录组织与基本开发流程。压缩包共173个文件体积约1.29MB以93个Vue组件、46个JavaScript文件、10个SCSS样式为主体并包含JSON配置、PNG/GIF图标、HTML模板及许可文件等其中Vue组件承载页面模块JS文件处理逻辑与交互SCSS负责统一样式文档配置则支撑工程运行与规范管理。资源已有464人浏览/学习适合用作调研uniapp项目结构或快速搭建初版微信小程序时的参考蓝本。从内容预览看内含异步校验、图片裁剪、富文本解析等常用工具模块并带有移动端图标和跨端HTML模板可减少重复造轮子加之ESLint忽略文件与开源许可证工程约束清晰便于在此基础上按业务需求扩展和维护。1. 基于Vue的uniapp微信小程序前端初版本动手前先分清楚三层边界接到“初版本”需求时通常意味着一个月后要有一个能演示、能体验、能收集反馈的版本后端接口可能只定了一部分。选择基于 Vue 的 uni-app 做微信小程序是因为一份代码能同时产出微信小程序和 H5前端团队不用为两个端各维护一套。但初版最容易翻车的不是页面样式而是三件事登录态链路没闭环、请求层到处重复写、H5 与微信端的能力差异到联调时才暴露。这篇内容按“工程初始化 → 壳配置 → 数据通道 → 功能坑位 → 打包自检”的顺序把一套能直接复用的初版搭法讲清楚。适合正在搭建小程序第一版的前端也适合要把 Vue2 老项目迁到 Vue3 的团队参考。2. 技术选型与初始化Vue3、Vue2 与 uni-app CLI 怎么定2.1 Vue3 与 Vue2 在 uni-app 里的差异影响初版的代码组织方式uni-app 同一套代码可以运行在小程序端、H5 端与 App 端底层是把 Vue 组件编译成各端可执行的代码。编译链路决定了一件事Vue2 版本走的是 webpackVue3 版本默认走 vite两者在依赖处理、构建速度和 HMR 体验上差别明显。初版新项目没有历史包袱时直接用 Vue3 组合式 API原因是页面逻辑可以按业务维度拆成函数例如把登录、获取用户信息、刷新 token 收敛到同一个模块里比选项式分散写更贴近小程序的页面组织方式。团队如果从 Vue2 迁过来uniapp vue2转vue3方法并不是把 package.json 里的 vue 版本改掉就行。下面这张表列了最容易踩的四个差异点迁移时逐条对照环节Vue2 习惯写法Vue3 需要调整响应式数据data 返回对象this.xxx 访问ref/reactive 定义模板里自动解包逻辑里要 .value生命周期onLoad/onShow 直接挂在组件选项里从 dcloudio/uni-app 按需 import自定义事件this.$emit(eventName, payload)defineEmits 声明后在 setup 里调用过滤器 filter模板中直接使用已移除改用 computed 或方法初版没有历史包袱时建议直接选用 Vue3 分支。Vue2 的选项式写法在小程序多页面场景下状态分散在 data、computed、watch 里页面一多维护成本明显上升Vue3 组合式 API 可以把一个页面的请求、渲染、交互集中到 setup 区块逻辑跳转时少翻几个文件。2.2 用 CLI 创建 uni-app 工程并安装依赖避免 IDE 版本锁定工程创建用官方的模板仓库拉取即可常见做法是# 拉取 Vue3 vite 分支模板项目名自定义 npx degit dcloudio/uni-preset-vue#vite my-uniapp-app cd my-uniapp-app # 安装依赖 npm install # 启动微信小程序端编译 npm run dev:mp-weixindegit 的作用是直接下载 GitHub 模板并去掉 git 历史比 git clone 少一步清理成本的环节。vite 分支对应 Vue3webpack 分支对应 Vue2选错分支会导致后续按 Vue3 语法写却编译报错。npm install 装依赖阶段常见的问题是 node 版本过高导致 esbuild 或 sass 编译失败优先把 Node 切到 16 或 18 的 LTS 版本再装。启动 dev:mp-weixin 后产物输出在 dist/dev/mp-weixin微信开发者工具里导入该目录即可看到页面。package.json 里默认脚本对应四个常用动作{ scripts: { dev:mp-weixin: uni -p mp-weixin, build:mp-weixin: uni build -p mp-weixin, dev:h5: uni, build:h5: uni build } }带 dev 前缀的是监听模式改代码会增量编译build 是产物构建发布时使用。-p 指定平台不传默认编译到 H5。这里要提醒一句微信开发者工具导入的是 dist 目录不是项目根目录。提示npm install 阶段遇到 node-sass 之类报错先看 Node 版本官方模板对 Node 16/18 兼容最好。2.3 目录约定与页面传参初版就该定死的两个规范工程初始化后src 下的目录结构建议直接按职责划分避免所有代码堆在 pages 里src/ ├── pages/ # 页面目录一个子目录对应一个页面 ├── components/ # 公共组件配合 easycom 免 import ├── api/ # 接口定义按模块拆文件 ├── utils/ # request 封装与工具函数 ├── store/ # 全局状态 ├── static/ # 图片、字体等静态资源 ├── App.vue # 应用入口globalData 与全局生命周期 ├── main.js # 挂载入口 ├── pages.json # 路由、tabBar、导航栏配置 └── manifest.json # appid、权限说明等应用配置页面之间的跳转参数接收方式与 vue-router 的 query 类似// 列表页发起跳转参数拼在 url 上 uni.navigateTo({ url: /pages/detail/detail?id1001title encodeURIComponent(初版需求清单) }) // 详情页接收 export default { onLoad(options) { console.log(options.id) // 1001 console.log(options.title) // 初版需求清单 } }单向传参场景用 URL 即可但只能传字符串对象需要先 JSON.stringify 再用 encodeURIComponent 包一层否则特殊字符会把参数截断。超过几百字的内容或需要多处共享的数据不要走 URL放 store 或 Storage 更合适。这个小细节也是前端面试里经常追问的传参场景。3. manifest.json 与 pages.json把微信小程序的壳先配对3.1 manifest.json 的 mp-weixin 节点appid、用户授权说明与校验开关uniapp manifest配置是所有平台公共配置的入口微信小程序相关的配置集中在 mp-weixin 节点。新建工程后先要填的是 appid否则微信开发者工具里预览、真机调试、上传都会报 appid 不合法。测试阶段可以先不填用测试号顶替但涉及定位、支付、订阅消息这些能力时必须换成正式 appid。{ mp-weixin: { appid: wx1234567890abcdef, setting: { urlCheck: false, es6: true, minified: true }, usingComponents: true, permission: { scope.userLocation: { desc: 用于展示离你最近的可用门店 } } } }urlCheck 开发期设为 false可以跳过 request 合法域名校验方便直连本地后端调试。上传体验版前必须改回 true并在微信公众平台配置 request 合法域名否则线上接口全部被拦。permission 里的 desc 会在定位授权弹窗中直接展示给用户微信要求必须写清楚用途留空会直接拒绝授权。es6 和 minified 保持默认即可分别控制 ES6 转译与代码压缩。3.2 pages.json 路由与 tabBar启动页、导航栏与文字 tab 一次配好pages.json 同时承担路由表、窗口样式、tabBar 三份职责微信小程序的页面注册顺序直接决定启动页是谁。初版通常结构简单配置一个首页加一个“我的”页就够了{ pages: [ { path: pages/index/index, style: { navigationBarTitleText: 首页 } }, { path: pages/mine/mine, style: { navigationBarTitleText: 我的 } } ], globalStyle: { navigationBarTextStyle: black, navigationBarBackgroundColor: #ffffff, navigationBarTitleText: 初版小程序, backgroundColor: #f5f5f5 }, tabBar: { color: #999999, selectedColor: #07c160, backgroundColor: #ffffff, list: [ { pagePath: pages/index/index, text: 首页 }, { pagePath: pages/mine/mine, text: 我的 } ] } }pages 数组第一项就是小程序启动页修改启动页顺序直接调整这个数组。tabBar 的 pagePath 必须在 pages 中存在否则编译直接报“tabBar 页面不存在”。tabBar 列表项支持 iconPath 配置图标初版设计资源还没到位时可以像示例一样只放文字微信允许文字 tab。导航栏要自定义时在页面的 style 里加 navigationStyle: custom原生导航栏会消失页面顶部需要用状态栏高度手动补一块占位。关于“修改刚进入的加载页面”小程序冷启动时的品牌加载页是微信后台配置的前端改不了前端能改的是首屏内容渲染时机常见做法是在 App.vue 的 onLaunch 里做登录态检查配合页面骨架屏减少白屏感这部分在第 6 章展开。3.3 easycom 组件规则与 uni.scss 全局样式变量初版本会用到大量 uni-ui 或自定义组件如果每个页面都手动 import 一遍组件一多代码会很啰嗦。easycom 的机制是只要组件文件路径符合规则页面模板里直接用标签名即可无需 import 也无需注册。{ easycom: { autoscan: true, custom: { ^uni-(.*): dcloudio/uni-ui/lib/uni-$1/uni-$1.vue } } }autoscan 会对 components 目录按“components/组件名/组件名.vue”的约定自动扫描custom 里把 uni- 开头的标签映射到 uni-ui 包内路径。配好后页面里直接写uni-list、uni-card就能用。样式方面uni.scss 中定义的变量在任意页面 style 里直接可用公共颜色、圆角、间距都放这里比每页复制颜色值可靠。注意 App.vue 的全局样式中标签选择器在小程序端部分场景不生效不要用它覆盖组件内部样式。4. request 封装与 code 换 token初版数据通道一次跑通4.1 基于 uni.request 的请求层统一 baseURL、头部与错误收敛初版最常见的失败模式是每个页面直接调 uni.requesttoken 怎么带写三遍401 怎么处理写三遍改 baseURL 要全局搜索。正确做法是先封装一个 request 函数把 header 注入、超时、业务码判断、错误提示全部收敛到一处。// utils/request.js const BASE_URL https://api.example.com let isRedirecting false // 防止 401 时重复跳转登录页 export function request(options) { return new Promise((resolve, reject) { uni.request({ url: BASE_URL options.url, method: options.method || GET, data: options.data || {}, header: { Content-Type: application/json, Authorization: Bearer (uni.getStorageSync(token) || ) }, timeout: 15000, success: (res) { if (res.statusCode 200 res.data.code 0) { resolve(res.data.data) } else if (res.statusCode 401 || res.data.code 401) { handleUnauthorized() reject(new Error(登录已过期)) } else { uni.showToast({ title: res.data.message || 请求失败, icon: none }) reject(new Error(res.data.message)) } }, fail: (err) { uni.showToast({ title: 网络异常请稍后重试, icon: none }) reject(err) } }) }) } function handleUnauthorized() { uni.removeStorageSync(token) uni.removeStorageSync(userInfo) if (isRedirecting) return isRedirecting true uni.navigateTo({ url: /pages/login/login, complete: () (isRedirecting false) }) }业务约定是 code 为 0 时请求成功401 表示登录失效。token 每次请求从 Storage 读取而不是存内存变量是为了避免 storage 被外部清除后内存里还残留旧 token造成请求头不一致。isRedirecting 是跳转锁防止多个接口同时 401 时连续跳转登录页把页面栈打爆。fail 分支统一提示网络异常具体网络错误细节打印到 console 便于排查。4.2 微信小程序用 code 换 token一次性凭证的正确用法微信小程序的登录态链路是固定的前端调 wx.login 拿到临时 code把 code 交给后端后端拿 code 调微信的 code2session 接口换取 openid 和 session_key再生成业务 token 返回前端。这个 code 的有效期为五分钟且只能使用一次所以它只承担“请求换 token”的角色不能当作登录凭证保存。// pages/login/login.vue 中的登录方法 uni.login({ provider: weixin, success: async (loginRes) { const { code } loginRes try { const data await request({ url: /auth/login, method: POST, data: { code } }) uni.setStorageSync(token, data.token) uni.setStorageSync(userInfo, data.userInfo) uni.switchTab({ url: /pages/index/index }) } catch (e) { console.error(登录失败, e) } } })code 的获取必须通过 uni.login 而不是手动拼参数provider 固定为 weixin。后端接口返回的 token 要落 Storage再次冷启动时 App.vue 的 onLaunch 里先检查 token 是否存在存在则跳过登录页不存在才引导登录。这里有一个前端面试高频追问点token 有效期内的静默续期怎么做。常见做法是请求层捕获 401 后调用刷新 token 接口拿到新 token 再重放原请求初版本可以先降级为“401 直接回登录页”等用户量上来再补续期逻辑。提示code2session 的调用必须放在服务端前端直接调用会暴露小程序的 secret属于严重的安全事故。登录态相关的故障初版联调中常见的有三种故障现象直接原因处理方式每次冷启动都被弹回登录页token 没存 Storage或后端返回字段名与前端读取不一致全局检查 setStorageSync 与 getStorageSync 的 key 是否统一多个接口同时报 401页面反复跳转401 处理没有防重入加 isRedirecting 跳转锁H5 端 uni.login 无响应非微信小程序环境不支持 wx.login按平台分流H5 走账号密码或手机号验证码4.3 请求体格式与参数编码的前后端对齐请求层封装好后还要和后端对齐两个细节。第一是 Content-Type上面封装里默认 application/json后端如果按表单解析就会拿到一串空 body后端要求表单格式时header 改成 application/x-www-form-urlencodeddata 用 URLSearchParams 或查询串。第二是 GET 参数的编码uni.request 会把 GET 的 data 自动拼到 query 上但中文和特殊字符仍然建议手动 encodeURIComponent 一次避免后端收到被截断的参数。这两处对齐越早做联调阶段的返工越少。5. 初版功能坑位自查m3u8 播放、H5 获取定位与分享配置5.1 vue 播放 m3u8 的跨端方案微信原生 video 与 hls.js 条件编译初版涉及视频流播放时m3u8 格式是绕不开的HLS 协议在微信小程序和 H5 端的支持程度不同。微信小程序的原生 video 组件底层是微信播放器直接支持 m3u8 地址H5 端的 video 标签原生不支持 HLS必须借助 hls.js 把流转成 MSE 可以喂给 video 的数据。template view classplayer video v-ifisMpWeixin :srcvideoSrc controls autoplay object-fitcontain erroronVideoError / view v-else idhlsVideoContainer classhls-video/view /view /template// #ifdef H5 import Hls from hls.js // #endif export default { data() { return { videoSrc: https://example.com/live/stream.m3u8, isMpWeixin: false } }, onLoad() { // #ifdef MP-WEIXIN this.isMpWeixin true // #endif }, onReady() { // #ifdef H5 this.initH5Player() // #endif }, methods: { // #ifdef H5 initH5Player() { const container document.getElementById(hlsVideoContainer) const video document.createElement(video) video.controls true video.autoplay true video.style.width 100% container.appendChild(video) if (Hls.isSupported()) { const hls new Hls({ maxBufferLength: 30 }) hls.loadSource(this.videoSrc) hls.attachMedia(video) hls.on(Hls.Events.ERROR, (event, data) { console.error(HLS 播放错误, data) }) } }, // #endif onVideoError() { uni.showToast({ title: 视频加载失败请重试, icon: none }) } } }条件编译的 #ifdef 指令在编译期生效H5 代码只会打进 H5 产物小程序产物不会包含 hls.js体积上不会浪费。maxBufferLength 控制缓冲时长网络差时调小能降低延迟网络好时调大可减少卡顿。autoplay 在部分浏览器会被拦截移动端普遍要求先静音或用户手势触发后才允许自动播放初版调试时如果发现点开不播先检查浏览器拦截而不是怀疑 hls.js 配置。微信端 video 组件遇到 m3u8 拉流失败时onVideoError 里 catch 住错误给出重试按钮即可。5.2 uniapp 开发 H5 嵌入微信公众号中获取定位三套路径按环境选uniapp 的 uni.getLocation 在小程序端是直通微信定位能力但在 H5 端被嵌入微信公众号里时表现并不一致。定位这个功能在 H5 与小程序端要按环境分三套处理小程序端优先用 uni.getLocationH5 在微信内置浏览器里优先用微信 JS-SDK 的 wx.getLocation需要后端提供签名普通 H5 浏览器里则可以尝试 HTML5 Geolocation。// 小程序与普通 H5 环境下的通用调用 uni.getLocation({ type: gcj02, isHighAccuracy: true, highAccuracyExpireTime: 3000, success: (res) { console.log(纬度, res.latitude) console.log(经度, res.longitude) }, fail: (err) { console.error(定位失败, err) } })type 参数要按地图服务商选wgs84 是 gps 原始坐标gcj02 是国测局加密坐标国内地图 SDK微信内置地图、高德、腾讯都要求 gcj02传错会导致标点偏移几百米。isHighAccuracy 开启后定位精度更高但更费电初版测试建议开启上线前根据场景决定是否降级。manifest 里 permission 的 scope.userLocation 配置在 3.1 节还没配的话小程序端调用 getLocation 会直接失败。H5 嵌入微信公众号里的定位核心坑在授权链路上公众号要先配置 JS 接口安全域名后端再根据当前页面的 URL 生成签名前端 wx.config 成功后才能调 wx.getLocation。签名依赖后端参与初版排期时要把后端配合时间计入。微信公众号内不配置 JS-SDK 而直接调 uni.getLocation很多安卓机子上会静默失败这也是 H5 端定位“时好时坏”的主要原因。5.3 自定义分享好友与顶部导航栏高度计算微信小程序的转发能力通过 onShareAppMessage 配置初版如果连一个分享入口都不留后续推广时还得返工。页面里声明了该方法右上角菜单才会出现“转发”按钮方法返回值决定分享卡片内容。export default { onShareAppMessage() { return { title: 初版小程序看看里面有什么, path: /pages/index/index?fromshare, imageUrl: /static/share.png } } }path 要带上前置页面需要的参数imageUrl 分享图尺寸建议 5:4缺省时默认截取当前页面顶部作为封面。如果用了自定义导航栏分享按钮和胶囊按钮都要在自定义区域内预留布局空间胶囊按钮的位置需要动态计算// #ifdef MP-WEIXIN const systemInfo uni.getSystemInfoSync() const menuButton uni.getMenuButtonBoundingClientRect() const navBarHeight (menuButton.top - systemInfo.statusBarHeight) * 2 menuButton.height const statusBarHeight systemInfo.statusBarHeight // #endif自定义导航栏的总高度等于状态栏高度加上导航内容区高度导航内容区通常按照胶囊按钮的垂直居中位置反推。这组数值在 iPhone 全面屏和安卓机型上差异较大不要在 css 里写死按上面公式计算后动态绑定到内联 style。顶部导航栏里如果有搜索框或标题水平方向要避让胶囊按钮的左侧位置否则会被挡住。6. 打包前自检清单跨端差异一次性抹平6.1 条件编译语句按平台隔离差异源码跨端差异源码统一用条件编译包裹而不是运行时 if 判断。编译期就能把不需要的代码从产物里剔除H5 包不会残留小程序 API 调用小程序包也不会有 document 操作。// #ifdef MP-WEIXIN uni.login({ provider: weixin }) // #endif // #ifdef H5 doH5LoginByPhone() // #endif6.2 微信端与 H5 端的差异自检清单发布前把下面这张表逐项过一遍大部分线上问题能提前拦下能力微信小程序端H5 端登录wx.login 换 codeOAuth 或账号密码/验证码定位uni.getLocation 直接可用公众号内需 JS-SDK 签名m3u8 播放video 原生支持依赖 hls.js分享好友onShareAppMessage 生效不支持需引导复制链接本地存储uni.setStorageSynclocalStorage 兼容层隐私模式会失效6.3 从构建到上架小程序包与 H5 包的产物去向微信小程序正式包用 build 命令产出后微信开发者工具导入 dist/build/mp-weixin在工具里完成预览、上传再到公众平台提审。H5 包 build 后是纯静态文件部署到任意 https 服务器即可。uniapp ios 打包需要 p12 证书与描述文件在 HBuilderX 云打包界面配置 Bundle Identifier 后生成安装包上架安卓应用市场时要注意 targetSdk 版本与各市场的签名要求包名和签名证书确定后不要再改否则无法覆盖安装。最后一件事build 前全局搜索 localStorage、document 等 H5 专属调用确认都被条件编译包裹或替换为 uni API再提交测试。本文还有配套的精品资源点击获取