# 小程序框架

# 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'
    }) 
  }
})

注意

  1. 如果开发者没有添加 onPageNotFound 监听,当跳转的目标页面不存在时,将由宿主APP接管处理;
  2. 请确保 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。​

注意

  1. 直接修改 this.data 无法改变页面的状态的。
  2. 仅支持设置可 JSON 化的数据。
  3. 单次设置的数据量不宜过大,不能超过 1024k。
  4. 请不要手动把 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() 函数用于获取当前页面栈的实例,以数组形式按栈的顺序给出,第一个元素为首页,最后一个元素为当前页面。​

注意

  1. 不要尝试手动修改页面栈,会导致路由以及页面状态错误。
  2. 不要在 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 中获取。
© 2021 凡泰极客
  • 免费试用
  • 编组
  • 编组 2