一、目录结构
1.1 主体(app)
由app.js
、app.json
、app.wxss
组成,包括小程序的注册、全局配置、页面注册、公共变量和方法,还有样式。
1.1.1 注册小程序
每个小程序都需要在 app.js
中调用 App()
方法注册小程序实例,绑定生命周期回调函数、错误监听和页面不存在监听函数等。整个小程序只有一个 App 实例,是全部页面共享的。
App({
onLaunch: function () {},
onShow: function () {},
onHide: function () {},
globalData: {
appName: '',
tplId: {},
APPID: '',
domain: '',
userInfo: null,
userWXInfo: null
}
})
开发者可以通过 getApp()
方法获取到全局唯一的 App 实例,获取App上的数据或调用开发者注册在 App
上的函数。
const appInstance = getApp()
console.log(appInstance.globalData.userInfo)
1.1.2 小程序全局配置
小程序根目录下的 app.json
文件用来对微信小程序进行全局配置,决定页面文件的路径、窗口表现、设置网络超时时间、设置多 tab 等。
{
"pages": [
"pages/index/index",
"pages/logs/index"
],
"window": {
"navigationBarTitleText": "Demo"
},
"tabBar": {
"list": [{
"pagePath": "pages/index/index",
"text": "首页"
}, {
"pagePath": "pages/logs/index",
"text": "日志"
}]
},
"networkTimeout": {
"request": 10000,
"downloadFile": 10000
},
"debug": true,
"navigateToMiniProgramAppIdList": [
"wxe5f52902cf4de896"
]
}
1.2 页面(Page)
一个小程序页面由四个文件组成,分别是:index.js、index.wxml、index.json、index.wxss。
⚠️为了方便开发者减少配置项,描述页面的四个文件必须具有相同的路径与文件名。
1.2.1 WXML
WXML(WeiXin Markup Language)是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。具有数据绑定、列表渲染、条件渲染、模板和引用等能力。
1.2.1.1 数据绑定
WXML 中的动态数据均来自对应 Page 的 data。语法使用大家熟悉的Mustache,比如:
Page({
data: {
message: 'Hello,World'
}
})
更新数据需要用到Page原型链上的setData
函数
this.setData({
message: 'updating view'
})
1.2.1.2 列表渲染
在组件上可以使用wx:for
绑定一个数组,来循环渲染该组件。使用 wx:for-item
可以指定数组当前元素的变量名,使用 wx:for-index
可以指定数组当前下标的变量名。比如:
{{ index }}:{{ item.message }}
{{ idx }}:{{ itemName.message }}
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 input 中的输入内容,switch 的选中状态),需要使用 wx:key
来指定列表中项目的唯一的标识符。
wx:key
的值以两种形式提供:
字符串:代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
保留关键字
*this
代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
1.2.1.3 条件渲染
在框架中,使用 wx:if=""
来判断是否需要渲染该代码块
1
2
3
1.2.1.4 模板
WXML提供模板(template),可以在模板中定义代码片段,然后在不同的地方调用。
{{ index }}: {{ msg }}
Time: {{ time }}
使用 is 属性,声明需要的使用的模板,然后将模板所需要的 data 传入,如:
Page({
data: {
item: {
index: 0,
msg: 'this is a template',
time: '2020-09-11'
}
}
})
注意:模板拥有自己的作用域,只能使用 data 传入的数据以及模板定义文件中定义的
模块。
1.2.1.5 引用
WXML 提供两种文件引用方式import
和include
。
import
import 有作用域的概念,即只会 import 目标文件中定义的 template,而不会 import 目标文件 import 的 template。
A template
B template
include
include
可以将目标文件除了
外的整个代码引入,相当于是拷贝到 include
位置,如:
body
header
footer
1.2.2 JS
页面文件夹下的.js文件里面是页面的脚本代码,通过调用Page方法注册页面,在方法内可指定页面的初始数据、生命周期函数、事件处理方法等。
⚠️ 小程序脚本逻辑运行在JSCore中,JSCore没有dom环境,因此小程序完全不支持dom操作
1.2.3 WXSS
页面文件夹下的.wxss文件里面是页面的样式表
1.2.4 JSON
每一个小程序页面也可以使用同名 .json
文件来对本页面的窗口表现进行配置,页面中配置项会覆盖 app.json
的 window
中相同的配置项。
二、生命周期
2.1 APP生命周期
首次(小程序没有运行在后台)打开小程序,触发且只触发一次onLaunch方法。
小程序初始化完成后,触发onShow方法。
小程序切换到后台,触发onHidden方法。
小程序从后台切换到前台,触发onShow方法。
小程序代码出错,触发onError方法。
小程序要跳转的页面不存在,触发onPageNotFound方法。
小程序进入后台一定时间,或者系统资源占用过高,会被销毁。
2.2 页面生命周期
打开Page页面,页面初始化,触发onLoad方法。
页面初始化完成,进入前台展示,触发onShow方法。
首次渲染页面完毕,触发onReady方法,一个页面只会触发一次。
小程序切换到后台或者跳转到其他页面时,触发onHide方法。
小程序从后台切换到前台或者从其他页面返回本页面时,触发onShow方法。
使用wx.redirectTo()或wx.navigateBack()等重定向方法销毁页面时,触发onUnload方法。
2.3 app生命周期影响page生命周期
小程序初始化完成触发App的onShow方法后,Page开始加载并只触发一次Page的onLoad方法。
小程序切换到后台时,先触发Page的onHide方法再触发App的onHide方法。
小程序从后台进入到前台时,先执触发App的onShow方法再触发Page的onShow方法
三、路由与通信
3.1 页面路由
在小程序中所有页面的路由全部由框架进行管理。
开发者可以使用 getCurrentPages()
函数获取当前页面栈。
路由方式 | 页面栈表现 | 跳转方式 |
---|---|---|
初始化 | 新页面入栈 | 打开小程序 |
打开新页面 | 新页面入栈 | 使用wx.navigateTo方法或 |
页面重定向 | 当前页面出栈,新页面入栈 | 使用wx.redirectTo方法或 |
页面返回 | 页面不断出栈,直到目标返回页 | 使用wx.navigateBack方法或 |
Tab 切换 | 页面全部出栈,只留下新的 Tab 页面 | 使用wx.switchTab方法或 |
重加载 | 页面全部出栈,只留下新的页面 | 使用wx.reLaunch方法或 |
wx.navigateTo():保留当前页面,跳转到应用内除了tabbar页面外的其他某个页面。
wx.redirectTo():关闭当前页面,跳转到应用内除了tabbar页面外的其他某个页面。
wx.navigateBack():关闭当前页面,返回上一页面或多级页面。
wx.switchTab():跳转到tabBar页面,并关闭其他所有非tabBar页面。
wx.reLaunch():关闭所有页面,打开到应用内的某个页面。
页面栈:
- wx.navigateTo()方法会增加页面栈层数,直到页面栈为十层。
- wx.redirectTo()方法不会增加页面栈层数。
- wx.navigateBack()方法会减少页面栈层数,直到页面栈层数为一。
- wx.switchTab()和wx.reLaunch()方法会将页面栈层数变为一。
- 可以在小程序页面中通过getCurrentPages()方法获取页面栈,获取到的第一个元素为首页,最后一个元素为当前页。
- 小程序中页面栈最多十层。
- 不要尝试修改页面栈,会导致路由以及页面状态错误。
- 不要在App.onLaunch的时候调用getCurrentPages(),此时 page 还没有生成。
3.2 页面通信
小程序页面之间的通信有三种方式:全局变量、本地存储、url传参。
全局变量: globalData
本地存储: 将数据存储在本地缓存中指定的key中,除非用户主动删除或因存储空间原因被系统清理,否则数据都一直可用,且不管是在app.js还是在其他page文件的js中都可对本地存储进行增删改查。
url传参: 在navigator
组件的url
参数内,需要跳转的页面路径可用“?”
后接参数,参数键与参数值用“=”
相连,不同参数用“&”
分隔。其他组件可以用data-“参数名”
格式绑定属性,js内可用event.currentTarget.dataset.“参数名”
的格式获取参数值,然后使用js跳转方法,方法的ur参数内跳转路径用“?”
连接跳转参数。
参数接收: 跳转后的页面在onLoad生命周期函数中可以接收一个参数,该参数以json的形式接收url跳转传递的值
Page({
onLoad: function(options) {
console.log(options)
}
})
四、常用功能
4.1 小程序登录
- 调用 wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
- 调用 auth.code2Session 接口,换取 用户唯一标识 OpenID 和 会话密钥 session_key。
4.2 UnionID 机制说明
如果开发者拥有多个移动应用、网站应用、和公众帐号(包括小程序),可通过 UnionID 来区分用户的唯一性,因为只要是同一个微信开放平台帐号下的移动应用、网站应用和公众帐号(包括小程序),用户的 UnionID 是唯一的。换句话说,同一用户,对同一个微信开放平台下的不同应用,UnionID是相同的。
4.3 获取手机号
获取微信用户绑定的手机号,需先调用wx.login接口。
因为需要用户主动触发才能发起获取手机号接口,所以该功能不由 API 来调用,需用 button 组件的点击来触发。
Page({
getPhoneNumber (e) {
console.log(e.detail.errMsg)
console.log(e.detail.iv)
console.log(e.detail.encryptedData)
}
})
4.4 分享转发
通过给 button
组件设置属性 open-type="share"
,可以在用户点击按钮后触发 Page.onShareAppMessage
事件:
4.5 授权
获取用户授权: 开发者可以使用wx.getSetting
获取用户当前的授权状态。
打开设置页面: 开发者可以使用wx.openSetting
打开设置界面,引导用户开启授权。
提前发起授权请求: 开发者可以使用wx.authorize
在调用需授权 API 之前,提前向用户发起授权请求。
常用授权接口:
scope | 对应接口 | 描述 |
---|---|---|
scope.userInfo | wx.getUserInfo | 用户信息 |
scope.userLocation | wx.getLocation, wx.chooseLocation | 地理位置 |
scope.userLocationBackground | wx.startLocationUpdateBackground | 后台定位 |
scope.address | wx.chooseAddress | 通讯地址 |
scope.invoiceTitle | wx.chooseInvoiceTitle | 发票抬头 |
scope.invoice | wx.chooseInvoice | 获取发票 |
scope.werun | wx.getWeRunData | 微信运动步数 |
scope.record | wx.startRecord | 录音功能 |
scope.writePhotosAlbum | wx.saveImageToPhotosAlbum, wx.saveVideoToPhotosAlbum | 保存到相册 |
scope.camera | camera组件 | 摄像头 |
- 如果用户未接受或拒绝过此权限,会弹窗询问用户,用户点击同意后方可调用接口;
- 如果用户已授权,可以直接调用接口;
- 一旦用户明确同意或拒绝过授权,其授权关系会记录在后台,直到用户主动删除小程序。
- 如果用户已拒绝授权,则不会出现弹窗,而是直接进入接口 fail 回调。请开发者兼容用户拒绝授权的场景。
⚠️ 注意:
-
wx.authorize({scope: "scope.userInfo"})
,不会弹出授权窗口,请使用 - 需要授权
scope.userLocation
、scope.userLocationBackground
时必须配置地理位置用途说明。
4.6 获取系统信息
小程序可以使用wx.getSystemInfo()方法获取系统信息。
wx.getSystemInfo({
success (res) {
console.log(res.model)
console.log(res.pixelRatio)
console.log(res.windowWidth)
console.log(res.windowHeight)
console.log(res.language)
console.log(res.version)
console.log(res.platform)
}
})
wx.getSystemInfoSync()为wx.getSystemInfo()的同步版本。
4.7 获取图片
小程序可以使用wx.chooseImage()方法获取图片,来源可以选择系统相册或相机拍照。
wx.chooseImage({
count: 1,
sizeType: ['original', 'compressed'],
sourceType: ['album', 'camera'],
success (res) {
// tempFilePath可以作为img标签的src属性显示图片
const tempFilePaths = res.tempFilePaths
}
})
wx.chooseImage()方法获取到的文件路径为本地临时路径,需要将文件长期保存需要调用wx.saveFile()方法。
wx.chooseImage({
success: function(res) {
const tempFilePaths = res.tempFilePaths
wx.saveFile({
tempFilePath: tempFilePaths[0],
success (res) {
const savedFilePath = res.savedFilePath
}
})
}
})
4.8 扫码
小程序使用wx.scanCode()方法可以调起客户端扫码界面进行扫码
// 允许从相机和相册扫码
wx.scanCode({
success (res) {
console.log(res)
}
})
// 只允许从相机扫码
wx.scanCode({
onlyFromCamera: true,
success (res) {
console.log(res)
}
})
五、其他开发事项
5.1 原生组件
小程序中的部分组件是由客户端创建的原生组件,这些组件有:
camera
canvas
-
input
(仅在focus时表现为原生组件) live-player
live-pusher
map
textarea
video
由于原生组件脱离在 WebView 渲染流程外,因此在使用时有以下限制:
- 原生组件的层级是最高的,所以页面中的其他组件无论设置 z-index 为多少,都无法盖在原生组件上。后插入的原生组件可以覆盖之前的原生组件。
- 原生组件无法在picker-view组件中使用。基础库 2.4.4 以下版本,原生组件不支持在 scroll-view、swiper、movable-view 中使用。
- 部分CSS样式无法应用于原生组件,例如:无法对原生组件设置CSS动画;无法定义原生组件为 position:fixed;不能在父级节点使用overflow:hidden来裁剪原生组件的显示区域。
- 原生组件的事件监听不能使用bind:eventname的写法,只支持bindeventname。原生组件也不支持catch和capture的事件绑定方式。
- 原生组件会遮挡vConsole弹出的调试面板。在工具上,原生组件是用web组件模拟的,因此很多情况并不能很好的还原真机的表现,建议在使用到原生组件时尽量在真机上进行调试。
为了解决原生组件层级最高的限制。小程序专门提供了 cover-view 和 cover-image 组件,可以覆盖在部分原生组件上面。这两个组件也是原生组件,但是使用限制与其他原生组件有所不同。
可覆盖的原生组件包括 map、video、canvas、camera、live-player、live-pusher
只支持嵌套 cover-view、cover-image、button。组件属性的长度单位默认为px,2.4.0起支持传入单位(rpx/px)。
5.2 分包
某些情况下,开发者需要将小程序划分成不同的子包,在构建时打包成不同的分包,用户在使用时按需进行加载。
在构建小程序分包项目时,构建会输出一个或多个分包。每个使用分包小程序必定含有一个主包。所谓的主包,即放置默认启动页面/TabBar 页面,以及一些所有分包都需用到公共资源/JS 脚本;而分包则是根据开发者的配置进行划分。
在小程序启动时,默认会下载主包并启动主包内页面,当用户进入分包内某个页面时,客户端会把对应分包下载下来,下载完成后再进行展示。
目前小程序分包大小有以下限制:
- 整个小程序所有分包大小不超过 16M
- 单个分包/主包大小不能超过 2M
对小程序进行分包,可以优化小程序首次启动的下载时间,以及在多团队共同开发时可以更好的解耦协作。
开发者通过在 app.json subpackages
字段声明项目分包结构:
{
"pages":[
"pages/index",
"pages/logs"
],
"subpackages": [
{
"root": "packageA",
"pages": [
"pages/cat",
"pages/dog"
]
}, {
"root": "packageB",
"name": "pack2",
"pages": [
"pages/apple",
"pages/banana"
]
}
]
}
六、参考
https://developers.weixin.qq.com/miniprogram/dev/api/