逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。
开发者写的所有代码最终将会打包成一份JavaScript
文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似 ServiceWorker,所以逻辑层也称为 App Service。
在JavaScript
的基础上,新增了一些功能,以方便小程序的开发:
getApp
和getCurrentPages
方法,分别用来获取App
实例和当前页面栈。注:小程序框架的逻辑层并非运行在浏览器中,因此JavaScript
在web中一些能力都无法使用。如window
、document
等
App()
函数用来注册一个小程序。接受一个object
参数,其指定小程序的生命周期回调等。
App()
必须在app.js
中调用,必须调用且只能调用一次。不然会出现无法预期的后果。
后台:当用户点击右上角关闭,或者按了设备Home键离开微信,小程序并没有直接销毁,而是进入了后台;
前台:当再次进入微信或再次打开小程序,又会从后台进入前台。
注:只有当小程序进入后台一定时间,或者系统资源占用过高,才会被真正的销毁。
//app.js
App({
onLaunch(options) {
// Do something initial when launch
},
onShow(options) {
// Do something when show
},
onHide() {
// Do something when hide.
},
onError(msg) {
console.log(msg)
},
globalData: 'I am global data'
})
App({
onPageNotFound(res) {
wx.redirectTo({
url: 'pages/...'
}) // 如果是 tabbar 页面,请使用 wx.switchTab
}
})
全局的getApp()
函数可以用来获取到小程序App
实例。
字段 | 类型 | 说明 | 最低版本 |
---|---|---|---|
allowDefault | Boolean | 在App 未定义时返回默认实现。当App被调用时,默认实现中定义属性会被覆盖合并到App中。一般用于独立分包。 |
2.2.4 |
// other.js
const appInstance = getApp()
console.log(appInstance.globalData) // I am global data
注意
App()
内的函数中调用getApp()
,使用this
就可以拿到app实例。getApp()
获取实例之后,不要私自调用生命周期函数。基础库 1.1.0 开始支持,低版本需做兼容处理
App
的onLaunch
和onShow
,或wx.getLaunchOptionsSync
中获取上述场景值。wx.getLaunchOptionsSync
和wx.onShow
中获取上述场景值部分场景值下还可以获取来源应用、公众号或小程序的appId。
由于安卓系统限制,目前还无法获取到按Home键退出到桌面,然后从桌面再次进小程序的场景值,对于这种情况,会保留上一次的场景值
Page(Object)
函数用来注册一个页面。接受一个Object
类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
Object 参数说明:
示例代码:
Page({
data: {
text: 'This is page data.'
},
onLoad(options) {
// do some initialize when page load
},
onReady() {
// do something when page ready.
},
onShow() {
// do something when page show
},
onHide() {
// do something when page hide.
},
onUnload() {
// do something when page close.
},
onPullDownRefresh() {
// do something when pull down
},
onReachBottom() {
// do something when page reach bottom.
},
onShareAppMessage() {
// return custom share data when user share.
},
onPageScroll() {
// do something when page scroll.
},
onResize() {
// do something when page resize
},
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
},
// event handler.
viewTap() {
this.setData({
text: 'set some data for updating view.'
},function() {
// this is setData callback
})
},
customData: {
hi: 'MINA'
}
})
除了Page
,作为高级用法,页面可以像自定义组件一样使用Component
来创建,这样可以使用自定义组件的特性,如behaviors
等。
data
是页面第一次渲染使用的初始数据。
页面加载时,data
将会以JSON
字符串的形式由逻辑层传至渲染层,因此data
中的数据必须是可以转成JSON
的类型:字符串,数字,布尔值,对象,数组。
渲染层可以通过WXML
对数据进行绑定。
<view>{{ text }}view>
<view>{{ array[0].msg }}view>
Page({
data: {
text: 'init data',
array: [{msg:'1'},{msg: '2'}]
}
})
Page({
onShareAppMessage(res) {
if(res.from === 'button') {
// 来自页面内转发按钮
console.log(res.target)
}
return {
title: '自定义转发标题',
path: '/page/user?id=123'
}
}
})
Page({
onTabItemTap(item) {
console.log(item.index)
console.log(item.pagePath)
console.log(item.text)
}
})
Page
中还可以定义组件事件处理函数。在渲染层的组件中加入事件绑定,当事件被触发时,就会执行 Page 中定义的事件处理函数。
click meview>
Page({
viewTap() {
console.log('view tap')
}
})
基础库 1.2.0 开始支持,低版本需做兼容处理。
到当前页面的路径,类型为String
。
Page({
onShow() {
console.log(this.route)
}
})
setData
函数用于将数据从逻辑层发送到视图层(异步),同时改变对应的this.data
的值(同步)。
参数说明
字段 | 类型 | 必填 | 描述 | 最低版本 |
---|---|---|---|---|
data | Object | 是 | 这次要改变的数据 | |
callback | Function | 否 | setData引起的界面更新渲染完毕后的回调函数 | 1.5.0 |
Object
以 key: value
的形式表示,将 this.data
中的 key
对应的值改变成 value
。
其中key
可以以数据路径的形式给出,支持改变数组中的某一项或对象的某个属性,如array[2].message
,a.b.c.d
,并且不需要在this.data
中预先定义。
注意:
this.data
而不调用this.setData
是无法改变页面的状态的,还会造成数据不一致。JSON
化的数据。data
中任何一项的value
设为undefined
,否则这一项将不被设置并可能遗留一些潜在问题。<view>{{text}}view>
<button bindtap="changeText">Change normal databutton>
<view>{{num}}view>
<button bindtap="changeNum">Change normal numbutton>
<view>{{array[0].text}}view>
<button bindtap="changeItemInArray">Change Array databutton>
<view>{{object.text}}view>
<button bindtap="changeItemInObject">Change Object databutton>
<view>{{newField.text}}view>
<button bindtap="addNewField">Add new databutton>
Page({
data: {
text: 'init data',
num: 0,
array: [{text: 'init data'}],
object: {
text: 'init data'
}
},
changeText() {
// this.data.text = 'changed data' // 不要直接修改 this.data
// 应该使用 setData
this.setData({
text: 'changed data'
})
},
changeNum() {
// 或者,可以修改 this.data 之后马上用 setData 设置一下修改了的字段
this.data.num = 1
this.setData({
num: this.data.num
})
},
changeItemInArray() {
// 对于对象或数组字段,可以直接修改一个其下的子字段,这样做通常比修改整个对象或数组更好
this.setData({
'array[0].text': 'changed data'
})
},
changeItemInObject() {
this.setData({
'object.text': 'changed data'
})
},
addNewField() {
this.setData({
'newField.text': 'new data'
})
}
})
在小程序中所有页面的路由全部由框架进行管理。
框架以栈的形式维护了当前的所有页面。当发生路由切换的时候,页面栈的表现如下:
getCurrentPages()
函数用户获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
注意:
App.onLaunch
的时候调用getCurrentPages()
,此时page
还没有生成。对于路由的触发方式以及页面生命周期函数如下:
示例:
Tab 切换对应的生命周期(以 A、B 页面为 Tabbar 页面,C是从 A 页面打开的页面,D 页面是从 C 页面打开的页面)
当前页面 | 路由后页面 | 触发的生命周期(按顺序) |
---|---|---|
A | A | Nothing happend |
A | B | A.onHide() , B.onLoad() , B.onShow() |
A | B(再次打开) | A.onHide() , B.onShow() |
C | A | C.onUnload() , A.onShow() |
C | B | C.onUnload() , B.onLoad() , B.onShow() |
D | B | D.onUnload() , C.onUnload() , B.onLoad() , B.onShow() |
D(从转发进入) | A | D.onUnload() , A.onLoad() , A.onShow() |
D(从转发进入) | B | D.onUnload() , B.onLoad() , B.onShow() |
Tips:
navigateTo
, redirectTo
只能打开非 tabBar 页面。switchTab
只能打开 tabBar 页面。reLaunch
可以打开任意页面。onLoad
中获取。在JavaScript 文件中声明的变量和函数只在该文件中有效;不同的文件中可以声明相同名字的变量和函数,不会互相影响。
通过全局函数getApp()
可以获取全局的应用实例,如果需要全局的数据可以在App()
中设置,如:
// app.js
App({
globalData: 1
})
// a.js
// The localValue can only be used in file a.js.
const localValue = 'a'
// Get the app instance.
const app = getApp()
// Get the global data and change it.
app.globalData++
// b.js
// You can redefine localValue in file b.js, without interference with the localValue in a.js.
const localValue = 'b'
// If a.js it run before b.js, now the globalData shoule be 2.
console.log(getApp().globalData)
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过module.exports
或者exports
才能对外暴露接口。
需要注意的是:
exports
是module.exports
的一个引用,因此在模块里边随意更改exports
的指向会造成未知的错误。所以更推荐开发者采用module.exports
来暴露模块接口,除非你已经清晰知道这两者的关系。node_modules
,开发者需要使用到node_modules
时候建议拷贝出相关的代码到小程序的目录中或者使用小程序支持的 npm 功能。// common.js
function sayHello(name) {
console.log('Hello ${name}!')
}
function sayGoodbye(name) {
console.log('Goodbye ${name}!')
}
module.exports.sayHello = sayHello
exports.sayGoodbye = sayGoodbye
在需要使用这些模块的文件中,使用require(path)
将公共代码引入
const common = require('common.js')
Page({
helloMINA() {
common.sayHello('MINA')
},
goodbyeMINA() {
common.sayGoodbye('MINA')
}
})
Tips
tip
: require 暂时不支持绝对路径相对路径:指由这个文件所在的路径引起的跟其他文件(或文件夹)的路径关系,网页结构设计中多采用这种方法来表示目标的路径。
./
:代表文件所在的目录(可以省略不写)../
:代表文件所在的父级目录../../
:代表文件所在的父级目录的父级目录/
:代表文件所在的根目录(可以理解成项目内部的绝对路径)绝对路径:指目录下的绝对位置,直接到达目标位置,通常是从盘符开始的路径。
小程序开发框架提供丰富的微信原生API,可以方便的调起微信提供的能力,如获取用户信息,本地存储,支付功能等。
小程序 API 有以下几种类型:
以on
开头的 API 用来监听某个事件是否触发,如:wx.onSocketOpen
,wx.onCompassChange
等。
这类 API 接受一个回调函数作为参数,当事件触发时会调用这个回调函数,并将相关数据以参数形式传入。
wx.onCompassChange(function (res) {
console.log(res.direction)
})
以Sync
结尾的 API 都是同步 API,如wx.setStorageSync
、wx.getSystemInfoSync
等。此外,也有一些其他的同步 API,如 wx.createWorker
、wx.getBackgroundAudioManager
等。
同步 API 的执行结果可以通过函数返回值直接获取,如果执行出错会抛出异常。
try {
wx.setStorageSync('key', 'value')
} catch (e) {
console.error(e)
}
大多数 API 都是异步 API,如wx.request
,wx.login
等。这类 API 接口通常都接受一个 Object
类型的参数,这个参数都支持按需指定以下字段来接收接口调用结果:
Object 参数说明
参数名 | 类型 | 必填 | 说明 |
---|---|---|---|
success | function | 否 | 接口调用成功的回调函数 |
fail | function | 否 | 接口调用失败的回调函数 |
complete | function | 否 | 接口调用结束的回调函数(调用成功、失败都会执行) |
其他 | Any | - | 接口定义的其他参数 |
回调函数的参数
success
、fail
、complete
函数调用时会传入一个Object
类型参数,包含以下字段:
属性 | 类型 | 说明 |
---|---|---|
errMsg | string | 错误信息,如果调用成功返回${ apiName }:ok |
errCode | number | 错误码,仅部分 API 支持,具体含义请参考对应 API 文档,成功时为0 |
其他 | Any | 接口返回的其他数据 |
异步 API 的执行结果需要通过Object
类型的参数中传入的对应回调函数获取。部分异步 API 也会有返回值,可以用来实现更丰富的功能,如wx.request
、wx.connectSockets
等。
wx.login({
success(res) {
console.log(res.code)
}
})