小程序框架
1. 逻辑层
1.1注册小程序
App(Object)
App() 函数用来注册一个小程序。接受一个 Object 参数,
App() 必须在 app.js 中调用,且只能调用一次。
Object参数说明
属性 | 类型 | 描述 | 触发时机 |
---|---|---|---|
onLaunch | Function | 生命周期回调—监听小程序初始化 | 小程序初始化完成时触发(全局只触发一次) |
onShow | Function | 生命周期回调—监听小程序显示 | 小程序启动,或从后台进入前台显示时触发 |
onHide | Function | 生命周期回调—监听小程序隐藏 | 小程序从前台进入后台时触发 |
onError | Function | 错误监听函数 | 当小程序发生脚本错误,或者 api 调用失败时触发,会带上错误信息 |
onPageNotFound | Function | 页面不存在监听函数 | 小程序要打开的页面不存在时触发,会带上页面信息回调该函数 |
其他 | 不限制 | 开发者可自由添加任意的 function 或数据到 Object 参数中,用this 可访问 |
前台、后台定义
小程序启动后,用户能够看到当前界面,此时小程序处于前台状态, 当用户通过右上角椭圆按钮关闭小程序或者离开宿主app时,小程序并没有立刻终止运行,而是进入了后台状态,此时会触发 onHide 回调事件。
当用户再次进入宿主app或再次打开小程序,小程序又会从后台切换至前台,此时会触发onShow 回调事件; 如果用户长时间没有打开小程序,或者系统资源紧张,小程序可能被销毁,此时小程序会完全退出。
示例代码
App({
onLaunch: function(options) {
console.log("launch 参数",options)
},
onShow: function(options) {
console.log("onShow 参数",options)
},
onHide: function() {
},
onError: function(error) {
console.log("错误信息:",error)
},
globalData: ''
})
onLaunch(Object)
小程序初始化完成时触发,全局仅触发一次。
Object 参数说明
字段 | 类型 | 说明 |
---|---|---|
path | String | 打开小程序的路径 |
query | Object | 打开小程序的 query 字段,可通过分享或者唤起协议中配置 |
referrerInfo | Object | 由另一个小程序或其他 App 进入小程序时,返回此字段 |
referrerInfo.appId | String | 来源小程序的 appId,详见下方说明 |
referrerInfo.extraData | Object 其他来源传过来的数据 |
onShow(Object)
小程序启动,或从后台进入前台时触发,每次切换到前台均会触发。
Object 参数说明
与 onLaunch 一致
onHide()
小程序从前台进入后台时触发,每次切换到后台均会触发
onError(String error)
小程序发生脚本错误,或者 api 调用失败时触发。
参数说明
名称 | 类型 | 说明 |
---|---|---|
error | String | 包含堆栈的错误信息 |
onPageNotFound(Object)
要打开的目标页面不存在时触发,经常用于捕获路由跳转的目标页面不存在情况。
参数说明
名称 | 类型 | 说明 |
---|---|---|
path | String | 不存在的页面的路径 |
query | Object | 打开不存在得页面的 query 参数 |
isEntryPage | Boolean | 是否本次启动的首个页面(例如从分享等入口进来,首个页面是开发者配置的分享页面) |
开发者可以在 onPageNotFound 回调中进行重定向处理,但必须在回调中同步处理,异步处理无效。
示例代码
App({
onPageNotFound(res) {
jd.redirectTo({
url: 'pages/index/index.fxml'
})
}
})
注意
- 如果开发者没有添加 onPageNotFound 监听,当跳转的目标页面不存在时,将由宿主APP接管处理;
- 请确保 onPageNotFound 回调中重定的目标页面存在,否则将由宿主APP接管处理,并且不再回调 onPageNotFound,避免调用死循环。
getApp(Object)
全局方法,getApp() 函数可以用来获取到小程序 App 实例,多用于页面中调用,获取APP实例的全局数据和方法。值得注意的是,在app.js中的 APP()方法中调用时,可通过 this 直接获取到,在其他页面中用 getApp() 方法。
Object 参数说明
字段 | 类型 | 说明 |
---|---|---|
allowDefault | Boolean | 在 App 未定义时返回默认实现。当App被调用时,默认实现中定义的属性会被覆盖合并到 App 中。 |
示例代码
const APP = getApp();
console.log(APP.globalData) // 输出 global data
2.注册页面
2.1 使用 Page 构造器注册页面
Page(Object) 函数用来注册一个页面。接受一个 Object 类型参数,其指定页面的初始数据、生命周期回调、事件处理函数等。
属性 | 类型 | 描述 |
---|---|---|
data | Object | 页面的初始数据 |
onLoad | Function | 生命周期回调—页面加载时触发 |
onShow | Function | 生命周期回调—监听页面显示 |
onReady | Function | 生命周期回调—监听页面初次渲染完成 |
onHide | Function | 生命周期回调—监听页面隐藏 |
onUnload | Function | 生命周期回调—监听页面卸载 |
onPullDownRefresh | Function | 触发下拉刷新时执行 |
onReachBottom | Function | 页面触底时执行 |
onShareAppMessage | Function | 转发 |
onPageScroll | Function | 页面滚动触发事件的处理函数 |
onTabItemTap | Function | 当前是 tab 页时,点击 tab 时触发 |
其他 | Any | 开发者可以添加任意的函数或数据到 Object 参数中,在本页面的函数中用 this 可以访问 |
示例代码
//index.js
Page({
data: {
userName: ""
},
onLoad: function(e) {
this.getName();
},
onReady: function() {
},
onShow: function() {
},
onHide: function() {
},
onUnload: function() {
},
onPullDownRefresh: function() {
},
onReachBottom: function() {
},
onShareAppMessage: function () {
},
onPageScroll: function() {
},
onTabItemTap(item) {
console.log("当前点击的是:",JSON.stringify(item))
},
// Event handler.
getName: function() {
this.setData({
userName:"cortana"
})
},
customData: {
dName: 'cortana'
}
})
详细的参数含义和使用请参考 Page 参考文档 。
2.1.1 初始数据
data 是页面第一次渲染使用的初始数据。
data 中的数据必须是以下类型:字符串,数字,布尔值,对象,数组。
渲染层可以通过 FXML 对数据进行绑定。
示例代码
<view>{{userName}}}</view>
<view>{{cover[0].url}}</view>
Page({
data: {
userName: 'cortana',
cover: [{url: 'http://xxxxx.jpg',title:"avatar"}, {url: 'http://xxxxx.jpg',title:"detail"}]
}
})
2.2 生命周期回调函数
生命周期的触发以及页面的路由方式详见
onLoad(Object query)
页面加载时触发。一个页面仅会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。
参数说明
名称 | 类型 | 说明 |
---|---|---|
query | Object | 打开当前页面路径中的参数 |
onShow()
页面显示/切入前台时触发。
onReady()
页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。
注意
对界面内容进行设置的 API 如 jd.setNavigationBarTitle,请在onReady之后进行。
onHide()
页面隐藏/切入后台时触发。 如 navigateTo 或底部 tab 切换到其他页面,小程序切入后台等。
onUnload()
页面卸载时触发。如 redirectTo 或 navigateBack 到其他页面时。
2.3 页面事件处理函数
onPullDownRefresh()
监听用户下拉刷新事件。
onReachBottom()
监听用户上拉触底事件。
onPageScroll(Object)
监听用户滑动页面事件。
参数说明
属性 | 类型 | 说明 |
---|---|---|
scrollTop | Number | 页面在垂直方向已滚动的距离(单位px) |
onShareAppMessage(Object)
监听用户点击页面内分享按钮(<button>
组件 open-type="share")或右上角菜单“推荐给朋友”按钮的行为,支持自定义转发内容。此事件需要 return 一个 Object,用于配置分享的内容,详见“分享内容配置”。
Object 参数说明
属性 | 类型 | 说明 |
---|---|---|
from | String | 转发事件来源。button:页面内转发按钮;menu:右上角“推荐给朋友”菜单 |
target | Object | 如果 from 值是 button,则 target 是触发这次转发事件的 button,否则为 undefined |
webViewUrl | String | 页面中包含 |
<web-view> 组件时,返回当前 <web-view> 的url |
分享内容配置
属性 | 类型 | 必填 | 说明 |
---|---|---|---|
mpId | string | 否 | 微信小程序id,此场景用于分享到微信后,用户点击分享卡片,进入该appid对应的微信小程序,实现引流到微信小程序 |
title | string | 否 | 转发标题 |
type | number | 否 | 转发形式(0 - 微信小程序正式版 ;1 - 微信小程序开发版;2 - 微信小程序体验版; |
path | string | 否 | 小程序路径 |
mpPath | string | 否 | 微信小程序路径 |
imageUrl | string | 是 | 图片地址(小程序封面图或H5页封面) |
channel | string | 否 | 渠道(不写默认微信朋友,微信朋友圈) |
url | string | 否 | H5链接地址(H5分享填写,不填默认中间页) |
desc | string | 否 | 分享内容摘要 |
示例代码
Page({
onShareAppMessage: function (res) {
console.log(res.target);
return {
mpId: '分享的微信小程序appid',
title: '分享的标题',
type: 0,
desc: '分享的描述、摘要等',
imageUrl: 'http://pic30.finogeeks.com/20130619/9885883_210838271000_2.jpg',
path: 'page/component/index',
mpPath:'分享的微信小程序路径',
channel:'Wxfriends,Wxmoments',
url: 'https://www.finclip.com/develop/index/ao00f99475552b3131',
}
}
})
示例代码分享到微信好友后,会打开对应的正式版微信小程序,分享到朋友圈会打开url对应的H5页面。
onTabItemTap(Object)
点击顶部、底部 tab 时触发
Object 参数说明
参数 | 类型 | 说明 |
---|---|---|
index | String | 被点击 tabItem 的序号,从 0 开始 |
pagePath | String | 被点击 tabItem 的页面路径 |
text | String | 被点击 tabItem 的按钮文字 |
示例代码
Page({
onTabItemTap(item) {
console.log("tabbar点击:",item);
}
})
2.4组件事件处理函数
Page 中还可以定义组件事件处理函数。在.fxml文件中,组件中加入事件绑定,当事件被触发时,就会执行 Page 中定义的事件处理函数。
示例代码
<view bindtap="getUsername">获取用户名</view>
Page({
getUsername: function() {
console.log("点击了获取用户名")
}
})
2.5 使用 Component 构造器构造页面
基础库 1.6.3 开始支持
Page 构造器适用于简单的页面。但对于复杂的页面, Page 构造器可能并不好用。
此时,可以使用 Component 构造器来构造页面。 Component 构造器的主要区别是:方法需要放在 methods: { } 里面。
代码示例
Component({
data: {
text: "This is page data."
},
methods: {
onLoad: function(options) {
// 页面创建时执行
},
onPullDownRefresh: function() {
// 下拉刷新时执行
},
// 事件响应函数
viewTap: function() {
// ...
}
}
})
这种创建方式非常类似于 自定义组件 ,可以像自定义组件一样使用 behaviors 等高级特性。具体细节请阅读 Component 构造器 章节。
Page.route
到当前页面的路径,类型为 String。
示例代码
<view bindtap="getCurrentRoute"> 点击查看当前页面路由 </view>
Page({
getCurrentRoute: function() {
console.log("当前页面route为:",this.route)
}
})
Page.prototype.setData(Object data, Function callback)
setData 函数用于将数据用异步的方式从逻辑层发送到视图层,同时改变对应的 this.data 的值(同步)。
Object 参数说明
字段 | 类型 | 必填 | 描述 |
---|---|---|---|
data | Object | 是 | 本次要改变的数据 |
callback | Function | 否 | setData 引起的界面更新渲染完毕后的回调函数 |
Object 以 key: value 的形式表示,将 this.data 中的 key 对应的值改变成 value。
注意
- 直接修改 this.data 无法改变页面的状态的。
- 仅支持设置可 JSON 化的数据。
- 单次设置的数据量不宜过大,不能超过 1024k。
- 请不要手动把 data 中任何一项的 value 设为 undefined 。
示例代码
<!--index.fxml-->
<view>{{title}}</view>
<button bindtap="changeTitle"> 字符串类型的改变 </button>
<view>{{num}}</view>
<button bindtap="changeNum"> 数值类型的改变</button>
<view>{{array[0].name}}</view>
<button bindtap="changeArray">数组类型的改变 </button>
<view>{{object.name}}</view>
<button bindtap="changeObject">对象类型的改变 </button>
//index.js
Page({
data: {
title: '我是title',
num: 0,
array: [{name: 'cortana'}],
object: {
text: 'init data'
}
},
changeText: function() {
this.setData({
title: '新标题'
})
},
changeNum: function() {
this.setData({
num: this.data.num +1
})
},
changeArray: function() {
this.setData({
'array[0].name':'Mary'
})
},
changeObject: function(){
this.setData({
'object.name': 'Mary'
});
}
})
3. 页面配置
每一个小程序页面也可以使用.json文件来对本页面的窗口表现进行配置。
页面的配置只能设置 app.json 中部分 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项,可配置的选项如下:
属性 | 类型 | 默认值 | 描述 |
---|---|---|---|
navigationBarBackgroundColor | HexColor | #000000 | 导航栏背景颜色,如 #000000 |
navigationBarTextStyle | String | white | 导航栏标题颜色,仅支持 black、white |
navigationBarTitleText | String | 导航栏标题文字内容 | |
navigationBarTitleFixed | Boolean | false | 标题是否固定,设置为 true 则加载H5时,标题不随H5标题变更;设置为 false则会随着H5的title变更 |
backgroundColor | HexColor | #ffffff | 窗口的背景色 |
backgroundTextStyle | String | dark | 下拉 loading 的样式,仅支持 dark、light |
disableScroll | Boolean | false | 设置为 true 则页面整体不能上下滚动;仅在页面配置中有效, |
示例 my.json 如下:
{
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black",
"navigationBarTitleText": "个人中心",
"backgroundColor": "#eeeeee",
"backgroundTextStyle": "light"
}
3.1 模块化
3.1.1 文件作用域
在.js 文件中声明的变量和方法只在当前文件中有效;不同的文件中可以声明相同名字的变量和方法。
通过全局函数 getApp() 可以获取全局的应用实例,如果需要全局的数据可以在 App() 中设置,如:
// app.js
App({
globalData: "cortana"
})
3.1.2 模块化
可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。
小程序目前暂不支持直接引入 node_modules , 需要时,可直接复制代码到小程序的目录中,再引用使用。
// util.js
function getDate() {
return new Date().toLocaleTimeString()
}
module.exports.getDate = getDate
exports.getDate = getDate
在需要使用这些模块的文件中,使用 require(path) 将公共代码引入
var util = require('util.js');
Page({
getDate: function() {
let d = util.getDate();
console.log(d);
}
})
提示
值得注意的是,require 引入模块时,需要使用相对路径。
4. 视图层
框架的视图层由 FXML 与 FTSS 编写,基础单元是组件。
- FXML 用于描述页面的结构,类似于 HTML ;
- FTSS 用于描述组件和页面的样式,是 css 的子集; fts 是小程序的一套脚本语言,基础语法同javascript,结合 FXML,可以创建出页面的结构。
组件 (Component) 是视图的基本组成单元,类似于HTML页面的各种标签,如div、span、img等
4.1 FXML
FXML 是框架设计的一套标签语言,结合基础组件、事件系统,可以构建出页面的结构。
以下是一些简单的示例:
数据绑定
<!--fxml-->
<view> hello {{name}} </view>
// page.js
Page({
data: {
name: 'cortana'
}
})
列表渲染
<!--fxml-->
<view ft:for="{{array}}"> {{item}} </view>
// page.js
Page({
data: {
array: ["苹果", "香蕉", "橘子", "西瓜"]
}
})
条件渲染
<!--fxml-->
<view ft:if="{{type == 1}}"> 类型 1 </view>
<view ft:elif="{{view == 2'}}"> 类型 2 </view>
<view ft:else="{{view == 3}}"> 类型 3 </view>
// page.js
Page({
data: {
type: 1
}
})
模板
<!--fxml-->
<template name="cat">
<view>
age: {{name}}, age: {{age}}
</view>
</template>
<template is="cat" data="{{...cat1}}"></template>
// page.js
Page({
data: {
cat1: {name: 'blue', age: '2'},
}
})
事件
<view bindtap="getDate"> {{date}} </view> //点击事件 bindtap
Page({
data: {
date: ""
},
getDate: function(e) {
this.setData({
date: new Date()
})
}
})
4.2 FTSS
与 CSS FTSS 扩展的特性有:
- 尺寸单位
- 样式导入
尺寸单位
rpx(responsive pixel): 可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
设备 | rpx换算px (屏幕宽度/750) | px换算rpx (750/屏幕宽度) |
---|---|---|
iPhone5 | 1rpx = 0.42px | 1px = 2.34rpx |
iPhone6 | 1rpx = 0.5px | 1px = 2rpx |
iPhone6 Plus | 1rpx = 0.552px | 1px = 1.81rpx |
注意
- 开发FinClip小程序时设计师可以用 iPhone6 作为视觉稿的标准。
- 在较小的屏幕上不可避免的会有一些毛刺,请在开发时尽量避免这种情况。
样式导入
使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。
示例代码
/** common.ftss **/
.small-p {
padding:5px;
}
/** app.ftss **/
@import "common.ftss";
.middle-p {
padding:15px;
}
内联样式
框架组件上支持使用 style、class 属性来控制组件的样式。
style:静态的样式统一写到 class 中。style 接收动态的样式,在运行时会进行解析,请尽量避免将静态的样式写进 style 中,以免影响渲染速度。
<view style="color:{{color}};" />
class:用于指定样式规则,其属性值是样式规则中类选择器名(样式类名)的集合,样式类名不需要带上.,样式类名之间用空格分隔。
<view class="normal_view" />
选择器
目前支持的选择器有:
选择器 | 样例 | 样例描述 |
---|---|---|
.class | .intro | 选择所有拥有 class="intro" 的组件 |
#id | #firstname | 选择拥有 id="firstname" 的组件 |
element | view | 选择所有 view 组件 |
element, element | view, checkbox 选择所有文档的 view 组件和所有的 checkbox 组件 | |
::after | view::after | 在 view 组件后边插入内容 |
::before | view::before | 在 view 组件前边插入内容 |
全局样式与局部样式
定义在 app.ftss 中的样式为全局样式,作用于每一个页面。在 page 的 FTSS 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.ftss 中相同的选择器。
4.3 FTS
FTS(是小程序的一套脚本语言,结合 FXML,可以构建出页面的结构。
注意
FTS 不依赖于运行时的基础库版本,可以在所有版本的小程序中运行。 FTS 与 JavaScript 是不同的语言,有自己的语法,并不和 JavaScript 一致。 FTS 的运行环境和其他 JavaScript 代码是隔离的,FTS 中不能调用其他 JavaScript 文件中定义的函数,也不能调用小程序提供的API。 FTS 函数不能作为组件的事件回调。
以下是一些使用 FTS 的简单示例:
页面渲染
<!--fxml-->
<fts module="m1">
var msg = "hello world";
module.exports.message = msg;
</fts>
<view> {{m1.message}} </view>
页面输出
hello world
数据处理
// page.js
Page({
data: {
array: [1, 2, 3, 4, 5, 1, 2, 3, 4]
}
})
<!--fxml-->
<!-- 下面的 getMax 函数,接受一个数组,且返回数组中最大的元素的值 -->
<fts module="m1">
var getMax = function(array) {
var max = undefined;
for (var i = 0; i < array.length; ++i) {
max = max === undefined ?
array[i] :
(max >= array[i] ? max : array[i]);
}
return max;
}
module.exports.getMax = getMax;
</fts>
<!-- 调用 fts 里面的 getMax 函数,参数为 page.js 里面的 array -->
<view> {{m1.getMax(array)}} </view>
页面输出
5
4.4 FTS 响应事件
背景
当小程序需要实现频繁用户交互的效果时,如果采用常规实现方法,如:
页面有 2 个元素 A 和 B,用户在 A 上做 touchmove 手势,要求 B 也跟随移动,movable-view 就是一个典型的例子。一次 touchmove 事件的响应过程为:
i、touchmove 事件从视图层(View)抛到逻辑层(Service)
ii、逻辑层(Service)处理 touchmove 事件,再通过 setData 来改变 B 的位置
一次 touchmove 的响应需要经过 2 次的逻辑层和渲染层的通信以及一次渲染,通信的耗时比较大。
此外 setData 渲染也会阻塞其它脚本执行,导致了整个用户交互的动画过程会有延迟,交互的实际表现不会很理想。
解决方案
基于以上原因,可以使用 FTS 函数用来响应小程序事件,然后在视图层(View)处理 dom 样式,实现比较好的效果。
目前只能响应内置组件的事件,不支持自定义组件事件。
FTS 函数的除了纯逻辑的运算,还可以通过封装好的 ComponentDescriptor 实例来访问以及设置组件的 class 和样式,对于交互动画,设置 style 和 class 能满足绝大多数需求了。
FTS 函数的例子如下:
var event = function(event, ownerInstance) {
// 获取组件实例
var instance = ownerInstance.selectComponent('.some-component')
instance.setStyle({
color: 'red',
"font-size": '18rpx'
})
instance.setClass('other-class')
return false // 不往上冒泡
}
其中入参 event 是小程序事件对象基础上多了 event.instance 来表示触发事件的组件的 ComponentDescriptor 实例。
ownerInstance 表示的是触发事件的组件所在的组件的 ComponentDescriptor 实例,如果触发事件的组件是在页面内的,则 ownerInstance 表示的是页面实例。
ComponentDescriptor 目前支持的 API 如下:
方法 | 参数 | 描述 |
---|---|---|
selectComponent | selector 对象 | 返回组件的 ComponentDescriptor 实例。 |
selectAllComponents | selector 对象数组 | 返回组件的 ComponentDescriptor 实例数组。 |
setStyle | Object/string | 设置组件样式,支持rpx。设置的样式优先级比组件 wxml 里面定义的样式高。不能设置最顶层页面的样式。 |
addClass/removeClass/hasClass | string | 设置组件的 class。设置的 class 优先级比组件 wxml 里面定义的 class 高。不能设置最顶层页面的 class。 |
callMethod | (funcName:string, args:object) | 调用当前组件/页面在逻辑层(App Service)定义的函数。funcName表示函数名称,args表示函数的参数。 |
getComputedStyle | Array <string> | 指定样式名列表,返回节点对应样式名的当前值。 |
getBoundingClientRect | 无 | 返回节点的尺寸信息。 |
使用方法
FXML 定义事件:
<fts module="event" src="./event.fts"></fts>
<view bindtouchmove="{{event.touchmove}}" class="movable"></view>
注意:FTS 函数必须用 {{}} 括起来。
文件 event.ftx 里面定义并导出函数:
module.exports = {
touchmove: function(event, instance) {
console.log('log event')
},
otherEvent: function(event, instance) {
console.log('log event')
}
}
4.5 基础组件
框架为开发者提供了一系列基础组件,开发者可以通过组合这些基础组件进行快速开发。详细介绍请参考组件文档。
什么是组件
- 组件是视图层的基本组成单元。
- 一个组件通常包括 开始标签 和 结束标签,属性 用来修饰这个组件,内容 在两个标签之内。
<tagname property="value">
Content goes here ...
</tagname>
注意
所有组件与属性都是小写,以连字符-连接
属性类型
类型 | 描述 | 注解 |
---|---|---|
Boolean | 布尔值 | 组件写上该属性,不管是什么值都被当作 true;只有组件上没有该属性时,属性值才为false。如果属性值为变量,变量的值会被转换为Boolean类型 |
Number | 数字 | 1, 2.5 |
String | 字符串 | "string" |
Array | 数组 | [ 1, "string" ] |
Object | 对象 | { key: value } |
EventHandler | 事件处理函数名 "handlerName" 是 Page 中定义的事件处理函数名 | |
Any | 任意属性 |
公共属性
所有组件都有以下属性
属性名 | 类型 | 描述 | 注解 |
---|---|---|---|
id | String | 组件的唯一标示 | 保持整个页面唯一 |
class | String | 组件的样式类 | 在对应的 FTSS 中定义的样式类 |
style | String | 组件的内联样式 | 可以动态设置的内联样式 |
hidden | Boolean | 组件是否显示 | 所有组件默认显示 |
data-* | Any | 自定义属性 | 组件上触发的事件时,会发送给事件处理函数 |
bind* / catch* | EventHandler | 组件的事件 详见事件 |
特殊属性
几乎所有组件都有各自定义的属性,可以对该组件的功能或样式进行修饰,请参考各个组件的定义。
4.6 获取界面上的节点信息
FXML节点信息
节点信息查询 API 可以用于获取节点属性、样式、在界面上的位置等信息。
最常见的用法是使用这个接口来查询某个节点的当前位置,以及界面的滚动位置。
示例代码
const query = ft.createSelectorQuery()
query.select('#the-id').boundingClientRect(function(res){
res.top // #the-id 节点的上边界坐标(相对于显示区域)
})
query.selectViewport().scrollOffset(function(res){
res.scrollTop // 显示区域的竖直滚动位置
})
query.exec()
上述示例中, #the-id 是一个节点选择器,与 CSS 的选择器相近但略有区别,请参见 SelectorQuery.select 的相关说明。
在自定义组件或包含自定义组件的页面中,推荐使用 this.createSelectorQuery 来代替 ft.createSelectorQuery ,这样可以确保在正确的范围内选择节点。
FXML节点布局相交状态
节点布局相交状态 API 可用于监听两个或多个组件节点在布局位置上的相交状态。这一组API常常可以用于推断某些节点是否可以被用户看见、有多大比例可以被用户看见。
这一组API涉及的主要概念如下。
- 参照节点:监听的参照节点,取它的布局区域作为参照区域。如果有多个参照节点,则会取它们布局区域的 交集 作为参照区域。页面显示区域也可作为参照区域之一。
- 目标节点:监听的目标,默认只能是一个节点(使用 selectAll 选项时,可以同时监听多个节点)。
- 相交区域:目标节点的布局区域与参照区域的相交区域。
- 相交比例:相交区域占参照区域的比例。
- 阈值:相交比例如果达到阈值,则会触发监听器的回调函数。阈值可以有多个。 以下示例代码可以在目标节点(用选择器 .target-class 指定)每次进入或离开页面显示区域时,触发回调函数。
示例代码
Page({
onLoad: function(){
ft.createIntersectionObserver().relativeToViewport().observe('.target-class', (res) => {
res.id // 目标节点 id
res.dataset // 目标节点 dataset
res.intersectionRatio // 相交区域占目标节点的布局区域的比例
res.intersectionRect // 相交区域
res.intersectionRect.left // 相交区域的左边界坐标
res.intersectionRect.top // 相交区域的上边界坐标
res.intersectionRect.width // 相交区域的宽度
res.intersectionRect.height // 相交区域的高度
})
}
})
以下示例代码可以在目标节点(用选择器 .target-class 指定)与参照节点(用选择器 .relative-class 指定)在页面显示区域内相交或相离,且相交或相离程度达到目标节点布局区域的20%和50%时,触发回调函数。
示例代码
Page({
onLoad: function(){
ft.createIntersectionObserver(this, {
thresholds: [0.2, 0.5]
}).relativeTo('.relative-class').relativeToViewport().observe('.target-class', (res) => {
res.intersectionRatio // 相交区域占目标节点的布局区域的比例
res.intersectionRect // 相交区域
res.intersectionRect.left // 相交区域的左边界坐标
res.intersectionRect.top // 相交区域的上边界坐标
res.intersectionRect.width // 相交区域的宽度
res.intersectionRect.height // 相交区域的高度
})
}
})
注意
与页面显示区域的相交区域并不准确代表用户可见的区域,因为参与计算的区域是“布局区域”,布局区域可能会在绘制时被其他节点裁剪隐藏(如遇祖先节点中 overflow 样式为 hidden 的节点)或遮盖(如遇 fixed 定位的节点)。
在自定义组件或包含自定义组件的页面中,推荐使用 this.createIntersectionObserver 来代替 ft.createIntersectionObserver ,这样可以确保在正确的范围内选择节点。
4.7 响应显示区域变化
显示区域尺寸
显示区域指小程序界面中可以自由布局展示的区域。在默认情况下,小程序显示区域的尺寸自页面初始化起就不会发生变化。但以下两种方式都可以改变这一默认行为。
在手机上启用屏幕旋转支持
从小程序基础库版本 1.5.33 开始,小程序在手机上支持屏幕旋转。使小程序中的页面支持屏幕旋转的方法是:在 app.json 的 window 段中设置 "pageOrientation": "auto" ,或在页面 json 文件中配置 "pageOrientation": "auto" 。
以下是在单个页面 json 文件中启用屏幕旋转的示例。
代码示例
{
"pageOrientation": "auto"
}
如果页面添加了上述声明,则在屏幕旋转时,这个页面将随之旋转,显示区域尺寸也会随着屏幕旋转而变化。
从小程序基础库版本 1.5.33 开始, pageOrientation 还可以被设置为 landscape ,表示固定为横屏显示。
在 iPad 上启用屏幕旋转支持
从小程序基础库版本 1.5.33 开始,在 iPad 上运行的小程序可以支持屏幕旋转。使小程序支持 iPad 屏幕旋转的方法是:在 app.json 中添加 "resizable": true 。
代码示例
{
"resizable": true
}
如果小程序添加了上述声明,则在屏幕旋转时,小程序将随之旋转,显示区域尺寸也会随着屏幕旋转而变化。
注意
在 iPad 上不能单独配置某个页面是否支持屏幕旋转。
Media Query
有时,对于不同尺寸的显示区域,页面的布局会有所差异。此时可以使用 media query 来解决大多数问题。
代码示例
.my-class {
width: 40px;
}
@media (min-width: 480px) {
/* 仅在 480px 或更宽的屏幕上生效的样式规则 */
.my-class {
width: 200px;
}
}
屏幕旋转事件
有时,仅仅使用 media query 无法控制一些精细的布局变化。此时可以使用 js 作为辅助。
在 js 中读取页面的显示区域尺寸,可以使用 selectorQuery.selectViewport 。
页面尺寸发生改变的事件,可以使用页面的 onResize 来监听。对于自定义组件,可以使用 resize 生命周期来监听。回调函数中将返回显示区域的尺寸信息。(从基础库版本 1.5.33 开始支持。)
代码示例
Page({
onResize(res) {
res.size.windowWidth // 新的显示区域宽度
res.size.windowHeight // 新的显示区域高度
}
})
4.8 页面路由
在小程序中所有页面的路由全部由小程序框架进行管理。
页面栈
框架以栈的形式维护了当前所有页面。 当发生路由切换的时候,页面栈的表现如下:
路由方式 | 页面栈表现 |
---|---|
初始化 | 新页面入栈 |
打开新页面 | 新页面入栈 |
页面重定向 | 当前页面出栈,新页面入栈 |
页面返回 | 页面不断出栈,直到目标返回页 |
Tab | 切换 页面全部出栈,只留下新的 Tab 页面 |
重加载 | 页面全部出栈,只留下新的页面 |
getCurrentPages()
getCurrentPages() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。
注意
- 不要尝试手动修改页面栈,会导致路由以及页面状态错误。
- 不要在 App.onLaunch 的时候调用 getCurrentPages(),此时 page 还没有生成。
路由方式
对于路由的触发方式以及页面生命周期函数如下:
路由方式 | 触发时机 | 路由前页面 | 路由后页面 |
---|---|---|---|
初始化 | 小程序打开的第一个页面 | onLoad, onShow | |
打开新页面 | 调用 API ft.navigateTo 或使用组件 <navigator open-type="navigateTo"/> | onHide | onLoad, onShow |
页面重定向 | 调用 API ft.redirectTo 或使用组件 <navigator open-type="redirectTo"/> | onUnload | onLoad, onShow |
页面返回 | 调用 API ft.navigateBack 或使用组件 <navigator open-type="navigateBack"> 或用户按左上角返回按钮 | onUnload | onShow |
Tab | 切换 调用 API ft.switchTab 或使用组件<navigator open-type="switchTab"/> 或用户切换 Tab | 各种情况请参考下表 | |
重启动 | 调用 API ft.reLaunch 或使用组件 <navigator open-type="reLaunch"/> | onUnload | onLoad, onShow |
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() |
提示
- navigateTo, redirectTo 只能打开非 tabBar 页面。
- switchTab 只能打开 tabBar 页面。
- reLaunch 可以打开任意页面。
- 页面底部的 tabBar 由页面决定,即只要是定义为 tabBar 的页面,底部都有 tabBar。
- 调用页面路由带的参数可以在目标页面的 onLoad 中获取。