我的第一个微信小程序开发实战总结
前言
作为一个有Go、PHP、JavaScript基础的后端开发者,这是我第一次完整开发微信小程序。通过"不德不推计分器"这个德州扑克筹码管理工具的开发,我从零开始学习了小程序的完整技术栈,并成功部署到生产环境。
这篇文章总结了我在这个过程中学到的核心技术知识,希望对其他初学者有所帮助。
项目背景
项目名称: 不德不推计分器
项目类型: 微信小程序
核心功能: 德州扑克筹码计数和房间管理系统
技术架构: 小程序前端 + Go后端 + MySQL数据库
小程序基础架构
1. 项目结构理解
小程序的项目结构非常清晰,这和我熟悉的Web项目有相似之处:
project/
├── app.js # 小程序入口逻辑(类似main.js)
├── app.json # 全局配置文件(类似config)
├── app.wxss # 全局样式文件(类似global.css)
├── pages/ # 页面目录(类似views)
│ └── index/
│ ├── index.js # 页面逻辑(类似controller)
│ ├── index.json # 页面配置
│ ├── index.wxml # 页面结构(类似HTML)
│ └── index.wxss # 页面样式(类似CSS)
└── components/ # 自定义组件目录
关键理解:
每个页面都是一个完整的MVC结构
配置文件分为全局配置(app.json)和页面配置(页面.json)
小程序使用自己的文件格式:.wxml(结构)、.wxss(样式)
2. 配置系统设计
小程序的配置系统让我印象深刻,采用了双配置文件的设计:
// project.config.json (公共配置)
{
"appid": "wxYOUR_APP_ID_HERE",
"compileType": "miniprogram",
"setting": {
"es6": true,
"postcss": true,
"minified": true
}
}
// project.private.config.json (私有配置)
{
"libVersion": "3.9.0",
"projectname": "xiaodejifenqi",
"setting": {
"skylineRenderEnable": true,
"compileHotReLoad": true
}
}
设计优势:
团队协作时公共配置保证环境一致
私有配置允许个人定制开发环境
私有配置会覆盖公共配置的同名字段
页面开发核心概念
1. 页面生命周期管理
这是我学到的最重要的概念之一。小程序的页面生命周期和我熟悉的Web应用有很大不同:
Page({
data: {
// 页面数据
},
onLoad(options) {
// 页面加载时触发,只会调用一次
// 适合:接收页面参数、初始化数据
const { roomId } = options
if (roomId) {
this.loadRoomData(roomId)
}
},
onShow() {
// 页面显示时触发,每次显示都会调用
// 适合:刷新数据、重新获取状态
this.refreshUserInfo()
},
onHide() {
// 页面隐藏时触发
// 适合:清理定时器、暂停音视频
if (this.data.timer) {
clearInterval(this.data.timer)
}
}
})
关键要点:
onLoad只执行一次,onShow每次显示都执行
页面参数通过onLoad的options对象获取
生命周期的执行顺序很重要,要合理安排初始化逻辑
2. 数据绑定与状态管理
小程序的数据绑定机制和React有相似性,但也有自己的特点:
// 错误的做法(直接修改data)
this.data.players.push(newPlayer) // ❌ 不会触发视图更新
// 正确的做法(使用setData)
const newPlayers = [...this.data.players, newPlayer]
this.setData({
players: newPlayers // ✅ 会触发视图更新
})
核心规则:
所有数据更新必须通过
setData()
方法避免直接修改data中的对象和数组
使用扩展运算符保持数据的不可变性
setData()是异步的,但大多数情况下不需要考虑回调
3. WXML模板语法
WXML的语法和我熟悉的模板引擎(如PHP的Twig)类似:
<!-- 数据绑定 -->
<text>{{userInfo.nickname}}</text>
<!-- 条件渲染 -->
<view wx:if="{{showError}}">
<text>{{errorMessage}}</text>
</view>
<!-- 列表渲染 -->
<view wx:for="{{players}}" wx:key="id">
<text>{{item.playerName}}: {{item.netChips}}</text>
</view>
<!-- 事件绑定 -->
<button bindtap="onCreateRoom" data-room-id="{{roomId}}">
创建房间
</button>
重要细节:
wx:key
是必须的,类似React的key事件参数通过
data-*
属性传递,在事件处理函数中通过e.currentTarget.dataset
获取双花括号内可以写简单的JavaScript表达式
4. 响应式布局单位 - rpx详解
这是我在学习小程序样式时学到的最重要概念之一:
/* rpx单位的实际应用 */
.container {
width: 750rpx; /* 占满整个屏幕宽度 */
height: 200rpx; /* 高度自适应不同设备 */
padding: 20rpx; /* 内边距自适应 */
font-size: 28rpx; /* 字体大小自适应 */
}
.button {
width: 300rpx; /* 按钮宽度占屏幕40% */
height: 88rpx; /* 标准按钮高度 */
}
rpx核心概念:
全称: Responsive Pixel(自适应像素)
设计思想: 将所有设备屏幕宽度等分为750份
换算公式: 1rpx = 设备屏幕宽度 / 750
不同设备的换算示例:
iPhone6 (375px宽): 1rpx = 0.5px
iPhone6 Plus (414px宽): 1rpx = 0.552px
微信开发者工具: 1rpx = 0.5px
实用技巧:
设计稿通常按750px宽度设计,可直接将px改为rpx
1:1转换规则:设计稿中的750px = 小程序中的750rpx
避免过小的rpx值(如1rpx、2rpx),可能导致渲染精度问题
这个单位解决了我在移动端开发中最头疼的屏幕适配问题!
5. 公共样式管理与代码优化
在开发过程中,我发现了一个严重的问题:大量的样式代码重复。通过深入分析项目的8个WXSS文件,发现了惊人的重复情况:
样式重复统计:
align-items: center
- 32次重复justify-content: center
- 20次重复主题渐变色 - 4个页面重复使用
border-radius
- 18处重复box-shadow
- 17次类似效果
这种重复不仅增加了代码量,也让维护变得困难。书上提到的公共样式抽取方法完美解决了这个问题。
公共样式文件设计
我创建了 /styles/common.wxss
公共样式文件,采用模块化设计:
/* 导入方式 */
@import "/styles/common.wxss";
/* 布局工具类 */
.flex-center {
display: flex;
align-items: center;
justify-content: center;
}
.flex-column-center {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
/* 主题色系统 */
.bg-primary-gradient {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.bg-glass {
background: rgba(255, 255, 255, 0.15);
backdrop-filter: blur(10rpx);
border: 1rpx solid rgba(255, 255, 255, 0.2);
}
/* 圆角系统 */
.radius-xs { border-radius: 8rpx; }
.radius-sm { border-radius: 12rpx; }
.radius-md { border-radius: 16rpx; }
.radius-lg { border-radius: 20rpx; }
.radius-xl { border-radius: 24rpx; }
/* 阴影系统 */
.shadow-sm { box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.08); }
.shadow-md { box-shadow: 0 6rpx 20rpx rgba(0, 0, 0, 0.12); }
.shadow-card { box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08); }
优化效果对比
优化前的重复代码:
.user-info {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border-radius: 20rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
padding: 40rpx 30rpx;
margin-bottom: 60rpx;
}
.card-container {
display: flex;
flex-direction: column;
align-items: center;
border-radius: 20rpx;
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.08);
/* 几乎相同的样式又写了一遍 */
}
优化后使用工具类:
<!-- WXML中使用工具类 -->
<view class="user-info flex-column-center radius-lg shadow-card p-lg mb-xl">
<!-- 内容 -->
</view>
<view class="card-container flex-column-center radius-lg shadow-card">
<!-- 内容 -->
</view>
/* WXSS中只需要写个性化样式 */
.user-info {
/* 只写这个组件独有的样式 */
background: white;
}
公共样式的模块化结构
我将公共样式分为以下模块:
布局模块 - Flex布局的各种组合
主题模块 - 颜色、渐变、背景
几何模块 - 圆角、阴影、边框
间距模块 - Padding、Margin的标准化
字体模块 - 字号、字重的层级系统
组件模块 - 按钮、卡片、头像等通用样式
工具模块 - 各种实用的CSS工具类
实施最佳实践
命名规范:
语义化命名:
.radius-lg
、.shadow-card
、.text-md
尺寸级别:xs/sm/md/lg/xl 五级标准
功能分类:布局(flex-)、主题(bg-)、几何(radius-)等前缀
使用策略:
新页面优先使用公共样式类
旧页面渐进式重构
基于公共样式进行个性化扩展
维护规范:
不直接修改公共样式文件
新增样式先考虑是否具备通用性
定期审查和优化公共样式系统
优化成果
通过实施公共样式管理,预期能够:
减少60%+的重复CSS代码 (约300+行代码)
提高开发效率 - 新页面开发时直接使用工具类
提升维护性 - 统一修改主题色、间距只需改一处
保证一致性 - 所有页面视觉元素标准化
增强可扩展性 - 便于添加新的设计规范
这个优化让我深刻理解了代码复用在前端开发中的重要性,也体验到了良好架构设计带来的开发效率提升。
API集成与网络请求
1. 网络请求最佳实践
作为后端开发者,我对网络请求的错误处理特别重视。小程序的wx.request
需要处理多层次的错误:
// 标准的API调用模式
wx.request({
url: 'https://xdjsq.codekissyoung.com/api/room/create',
method: 'POST',
data: {
room_name: roomName,
chips_per_hand: chipsPerHand
},
success: (res) => {
// 1. HTTP状态码检查
if (res.statusCode === 200 && res.data) {
// 2. 业务状态检查
if (res.data.success) {
// 成功处理
this.handleSuccess(res.data)
} else {
// 业务错误处理
wx.showToast({
title: res.data.message || '操作失败',
icon: 'none'
})
}
} else {
// HTTP错误处理
this.handleHttpError(res.statusCode)
}
},
fail: (err) => {
// 3. 网络异常处理
console.error('网络请求失败:', err)
wx.showToast({
title: '网络连接失败',
icon: 'none'
})
}
})
三层错误处理机制:
网络层:连接失败、超时等网络异常
HTTP层:状态码非200的HTTP错误
业务层:接口返回的业务逻辑错误
2. 用户体验优化
小程序的用户体验优化主要体现在加载状态和错误提示:
// 操作前显示加载
wx.showLoading({
title: '创建中...'
})
// API调用
wx.request({
// ... API配置
success: (res) => {
wx.hideLoading() // 隐藏加载
if (res.data.success) {
wx.showToast({
title: '创建成功',
icon: 'success',
duration: 1500,
complete: () => {
// Toast完成后跳转页面
wx.navigateTo({
url: `/pages/room/room?roomId=${res.data.room_id}`
})
}
})
}
},
fail: (err) => {
wx.hideLoading() // 确保隐藏加载
// 错误处理...
}
})
组件化开发
1. 自定义组件结构
小程序的组件化开发让我想起了Vue的单文件组件:
// components/create-room-modal/create-room-modal.js
Component({
properties: {
// 组件属性,类似Vue的props
show: {
type: Boolean,
value: false
}
},
data: {
// 组件内部数据
roomName: '',
chipsPerHand: 1000
},
methods: {
// 组件方法
onConfirm() {
// 向父组件发送事件,类似Vue的$emit
this.triggerEvent('confirm', {
roomName: this.data.roomName,
chipsPerHand: this.data.chipsPerHand
})
},
onClose() {
this.triggerEvent('close')
}
}
})
组件通信机制:
父→子:通过properties传递数据
子→父:通过triggerEvent发送事件
这和Vue的props/emit机制非常相似
2. 组件使用方式
// 页面配置中引入组件
{
"usingComponents": {
"create-room-modal": "/components/create-room-modal/create-room-modal"
}
}
<!-- 页面中使用组件 -->
<create-room-modal
show="{{showCreateRoomModal}}"
bind:close="onCloseCreateRoomModal"
bind:confirm="onConfirmCreateRoom">
</create-room-modal>
权限控制与安全
1. 前端权限控制
在我的项目中,实现了基于用户身份的权限控制:
// 权限检查逻辑
canOperatePlayer(playerId) {
// 创建者可以操作所有人
if (this.isRoomCreator()) {
return true
}
// 普通玩家只能操作自己
return playerId === this.data.currentUser.openid
}
isRoomCreator() {
return this.data.currentUser.openid === this.data.roomInfo.creatorId
}
// 权限控制的UI表现
onTakeChips(e) {
const { playerId } = e.currentTarget.dataset
if (!this.canOperatePlayer(playerId)) {
wx.showToast({
title: '只能操作自己的筹码',
icon: 'none',
duration: 2000
})
return
}
// 执行操作...
}
设计原则:
前端权限检查提升用户体验
后端必须有对应的权限验证
用户友好的权限提示信息
2. 微信登录集成
微信小程序的登录机制是我学习的重点:
// 微信登录流程
autoLogin() {
wx.login({
success: (res) => {
if (res.code) {
this.loginWithCode(res.code)
}
},
fail: (err) => {
console.error('wx.login 失败:', err)
this.generateTempUserInfo() // 降级处理
}
})
}
loginWithCode(code) {
wx.request({
url: 'https://xdjsq.codekissyoung.com/api/user/login',
method: 'POST',
data: { code },
success: (res) => {
if (res.data.success) {
const userInfo = {
openid: res.data.user.openid,
nickname: res.data.user.nickname,
avatar_url: res.data.user.avatar_url
}
// 保存到本地存储
wx.setStorageSync('userInfo', userInfo)
this.setData({ userInfo })
}
}
})
}
登录机制理解:
wx.login()
获取临时code将code发送给后端服务器
后端调用微信接口获取openid
返回用户信息并保存到本地
数据持久化策略
1. 本地存储使用
小程序的本地存储API非常简单:
// 保存数据
wx.setStorageSync('userInfo', userInfo)
// 获取数据
const savedUserInfo = wx.getStorageSync('userInfo')
if (savedUserInfo && savedUserInfo.openid) {
this.setData({ userInfo: savedUserInfo })
}
// 清除数据
wx.removeStorageSync('userInfo')
2. 数据同步策略
我采用了"本地优先,服务器同步"的策略:
// 用户信息更新流程
onEditProfile() {
wx.showModal({
title: '修改昵称',
editable: true,
success: (res) => {
if (res.confirm && res.content) {
const newNickname = res.content.trim()
// 1. 立即更新本地显示
const userInfo = { ...this.data.userInfo }
userInfo.nickname = newNickname
this.setData({ userInfo })
// 2. 保存到本地存储
wx.setStorageSync('userInfo', userInfo)
// 3. 异步同步到服务器
this.syncUserInfoToServer(userInfo)
}
}
})
}
后端API设计经验
虽然重点是小程序开发,但我也想分享一些后端API设计的经验,因为这对前端开发也很重要。
1. RESTful API设计
我的API遵循RESTful规范:
GET /api/room/{roomId} - 获取房间信息
POST /api/room/create - 创建房间
POST /api/room/join/{roomId} - 加入房间
POST /api/room/{roomId}/chip/operate - 筹码操作
GET /api/room/{roomId}/chip/history - 筹码历史
2. 统一响应格式
所有API都使用统一的响应格式:
{
"success": true,
"data": {
"room_id": "ABC123",
"room_name": "周末局"
},
"message": "创建成功"
}
这样前端可以使用统一的处理逻辑。
部署与上线经验
1. 域名配置
小程序要求所有网络请求必须使用HTTPS,并且域名需要在微信公众平台配置:
购买SSL证书并配置到服务器
在微信公众平台设置request合法域名
确保所有API调用使用HTTPS协议
2. 真机测试
开发者工具和真机环境有差异,需要重点测试:
网络请求的稳定性
页面跳转和参数传递
用户体验和界面适配
开发中踩过的坑
1. 数据更新不生效
问题:直接修改this.data中的数据,页面不更新
// 错误
this.data.players.push(newPlayer)
// 正确
const newPlayers = [...this.data.players, newPlayer]
this.setData({ players: newPlayers })
2. 页面参数传递
问题:页面跳转时参数传递错误
// 错误:参数会被转为字符串
wx.navigateTo({
url: `/pages/room/room?roomData=${roomData}`
})
// 正确:复杂数据通过全局变量或重新请求
getApp().globalData.roomData = roomData
wx.navigateTo({
url: `/pages/room/room?roomId=${roomId}`
})
3. 异步操作处理
问题:在生命周期函数中进行异步操作时机不对
// 有问题的做法
onShow() {
// 每次显示都会重新请求,可能造成重复请求
this.loadData()
}
// 更好的做法
onShow() {
if (this.data.needRefresh) {
this.loadData()
this.setData({ needRefresh: false })
}
}
性能优化经验
1. setData优化
// 避免频繁的setData调用
// 错误
for (let i = 0; i < 100; i++) {
this.setData({
[`list[${i}]`]: newValue
})
}
// 正确:批量更新
const updates = {}
for (let i = 0; i < 100; i++) {
updates[`list[${i}]`] = newValue
}
this.setData(updates)
2. 图片优化
我在项目中使用了DiceBear API生成用户头像,避免了用户上传图片的复杂性:
generateAvatar(id) {
return `https://api.dicebear.com/7.x/bottts/svg?seed=${encodeURIComponent(id)}`
}
学习心得和建议
1. 从后端转前端的思维转变
作为后端开发者学习小程序,需要适应:
状态管理:后端是无状态的,前端需要管理页面状态
异步编程:回调、Promise的大量使用
用户体验:需要考虑加载状态、错误提示、交互反馈
2. 学习建议
先理解架构:项目结构、配置文件、生命周期
掌握核心API:setData、网络请求、页面跳转
重视组件化:提高代码复用性和维护性
注重用户体验:加载状态、错误处理、交互反馈
前后端协同:API设计要考虑前端的使用便利性
3. 调试技巧
使用
console.log
大量输出调试信息善用微信开发者工具的网络面板
真机调试时开启远程调试功能
利用微信开发者工具的存储面板查看本地数据
总结
通过这个项目,我从一个小程序零基础的后端开发者,成长为能够独立开发完整小程序应用的全栈开发者。小程序开发虽然有其特殊性,但核心的编程思想和我熟悉的技术栈是相通的。
核心收获:
技术栈掌握:小程序前端开发 + Go后端 + MySQL数据库
架构设计:前后端分离、RESTful API、组件化开发
工程实践:错误处理、权限控制、性能优化
产品思维:用户体验、交互设计、功能完整性
这个项目现在已经部署到生产环境,具备了完整的商业应用功能。从技术学习的角度来看,它覆盖了小程序开发的核心技能,是一个很好的学习和实践项目。
希望这些经验总结能对其他初学者有所帮助。小程序开发虽然有学习曲线,但掌握了核心概念和最佳实践后,可以快速构建出功能丰富的应用。
技术栈:微信小程序 + Go + MySQL + Apache + SSL
开发时间:2025年1月