支付宝小程序代码结构及组成 (三)

简介: 本节以 IDE 内的 Todo App 模板小程序为例,介绍支付宝小程序的文件结构,以及每种文件类型在小程序中的作用。

本章内容出自《小程序开发不求人》电子书,点击下载完整版

支付宝小程序代码结构及组成

小程序页面结构

概述

Page 代表应用的一个页面,负责页面展示和交互。每个页面对应一个子目录,一般有多少个页面,就有多少个子目录。它也是一个构造函数,用来生成页面实例。每个小程序页面一般包含四个文件。

  • [pageName].js:页面逻辑
  • [pageName].axml:页面结构
  • [pageName].acss:页面样式(可选)
  • [pageName].json:页面配置(可选)

页面初始化时,提供数据。

Page({
 data: {
 title: 'Alipay',
 array: [{user: 'li'}, {user: 'zhao'}],
 },
});

根据以上提供的数据,渲染页面内容。

<view>{{title}}</view>
<view>{{array[0].user}}</view>

定义交互行为时,需要指定响应函数。

<view onTap="handleTap">click me</view>

以上代码指定用户触摸按钮时,调用 handleTap 方法。

Page({
 handleTap() {
 console.log('yo! view tap!');
 },
});

页面重新渲染,需要在页面脚本里面调用 this.setData 方法。

<view>{{text}}</view>
<button onTap="changeText"> Change normal data </button>

以上代码指定用户触摸按钮时,调用 changeText 方法。

Page({
 data: {
 text: 'init data',
 },
 changeText() {
 this.setData({
 text: 'changed data',
 });
 },
});

上面代码中,changeText 方法里面调用 this.setData 方法,会导致页面重新渲染。

页面配置

在 /pages 目录中的 .json 文件用于配置当前页面的窗口表现。页面配置比app.json 全局配置简单得多,只能设置 window 相关配置项,但无需写window 这个键。页面配置项会优先于全局配置项。

window 配置项同 全局配置项,同时支持以下几点:

  • 支持 optionMenu 配置导航图标,点击后触发 onOptionMenuClick。但注意:optionMenu 配置将被废弃,建议使用 my.setOptionMenu 设置导航栏图标。
  • 支持 titlePenetrate ,设置导航栏点击穿透。
  • 支持 barButtonTheme,设置导航栏图标主题。

完整配置项如下:

image.png

以下为一个基本示例:

{
 "optionMenu": {
 "icon": "https://img.alicdn.com/tps/i3/T1OjaVFl4dXXa.JOZB-114-114.png"
 },
 "titlePenetrate": "YES",
 "barButtonTheme": "light"
}

页面结构

在 /pages 目录中的 .axml 文件用于定义当前这个页面的结构,文件内容遵循AXML 语法。
AXML 和 HTML 非常相似,但是也有很多不一样的地方。
AXML 是小程序框架设计的一套标签语言,用于描述小程序页面的结构。
AXML 语法可分为五个部分:数据绑定条件渲染列表渲染模板引用
AXML 代码示例:

<!-- pages/index/index.axml -->
<view a:for="{{items}}"> {{item}} </view>
<view a:if="{{view == 'WEBVIEW'}}"> WEBVIEW </view>
<view a:elif="{{view == 'APP'}}"> APP </view>
<view a:else> alipay </view>
<view onTap="add"> {{count}} </view>

对应的 .js 文件示例:

// pages/index/index.js
Page({
 data: {
 items: [1, 2, 3, 4, 5, 6, 7],
 view: 'alipay',
 count: 1,
 },
 add(e) {
 this.setData({
 count: this.data.count + 1,
 });
 },
});

对应的 .acss 文件示例:

/* pages/index/index.acss */
view {
 padding-left: 10px;
}

页面样式

在 /pages 目录中的 .acss 文件用于定义页面样式。
每个页面中的根元素为 page,需要设置页面高度或背景色时,可按如下方式:

page {
 background-color: #fff;
}

ACSS 是一套样式语言,用于描述 AXML 的组件样式,决定 AXML 的组件的显示效果。
为适应广大前端开发者,ACSS 和 CSS 规则完全一致,100% 可以用。同时为更适合开发小程序,对 CSS 进行了扩充。
ACSS 支持 px,rpx,vh,vw 等单位。

rpx

rpx(responsive pixel)可以根据屏幕宽度进行自适应,规定屏幕宽为750rpx。以 Apple iPhone6 为例,屏幕宽度为 375px,共有 750 个物理像素,则 750rpx = 375px = 750 物理像素,1rpx = 0.5px = 1 物理像素。

image.png

样式导入

使用 @import 语句可以导入外联样式表,@import 后需要加上外联样式表相对路径,用;表示结束。
示例代码:

/** button.acss **/
.sm-button {
 padding: 5px;
}
/** app.acss **/
@import "./button.acss";
.md-button {
 padding: 15px;
}

导入路径支持从 node_modules 目录载入第三方模块,例如 page.acss:

@import "./button.acss"; /*相对路径*/
@import "/button.acss"; /*项目绝对路径*/
@import "third-party/page.acss"; /*第三方 npm 包路径*/

内联样式

组件上支持使用 style、class 属性来控制样式。
style 属性
用于接收动态样式,样式在运行时会进行解析。行内样式不支持!important 优先级规则。

<view style="color:{{color}};" />

class 属性
用于接收静态样式,属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,多个类名间以空格分隔。请静态样式写进 class 中,避免将静态样式写进 style 中,以免影响渲染速度。

<view class="my-awesome-view" />

选择器
同 CSS3 保持一致。
注意:

  • .a-, .am- 开头的类选择器为系统组件占用,不可使用。
  • 不支持属性选择器。

全局样式与局部样式

  • app.acss 中的样式为全局样式,作用于每一个页面。
  • 页面文件夹内的 .acss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖app.acss 中相同的选择器。

本地资源引用

ACSS 文件里的本地资源引用请使用绝对路径的方式,不支持相对路径引用。例如:

/* 支持 */
background-image: url('/images/ant.png');
/* 不支持 */
background-image: url('./images/ant.png');

页面运行机制

Page(object: Object)

在 /pages 目录的 .js 文件中,定义 Page(),用于注册一个小程序页面,接受一个 object 作为属性,用来指定页面的初始数据、生命周期回调、事件处理等。
以下为一个基本的页面代码:

// pages/index/index.js
Page({
 data: {
 title: "Alipay",
 },
 onLoad(query) {
 // 页面加载
 },
 onShow() {
 // 页面显示
 },
 onReady() {
 // 页面加载完成
 },
 onHide() {
 // 页面隐藏
 },
 onUnload() {
 // 页面被关闭
 },
 onTitleClick() {
 // 标题被点击
 },
 onPullDownRefresh() {
 // 页面被下拉
 },
 onReachBottom() {
 // 页面被拉到底部
 },
 onShareAppMessage() {
 // 返回自定义分享信息
 },
 // 事件处理函数对象
 events: {
 onBack() {
 console.log('onBack');
 },
 },
 // 自定义事件处理函数
 viewTap() {
 this.setData({
 text: 'Set data for update.',
 });
 },
 // 自定义事件处理函数
 go() {
 // 带参数的跳转,从 page/ui/index 的 onLoad 函数的 query 中读取 type
 my.navigateTo({url:'/page/ui/index?type=mini'});
 },
 // 自定义数据对象
 customData: {
 name: 'alipay',
 },
});

页面生命周期

下图说明了页面 Page 对象的生命周期。
小程序主要靠视图线程(Webview)和应用服务线程(Worker)来控制管理。视图线程和应用服务线程同时运行。

  • 应用服务线程启动后运行 app.onLauch 和 app.onShow 以完成 App 创建,再运行page.onLoad 和 page.onShow 以完成 Page 创建,此时等待视图线程初始化完成通知。
  • 视图线程初始化完成通知应用服务线程,应用服务线程将初始化数据发送给视图线程进行渲染,此时视图线程完成第一次数据渲染。
  • 第一次渲染完成后视图线程进入就绪状态并通知应用服务线程,应用服务线程调用page.onReady 函数并进入活动状态。
  • 应用线程进入活动状态后每次数据修改将会通知视图线程进行渲染。当切换页面进入后台,应用线程调用 page.onHide 函数后,进入存活状态;页面返回到前台将调用page.onShow 函数,进入活动状态;当调用返回或重定向页面后将调用page.onUnload 函数,进行页面销毁。

image.png

object 属性说明

属性 类型 描述 最低版本
data ObjectFunction 初始数据或返回初始化数据的函数。 -
events Object 事件处理函数对象 1.13.7
onLoad Function(query:Object) 页面加载时触发 -
onShow Function 页面显示时触发 -
onReady Function 页面初次渲染完成时触发 -
onHide Function 页面隐藏时触发 -
onUnload Function 页面卸载时触发 -
onShareAppMessage Function(options:Object) 点击右上角分享时触发 -
onTitleClick Function 点击标题触发 -
onOptionMenuClick Function 点击导航栏额外图标触发 1.3.0
onPopMenuClick Function 点击右上角通用菜单中的自定义菜单按钮触发 1.3.0
onPullDownRefresh Function({from:manual|code}) 页面下拉时触发 -
onPullIntercept Function 下拉中断时触发 1.11.0
onTabItemTap Function 点击 tabItem 时触发 1.11.0
onPageScroll Function({scrollTop}) 页面滚动时触发 -
onReachBottom Function 上拉触底时触发 -
其他 Any 开发者可以添加任意的函数或属性到object中,在页面的函数中可以用 this来访问 -

页面数据对象 data

通过设置 data 指定页面的初始数据。当 data 为对象时,被所有页面共享。即:当该页面回退后再次进入该页面时,会显示上次页面的数据,而非初始数据。这种情况,可以通过设置 data 为不可变数据或者变更 data 为页面独有数据两种方式来解决。
设置为不可变数据

Page({
data: { arr:[] },
doIt() {
 this.setData({arr: [...this.data.arr, 1]});
},
});

设置页面独有数据(不推荐)

Page({
data() { return { arr:[] }; },
doIt() {
 this.setData({arr: [1, 2, 3]});
},
});

注意:不要直接修改 this.data,无法改变页面的状态,还会造成数据不一致。比如:

Page({
data: { arr:[] },
doIt() {
 this.data.arr.push(1); // 不要这么写!
 this.setData({arr: this.data.arr});
}
});

生命周期函数

onLoad(query: Object)

页面初始化时触发。一个页面只会调用一次。
query 为 my.navigateTo 和 my.redirectTo 中传递的 query 对象。
query 内容格式为:“参数名=参数值&参数名=参数值…”。

image.png

onShow()

页面显示/切入前台时触发。

onReady()

页面初次渲染完成时触发。
一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
对界面的设置,如 my.setNavigationBar 请在 onReady 之后设置。

onHide()

页面隐藏/切入后台时触发。
如 my.navigateTo 到其他页面或底部 tab 切换等。

onUnload()

页面卸载时触发。
如 my.redirectTo 或 my.navigateBack 到其他页面等。

页面事件处理函数

onShareAppMessage(options: Object)

点击右上角通用菜单中的分享按钮时或点击页面内分享按钮时触发。详见分享

onTitleClick()

点击标题时触发。

onOptionMenuClick()

点击右上角菜单按钮时触发。

onPopMenuClick()

点击右上角通用菜单按钮时触发。

onPullDownRefresh({from: manual|code})

下拉刷新时触发。
需要先在 app.json 的 window 选项中开启 pullRefresh 。当处理完数据刷新后,my.stopPullDownRefresh 可以停止当前页面的下拉刷新。

onPullIntercept()

下拉中断时触发。

onTabItemTap(object: Object)

点击 tabItem 时触发。

image.png

onPageScroll({scrollTop})

页面滚动时触发,scrollTop 为页面滚动距离。

onReachBottom()

上拉触底时触发。

events

为了使代码更加简洁,提供了新的事件处理对象 events。
已有的页面处理事件函数跟直接在 page 实例上暴露的事件函数等价。
注意:

  • events 从基础库 1.13.7 版本 开始支持。
  • 请正确区分页面事件函数与 events 内同名事件函数的基础库版本要求。

以下是 events 支持的事件函数列表:

事件 类型 描述 最低版本
onBack Function 页面返回时触发 1.13.7
onKeyboardHeight Function 键盘高度变化时触发 1.13.7
onOptionMenuClick Function 点击右上角菜单按钮触发 1.13.7
onPopMenuClick Function 点击右上角通用菜单按钮触发 1.13.7
onPullIntercept Function 下拉截断时触发 1.13.7
onPullDownRefresh Function({from:manual/code}) 页面下拉时触发 1.13.7
onTitleClick Function 点击标题触发 1.13.7
onTabItemTap Function 点击非当前 tabItem 后触发 1.13.7
beforeTabItemTap Function 点击非当前 tabItem 前触发 1.13.7
onResize Function({size:{windowWidth:number,windowHeight:number}}) window 尺寸改变时触发 1.16.0

示例代码:

// 特征检测
my.canIUse('page.events.onBack');
Page({
 data: {
 text: 'This is page data.'
 },
 onLoad(){
 // 页面加载时触发
 },
 events:{
 onBack(){
 // 页面返回时触发
 },
 onKeyboardHeight(e){
 // 键盘高度变化时触发
 console.log('键盘高度:', e.height)
 },
 onOptionMenuClick(){
 // 点击右上角菜单按钮触发
 },
 onPopMenuClick(e){
 // 点击右上角通用菜单中的自定义菜单按钮触发
 console.log('用户点击自定义菜单的索引', e.index)
 console.log('用户点击自定义菜单的 name', e.name)
 console.log('用户点击自定义菜单的 menuIconUrl', e.menuIconUrl)
 },
 onPullIntercept(){
 // 下拉截断时触发
 },
 onPullDownRefresh(e){
 // 页面下拉时触发。e.from 的值是“code”表示 startPullDownRefresh 触发的
事件;值是“manual”表示用户下拉触发的下拉事件
 console.log('触发下拉刷新的类型', e.from)
 my.stopPullDownRefresh()
 },
 onTitleClick(){
 // 点击标题触发
 },
 onTabItemTap(e){
 // e.from 是点击且切换 tabItem 后触发,值是“user”表示用户点击触发的事件;
值是“api”表示 switchTab 触发的事件
 console.log('触发 tab 变化的类型', e.from)
 console.log('点击的 tab 对应页面的路径', e.pagePath)
 console.log('点击的 tab 的文字', e.text)
 console.log('点击的 tab 的索引', e.index)
 },
 beforeTabItemTap(){
 // 点击但切换 tabItem 前触发
 },
 onResize(e){
 // window 尺寸改变时触发
 var {windowWidth, windowHeight} = e.size
 console.log('改变后 window 的宽度', windowWidth)
 console.log('改变后 window 的高度', windowHeight)
 },
 }
})

Page.prototype.setData(data: Object, callback: Function)

setData 会将数据从逻辑层发送到视图层,同时改变对应的 this.data 的值。
Object 以 key: value 的形式表示,将 this.data 中的 key 对应的值改变成value。其中 key 可以非常灵活,以数据路径的形式给出,如array[2].message、a.b.c.d,可以不需要在 this.data 中预先定义。
使用过程中,需要注意以下几点:

  1. 直接修改 this.data 无效,无法改变页面的状态,还会造成数据不一致。
  2. 仅支持设置可 JSON 化的数据。
  3. 请尽量避免一次设置过多的数据。
  4. 请不要把 data 中任何一项的 value 设为 undefined ,否则这一项将不被设置并可能遗留一些潜在问题。

示例代码:

<view>{{text}}</view>
<button onTap="changeTitle"> Change normal data </button>
<view>{{array[0].text}}</view>
<button onTap="changeArray"> Change Array data </button>
<view>{{object.text}}</view>
<button onTap="changePlanetColor"> Change Object data </button>
<view>{{newField.text}}</view>
<button onTap="addNewKey"> Add new data </button>
<view>hello: {{name}}</view>
<button onTap="changeName"> Change name </button>
Page({
 data: {
 text: 'test',
 array: [{text: 'a'}],
 object: {
 text: 'blue',
 },
 name: 'taobao',
 },
 changeTitle() {
 // 错误!不要直接去修改 data 里的数据
 // this.data.text = 'changed data'

 // 正确
 this.setData({
 text: 'ha',
 });
 },
 changeArray() {
 // 可以直接使用数据路径来修改数据
 this.setData({
 'array[0].text': 'b',
 });
 },
 changePlanetColor(){
 this.setData(
 'object.text': 'red',
 });
 },
 addNewKey() {
 this.setData({
 'newField.text': 'c',
 });
 },
 changeName() {
 this.setData({
 name: 'alipay',
 }, () => { // 接受传递回调函数
 console.log(this); // this 当前页面实例
 this.setData({ name: this.data.name + ', ' + 'welcome!'});
 });
 },
});

参数说明:

事件 类型 描述 最低版本
data Object 待改变的数据 -
callback Function 回调函数,在页面渲染更新完成之后执行。 使用my.canIUse('page.setData.callback') 做兼容性处理。详见 1.7.0

Page.prototype.$spliceData(data: Object, callback: Function)

说明: $spliceData 自 1.7.2 之后才支持,可以使用my.canIUse('page.$spliceData') 做兼容性处理。详见 兼容接口说明
spliceData 同样用于将数据从逻辑层发送到视图层,但是相比于 setData,在处理长列表的时候,其具有更高的性能。

Object 以 key: value 的形式表示,将 this.data 中的 key 对应的值改变成value。其中 key 可以非常灵活,以数据路径的形式给出,如array[2].message、a.b.c.d,可以不需要在 this.data 中预先定义。value 为一个数组(格式:[start, deleteCount, ...items]), 数组的第一个元素为操作的起始位置,第二个元素为删除的元素的个数,剩余的元素均为插入的数据。对应es5 中数组的 splice 方法。

示例代码:

<!-- pages/index/index.axml -->
<view class="spliceData">
 <view a:for="{{a.b}}" key="{{item}}" style="border:1px solid red">
 {{item}}
 </view>
</view>
// pages/index/index.js
Page({
 data: {
 a: {
 b: [1,2,3,4],
 },
 },
 onLoad(){
 this.$spliceData({ 'a.b': [1, 0, 5, 6] });
 },
});

页面输出:

1
5
6
2
3
4

参数说明:

image.png

Page.prototype.$batchedUpdates(callback: Function)

批量更新数据。
说明 $batchedUpdates 自 1.14.0 之后才支持,可以使用my.canIUse('page.$batchedUpdates') 做兼容性处理。详见 兼容接口说明
参数说明:

image.png

下面是示例代码:

// pages/index/index.js
Page({
 data: {
 counter: 0,
 },
 plus() {
 setTimeout(() => {
 this.$batchedUpdates(() => {
 this.setData({
 counter: this.data.counter + 1,
 });
 this.setData({
 counter: this.data.counter + 1,
 });
 });
 }, 200);
 },
});
<!-- pages/index/index.axml -->
<view>{{counter}}</view>
<button onTap="plus">+2</button>
  1. 本示例中每次点击按钮,页面的 counter 会加 2
  2. 将 setData 放在 this.$batchedUpdates 中,这样尽管有多次 setData,但是却只有一次数据的传输

Page.route

Page 路径,对应 app.json 中配置的路径值,类型为 String。
这是一个只读属性。

Page({
 onShow() {
 // 对应 app.json 中配置的路径值
 console.log(this.route)
 }
})

getCurrentPages 方法

getCurrentPages() 方法用于获取当前页面栈的实例,返回页面数组栈。第一个元素为首页,最后一个元素为当前页面。

框架以栈的形式维护当前的所有页面。路由切换与页面栈的关系如下:

image.png

下面代码可以用于检测当前页面栈是否具有 5 层页面深度。

if (getCurrentPages().length === 5) {
my.redirectTo({
url: '/pages/logs/logs'
});
} else {
my.navigateTo({
url: '/pages/index/index'
});
}

注意: 不要尝试修改页面栈,会导致路由以及页面状态错误。

相关文章
|
1月前
|
存储 JSON 小程序
微信小程序入门之新建并认识小程序结构
微信小程序入门之新建并认识小程序结构
48 1
|
26天前
|
开发框架 小程序 JavaScript
小程序代码丢失!反编译找回
小程序源代码的容易获取问题确实存在一些潜在的安全隐患。然而,现在的小程序开发框架采用像 Babel 这样的打包工具,将 JavaScript 逻辑代码混合在一个文件中并进行转编译,使其变得难以理解。
30 0
小程序代码丢失!反编译找回
|
2月前
|
JSON 小程序 前端开发
微信小程序的目录结构及页面结构的说明
本文详细介绍了微信小程序的目录结构、页面组成部分以及项目的全局配置文件,阐述了小程序的宿主环境和运行机制,包括小程序启动和页面渲染的过程。
微信小程序的目录结构及页面结构的说明
|
6月前
|
JavaScript Java 测试技术
基于小程序的移动学习平台+springboot+vue.js附带文章和源代码说明文档ppt
基于小程序的移动学习平台+springboot+vue.js附带文章和源代码说明文档ppt
46 0
|
2月前
|
小程序 JavaScript Go
代码总有一个是你想要的分享63个微信小程序源
分享63个微信小程序源代码,包括电商系统、同城拼车、博客等多种应用,涵盖C#、Node.js、Golang等技术栈。每个项目附带源码和示例,适合初学者和开发者参考学习。提取码:8888,代码效果参考:http://www.603393.com/sitemap.xml。
59 2
|
3月前
|
小程序 前端开发 JavaScript
微信小程序实现微信支付(代码和注释很详细)
微信小程序实现微信支付(代码和注释很详细)
|
3月前
|
小程序 JavaScript 前端开发
微信小程序开发必备前置知识:基本代码构成与语法
【8月更文挑战第8天】微信小程序的基本代码构成与语法
101 0
微信小程序开发必备前置知识:基本代码构成与语法
|
3月前
|
小程序 JavaScript 安全
微信小程序实现云闪付支付(代码和注释很详细)
微信小程序实现云闪付支付(代码和注释很详细)
|
5月前
|
XML 小程序 Java
java小程序代码详细展示
java小程序代码详细展示
38 0
|
6月前
|
JavaScript Java 测试技术
基于小程序的订餐系统+springboot+vue.js附带文章和源代码说明文档ppt
基于小程序的订餐系统+springboot+vue.js附带文章和源代码说明文档ppt
34 0
基于小程序的订餐系统+springboot+vue.js附带文章和源代码说明文档ppt
下一篇
无影云桌面