基础
💡 核心结论
小程序采用双线程架构:逻辑层(JSCore)和视图层(WebView)分离
WXML是类HTML语法,WXSS是类CSS语法,支持rpx响应式单位
生命周期分为应用生命周期和页面生命周期
小程序不支持DOM操作,数据驱动视图更新
小程序包体积限制2MB,分包后主包不超过2MB
1. 快速开始
1.1 注册与配置
# 1. 注册小程序账号
https://mp.weixin.qq.com/
# 2. 获取AppID
开发 → 开发管理 → 开发设置 → AppID
# 3. 下载开发者工具
https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html
1.2 项目结构
my-miniprogram/
├── pages/ # 页面目录
│ ├── index/
│ │ ├── index.js # 页面逻辑
│ │ ├── index.json # 页面配置
│ │ ├── index.wxml # 页面结构
│ │ └── index.wxss # 页面样式
│ └── logs/
│ ├── logs.js
│ ├── logs.json
│ ├── logs.wxml
│ └── logs.wxss
├── utils/ # 工具函数
│ └── util.js
├── app.js # 应用逻辑
├── app.json # 全局配置
├── app.wxss # 全局样式
├── project.config.json # 项目配置
└── sitemap.json # 索引配置
1.3 全局配置 (app.json)
{
"pages": [
"pages/index/index",
"pages/logs/logs"
],
"window": {
"backgroundTextStyle": "light",
"navigationBarBackgroundColor": "#fff",
"navigationBarTitleText": "我的小程序",
"navigationBarTextStyle": "black",
"enablePullDownRefresh": true,
"backgroundColor": "#f8f8f8"
},
"tabBar": {
"color": "#999",
"selectedColor": "#07c160",
"backgroundColor": "#fff",
"borderStyle": "black",
"list": [
{
"pagePath": "pages/index/index",
"text": "首页",
"iconPath": "images/home.png",
"selectedIconPath": "images/home-active.png"
},
{
"pagePath": "pages/profile/profile",
"text": "我的",
"iconPath": "images/profile.png",
"selectedIconPath": "images/profile-active.png"
}
]
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"debug": false
}
2. 页面开发
2.1 WXML 语法
数据绑定:
<!-- pages/index/index.wxml -->
<view class="container">
<!-- 文本绑定 -->
<text>{{message}}</text>
<!-- 属性绑定 -->
<image src="{{imgUrl}}" mode="aspectFit"></image>
<!-- 运算 -->
<text>{{a + b}}</text>
<text>{{flag ? '真' : '假'}}</text>
<!-- 组合 -->
<view class="item-{{index}}">Item {{index + 1}}</view>
</view>
列表渲染:
<!-- wx:for -->
<view wx:for="{{items}}" wx:key="id">
{{index}}: {{item.name}}
</view>
<!-- 自定义变量名 -->
<view wx:for="{{items}}" wx:for-item="product" wx:for-index="idx" wx:key="id">
{{idx}}: {{product.name}}
</view>
<!-- 嵌套循环 -->
<view wx:for="{{categories}}" wx:key="id">
<text>{{item.name}}</text>
<view wx:for="{{item.products}}" wx:for-item="product" wx:key="id">
{{product.name}}
</view>
</view>
条件渲染:
<!-- wx:if -->
<view wx:if="{{condition}}">显示内容</view>
<view wx:elif="{{condition2}}">其他内容</view>
<view wx:else>默认内容</view>
<!-- hidden -->
<view hidden="{{!show}}">隐藏内容</view>
<!-- block -->
<block wx:if="{{true}}">
<view>内容1</view>
<view>内容2</view>
</block>
模板:
<!-- 定义模板 -->
<template name="msgItem">
<view>
<text>{{index}}: {{msg}}</text>
<text>Time: {{time}}</text>
</view>
</template>
<!-- 使用模板 -->
<template is="msgItem" data="{{...item}}" />
2.2 WXSS 样式
/* pages/index/index.wxss */
/* rpx:响应式单位(750rpx = 屏幕宽度) */
.container {
width: 750rpx;
padding: 20rpx;
}
/* 全局选择器 */
page {
background-color: #f8f8f8;
font-size: 28rpx;
}
/* 类选择器 */
.title {
font-size: 36rpx;
font-weight: bold;
color: #333;
}
/* ID选择器 */
#header {
height: 100rpx;
}
/* 伪类 */
.button:active {
opacity: 0.7;
}
/* Flex布局 */
.flex-row {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
/* 导入样式 */
@import "../../styles/common.wxss";
2.3 页面逻辑 (JS)
// pages/index/index.js
Page({
// 页面数据
data: {
message: 'Hello World',
count: 0,
items: [],
userInfo: {}
},
// 生命周期函数
onLoad(options) {
console.log('页面加载', options)
this.loadData()
},
onShow() {
console.log('页面显示')
},
onReady() {
console.log('页面初次渲染完成')
},
onHide() {
console.log('页面隐藏')
},
onUnload() {
console.log('页面卸载')
},
// 下拉刷新
onPullDownRefresh() {
this.loadData()
wx.stopPullDownRefresh()
},
// 上拉加载
onReachBottom() {
this.loadMore()
},
// 分享
onShareAppMessage() {
return {
title: '分享标题',
path: '/pages/index/index',
imageUrl: '/images/share.jpg'
}
},
// 自定义方法
loadData() {
wx.showLoading({ title: '加载中' })
wx.request({
url: 'https://api.example.com/data',
success: (res) => {
this.setData({
items: res.data
})
},
complete: () => {
wx.hideLoading()
}
})
},
// 事件处理
handleTap(e) {
console.log('点击事件', e)
this.setData({
count: this.data.count + 1
})
},
handleInput(e) {
this.setData({
message: e.detail.value
})
}
})
3. 生命周期
3.1 应用生命周期
// app.js
App({
// 全局数据
globalData: {
userInfo: null,
token: ''
},
// 小程序初始化
onLaunch(options) {
console.log('小程序启动', options)
// 场景值:options.scene
// 启动参数:options.query
// 检查更新
this.checkUpdate()
// 获取系统信息
const systemInfo = wx.getSystemInfoSync()
console.log(systemInfo)
},
// 小程序显示
onShow(options) {
console.log('小程序切前台', options)
},
// 小程序隐藏
onHide() {
console.log('小程序切后台')
},
// 错误监听
onError(msg) {
console.error('小程序错误', msg)
},
// 页面不存在
onPageNotFound(res) {
console.log('页面不存在', res)
wx.redirectTo({
url: '/pages/index/index'
})
},
// 检查更新
checkUpdate() {
const updateManager = wx.getUpdateManager()
updateManager.onCheckForUpdate((res) => {
console.log('检查更新', res.hasUpdate)
})
updateManager.onUpdateReady(() => {
wx.showModal({
title: '更新提示',
content: '新版本已准备好,是否重启应用?',
success(res) {
if (res.confirm) {
updateManager.applyUpdate()
}
}
})
})
}
})
3.2 页面生命周期流程
小程序启动
↓
App.onLaunch
↓
App.onShow
↓
Page.onLoad ────→ 页面加载,只调用一次
↓
Page.onShow ────→ 页面显示,每次切换都调用
↓
Page.onReady ───→ 初次渲染完成,只调用一次
↓
[用户操作]
↓
Page.onHide ────→ 页面隐藏
↓
Page.onUnload ──→ 页面卸载
4. 组件
4.1 视图容器
<!-- view:块级容器 -->
<view class="container">
<view class="item">Item 1</view>
<view class="item">Item 2</view>
</view>
<!-- scroll-view:可滚动容器 -->
<scroll-view
scroll-y
style="height: 400rpx"
bindscrolltolower="onReachBottom"
enable-back-to-top
>
<view wx:for="{{items}}" wx:key="id">{{item.name}}</view>
</scroll-view>
<!-- swiper:轮播图 -->
<swiper
indicator-dots
autoplay
interval="3000"
duration="500"
circular
>
<swiper-item wx:for="{{banners}}" wx:key="id">
<image src="{{item.image}}" mode="aspectFill"></image>
</swiper-item>
</swiper>
4.2 基础组件
<!-- text:文本 -->
<text selectable user-select>可选择的文本</text>
<!-- image:图片 -->
<image
src="{{imgUrl}}"
mode="aspectFit"
lazy-load
bindload="onImageLoad"
binderror="onImageError"
></image>
<!-- icon:图标 -->
<icon type="success" size="40" color="#07c160"></icon>
<!-- progress:进度条 -->
<progress percent="60" show-info stroke-width="4" activeColor="#07c160"></progress>
<!-- rich-text:富文本 -->
<rich-text nodes="{{htmlContent}}"></rich-text>
4.3 表单组件
<form bindsubmit="onSubmit" bindreset="onReset">
<!-- input:输入框 -->
<input
type="text"
placeholder="请输入用户名"
value="{{username}}"
bindinput="onUsernameInput"
maxlength="20"
/>
<!-- textarea:多行输入 -->
<textarea
placeholder="请输入内容"
auto-height
maxlength="200"
></textarea>
<!-- button:按钮 -->
<button type="primary" form-type="submit">提交</button>
<button type="default" form-type="reset">重置</button>
<!-- checkbox:复选框 -->
<checkbox-group bindchange="onCheckboxChange">
<label wx:for="{{items}}" wx:key="id">
<checkbox value="{{item.value}}"/>{{item.name}}
</label>
</checkbox-group>
<!-- radio:单选框 -->
<radio-group bindchange="onRadioChange">
<label wx:for="{{items}}" wx:key="id">
<radio value="{{item.value}}"/>{{item.name}}
</label>
</radio-group>
<!-- switch:开关 -->
<switch checked="{{isChecked}}" bindchange="onSwitchChange"/>
<!-- slider:滑块 -->
<slider min="0" max="100" value="{{value}}" bindchange="onSliderChange"/>
<!-- picker:选择器 -->
<picker mode="selector" range="{{items}}" bindchange="onPickerChange">
<view>{{items[index]}}</view>
</picker>
</form>
4.4 导航组件
<!-- navigator:页面跳转 -->
<navigator url="/pages/detail/detail?id=1">跳转到详情</navigator>
<navigator url="/pages/list/list" open-type="redirect">重定向</navigator>
<navigator url="/pages/other/other" open-type="switchTab">切换Tab</navigator>
4.5 自定义组件
创建组件:
// components/product-item/product-item.js
Component({
// 组件属性
properties: {
product: {
type: Object,
value: {}
},
showPrice: {
type: Boolean,
value: true
}
},
// 组件数据
data: {
count: 0
},
// 组件生命周期
lifetimes: {
attached() {
console.log('组件被添加到页面')
},
detached() {
console.log('组件从页面移除')
}
},
// 页面生命周期(组件所在页面)
pageLifetimes: {
show() {
console.log('页面显示')
},
hide() {
console.log('页面隐藏')
}
},
// 组件方法
methods: {
onTap() {
// 触发自定义事件
this.triggerEvent('tap', {
id: this.data.product.id
})
},
onAdd() {
this.setData({
count: this.data.count + 1
})
}
}
})
<!-- components/product-item/product-item.wxml -->
<view class="product-item" bindtap="onTap">
<image src="{{product.image}}" class="product-image"></image>
<view class="product-info">
<text class="product-name">{{product.name}}</text>
<text class="product-desc">{{product.description}}</text>
<view class="product-footer" wx:if="{{showPrice}}">
<text class="product-price">¥{{product.price}}</text>
<button size="mini" bindtap="onAdd">加购</button>
</view>
</view>
</view>
/* components/product-item/product-item.wxss */
.product-item {
display: flex;
padding: 20rpx;
border-bottom: 1rpx solid #eee;
}
.product-image {
width: 200rpx;
height: 200rpx;
border-radius: 8rpx;
}
.product-info {
flex: 1;
margin-left: 20rpx;
}
.product-name {
font-size: 32rpx;
font-weight: bold;
}
.product-price {
color: #ff5722;
font-size: 36rpx;
}
// components/product-item/product-item.json
{
"component": true,
"usingComponents": {}
}
使用组件:
// pages/index/index.json
{
"usingComponents": {
"product-item": "/components/product-item/product-item"
}
}
<!-- pages/index/index.wxml -->
<view class="container">
<product-item
wx:for="{{products}}"
wx:key="id"
product="{{item}}"
show-price="{{true}}"
bind:tap="onProductTap"
></product-item>
</view>
// pages/index/index.js
Page({
data: {
products: []
},
onProductTap(e) {
const { id } = e.detail
wx.navigateTo({
url: `/pages/detail/detail?id=${id}`
})
}
})
组件通信:
// 父组件向子组件传值(通过properties)
// 子组件向父组件传值(通过triggerEvent)
// 获取父组件实例
const parent = this.getRelationNodes('../parent/parent')[0]
parent.someMethod()
// 组件间关系
// components/child/child.json
{
"component": true,
"relations": {
"../parent/parent": {
"type": "ancestor"
}
}
}
5. API
5.1 路由跳转
// 保留当前页面,跳转
wx.navigateTo({
url: '/pages/detail/detail?id=1'
})
// 关闭当前页面,跳转
wx.redirectTo({
url: '/pages/result/result'
})
// 跳转到tabBar页面
wx.switchTab({
url: '/pages/index/index'
})
// 关闭所有页面,跳转
wx.reLaunch({
url: '/pages/home/home'
})
// 返回上一页
wx.navigateBack({
delta: 1 // 返回的页面数
})
5.2 交互反馈
// 显示提示
wx.showToast({
title: '操作成功',
icon: 'success',
duration: 2000
})
// 显示模态对话框
wx.showModal({
title: '提示',
content: '确定要删除吗?',
success(res) {
if (res.confirm) {
console.log('用户点击确定')
} else if (res.cancel) {
console.log('用户点击取消')
}
}
})
// 显示加载提示
wx.showLoading({
title: '加载中',
mask: true
})
wx.hideLoading()
// 显示操作菜单
wx.showActionSheet({
itemList: ['选项1', '选项2', '选项3'],
success(res) {
console.log('用户点击了', res.tapIndex)
}
})
5.3 数据缓存
// 同步存储
wx.setStorageSync('key', 'value')
const value = wx.getStorageSync('key')
wx.removeStorageSync('key')
wx.clearStorageSync()
// 异步存储
wx.setStorage({
key: 'userInfo',
data: {name: '张三', age: 25},
success() {
console.log('存储成功')
}
})
wx.getStorage({
key: 'userInfo',
success(res) {
console.log(res.data)
}
})
// 获取存储信息
wx.getStorageInfo({
success(res) {
console.log(res.keys) // 所有key
console.log(res.currentSize) // 当前大小(KB)
console.log(res.limitSize) // 限制大小(KB)
}
})
6. 常见问题
Q1: rpx和px的区别?
A:
rpx:响应式像素,750rpx = 屏幕宽度px:固定像素,不同屏幕显示大小不同iPhone6:375px = 750rpx
Q2: setData的注意事项?
A:
单次setData数据不超过1MB
不要频繁调用(会阻塞渲染)
只更新需要改变的数据
避免在后台态调用
Q3: 小程序包体积限制?
A:
整个小程序:不超过20MB
主包:不超过2MB
单个分包:不超过2MB
参考资源
微信小程序官方文档
微信开放社区
小程序示例代码