# 逻辑层

# 1. 逻辑层介绍

小程序开发框架的逻辑层使用JavaScript引擎,为小程序提供开发者JavaScript代码的运行环境以及 “人民网+”小程序的特有功能。

逻辑层将数据进行处理后发送给视图层,同时接受视图层的事件反馈。

开发者写的所有代码最终将会打包成一份JavaScript文件,并在小程序启动的时候运行,直到小程序销毁。这一行为类似ServiceWorker,所以逻辑层也称之为App Service

JavaScript 的基础上,我们增加了一些功能,以方便小程序的开发:

  • 增加AppPage方法,进行程序注册和页面注册;
  • 增加getAppgetCurrentPages方法,分别用来获取 App 实例和当前页面栈;
  • 提供模块化能力,每个页面有独立的作用域。

# 2. 注册小程序

# 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

# 3. 注册页面

# 3.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 参考文档 。

# 3.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"}]
  }
})

# 3.2 生命周期回调函数

生命周期的触发以及页面的路由方式详见

# onLoad(Object query)

页面加载时触发。一个页面仅会调用一次,可以在 onLoad 的参数中获取打开当前页面路径中的参数。

参数说明

名称 类型 说明
query Object 打开当前页面路径中的参数

# onShow()

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

# onReady()

页面初次渲染完成时触发。一个页面只会调用一次,代表页面已经准备妥当,可以和视图层进行交互。

注意

对界面内容进行设置的 API 如 jd.setNavigationBarTitle,请在onReady之后进行。

# onHide()

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

# onUnload()

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

# 3.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);
  }
})

# 3.4 组件事件处理函数

Page 中还可以定义组件事件处理函数。在.fxml文件中,组件中加入事件绑定,当事件被触发时,就会执行 Page 中定义的事件处理函数。​

示例代码

<view bindtap="getUsername">获取用户名</view>
Page({
  getUsername: function() {
    console.log("点击了获取用户名")
  }
})

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

# 4. 页面配置

每一个小程序页面也可以使用.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

示例 my.json 如下:

{
  "navigationBarBackgroundColor": "#ffffff",
  "navigationBarTextStyle": "black",
  "navigationBarTitleText": "个人中心",
  "backgroundColor": "#eeeeee",
  "backgroundTextStyle": "light"
}

# 4.1 模块化

# 4.1.1 文件作用域

在.js 文件中声明的变量和方法只在当前文件中有效;不同的文件中可以声明相同名字的变量和方法。

通过全局函数 getApp() 可以获取全局的应用实例,如果需要全局的数据可以在 App() 中设置,如:

// app.js
App({
  globalData: "cortana"
})

# 4.1.2 模块化

可以将一些公共的代码抽离成为一个单独的 js 文件,作为一个模块。模块只有通过 module.exports 或者 exports 才能对外暴露接口。

小程序目前暂不支持「直接引入 node_modules」,需要通过 构建 npm 完成引入工作,需要时也可直接复制代码到小程序的目录中,再进行使用。

// 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 引入模块时,需要使用相对路径。