小程序
简介
何为小程序
- 在用户的角度上,就是便捷的跟用户连接的方式。
- 在技术的角度上,就是在微信巨人的肩膀上快速便捷的开发程序。
小程序的组成
- JSON配置
- WXML模板
- WXSS样式
- JS脚本
JSON配置(以.json结尾的文件)
1. 作用
JSON其实我们都很了解,就是一种数据格式。那么它在小程序充当的角色就是配置的作用。比如app.json,可以进行页面注册的配置。
2. 语法
跟JS对象表达式很相像,但是有一些差别
- key值必须用双引号包裹起来,并且不能用单引号
- 数字,包括浮点数和整数
- 字符串,需要包裹在双引号中
- bool值,true或者false
- 数组,需要包裹在方括号中
- 对象,需要包裹在大括号中
- NULL
- 其他格式都会报undefined
- 不能添加注释
WXML模板(以.wxml结尾的文件)
WXML可以类比HTML,语法上很多相似的地方。
1. 语法
注释
基本语法
<标签名 属性名1="属性值1" 属性名2="属性值2" ...> ...标签名>
- 严格闭合
- 大小写敏感
2.数据绑定
web开发是使用JS通过DOM接口进行动态的渲染页面,小程序使用数据绑定进行动态渲染。
1.基本语法:
通过{{变量名}}的形式进行动态的绑定
//WXML模板
{{time}}
//JS
Page({
data: {
time: (new.Date()).toString()
},
})
2. 属性也可以进行动态绑定,但是需要包括在双引号内
//WXML模板
{{time}}
//JS
Page({
data: {
height: 1
},
})
3. 除了动态绑定,也可以在该语法下绑定常量
//WXML模板
{{1,2,3}}
- 变量名对大小写敏感
- 没有定义的变量或者设置为undefined的变量不会同步到WXML模板。
3.逻辑语法
1. 三元运算
//WXML模板
{{a === 10 ? "a等于10的要执行的语句" : "a不等于10的要执行的语句"}}
2. 字符串拼接
{{
name: world
}}
//WXML模板
{{"hello" + name}}
输出hello world
3. 算术运算
{
a:10,
b:2,
c:5,
d:2
}
{{a + b}} + {{c}} + d
输出:12 + 5 + d
4. 条件逻辑
if else,例子如下:
6
1
3
如果需要对多个标签进行判断,则使用
1
2
4.列表渲染
1. 默认
for循环,默认下标的变量名是index,默认当前项变量名为item,下标是从1开始
{{index}}: {{item.messge}}
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
输出:
0:foo
1:bar
2.自定义
可以自定义下标和当前项的变量名
{{idx}}: {{itemName.message}}
3.块
{{index}}:
{{item}}
WXSS样式
1. 文件组成
- app.wxss是项目公共样式
- 页面样式是跟WXML模板同级的WXSS样式
2. 尺寸单位
我们以前描述图片一般都是使用像素px。但是现在很多类型的手机的屏幕有很多种,所以小程序使用rpx相对的单位来进行描述。
小程序进行编译之后,会对xpr进行换算,这样就能够相对屏幕,不会太多空白。以375物理像素为基准,也就是在一个宽度为375物理像素的屏幕下,1rpx = 1px。举个例子:iPhone6屏幕宽度为375px,共750个物理像素,那么1rpx = 375 / 750 px = 0.5px。
样式使用方式
引用样式
@import './test_0.wxss'
内联样式
选择器
类型 | 选择器 | 样例 | 样例描述 |
---|---|---|---|
类选择器 | .class | .intro | 选择所有拥有 class="intro" 的组件 |
id选择器 | #id | #firstname | 选择拥有 id="firstname" 的组件 |
元素选择器 | element | view checkbox | 选择所有文档的 view 组件和所有的 checkbox 组件 |
伪元素选择器 | ::after | view::after | 在 view 组件后边插入内容 |
伪元素选择器 | ::before | view::before | 在 view 组件前边插入内容 |
权重
view{ // 权重为 1
color: blue
}
.ele{ // 权重为 10
color: red
}
#ele{ // 权重为 100
color: pink
}
view#ele{ // 权重为 1 + 100 = 101,优先级最高,元素颜色为orange
color: orange
}
view.ele{ // 权重为 1 + 10 = 11
color: green
}
官方样式库
https://github.com/Tencent/we...
JS脚本
ECMAScript
小程序的JS是由ECMAScript和小程序框架和小程序API来实现.
小程序执行环境
不同手机的平台不同,ECMAScript标准不同,小程序开发工具做了一个功能可以方便去控制,不需要程序员做考虑。
开发者需要在项目设置中,勾选 ES6 转 ES5 开启此功能。
作用域
文件声明的变量和方法只在该文件下有用
当需要使用全局变量的时候getApp()获取全局实例,
// a.js
// 获取全局变量
var global = getApp()
global.globalValue = 'globalValue'
// b.js
// 访问全局变量
var global = getApp()
console.log(global.globalValue) // 输出 globalValue
如果要将这个全局变量全部可以使用在App()中进行设置
// app.js
App({
globalData: 1
})
模块化
web开发的变量是可以被后续加载的脚本访问或者改写的,但是我们明白了上一节的作用域之后,在小程序是做不到这样的,那么如果我们需要用其他文件的方法怎么使用?
小程序将每一个JS文件作为一个模块,通过module.exports 或者 exports 对外暴露接口。
// moduleA.js
module.exports = function( value ){
return value * 2;
}
// B.js
// 在B.js中引用模块A
var multiplyBy2 = require('./moduleA')
var result = multiplyBy2(4)
脚本的执行顺序
- 按照加载的模块的顺序
- 小程序会按照开发者在 app.json 中定义的 pages 的顺序,逐一执行。
小程序宿主环境
上一节我们明白了小程序是由JSON,WXML,WXSS,JS等文件组成,那么这些文件是怎么进行交互的,怎么协同工作的?我们现在就需要去了解小程序的宿主环境。宿主环境会提供给我们微信客户端的能力。
宿主环境
分类
宿主环境分为逻辑层和渲染层
- 逻辑层。JS文件运行在逻辑层。
- 渲染层。WXML模板和WXSS样式运行渲染层。
如何渲染
- 渲染层使用{{}}语法绑定变量
- 逻辑层负责产生,处理数据。
- 逻辑层通过Page实例的setData方法传递数据给渲染层。
通讯模型
渲染层和逻辑层分别由两个线程管理。渲染层使用WebView进行渲染,逻辑层使用 JsCode线程进行渲染。一个程序有多个页面,所以会有多个线程,WebView和JsCode线程最终通过微信客户端进行中转。
数据驱动
我们一开始了解的如何渲染只是一个初步了解,那么里面到底是怎么运作的呢?我们知道数据和试图应该联动变化。所以小程序就有一种方法,可以实现数据变化试图也随之变化的技术,叫数据驱动。
WXML模板可以等价于一个DOM树,通过JS也可以表示DOM树。例子如下:
//WXML模板
a
b
c
//JS
{
name: "view",
children:[
{name:"view", children:[{text:"a"}]},
{name:"view", children:[{text:"b"}]},
{name:"view", children:[{text:"c"}]},
]
}
数据驱动就是先将WXML模板转换为JS对象,然后使用Page的setData方法,给对象赋值,然后将JS对象渲染为WXML模板。过程如下:
//WXML模板
{{msg}}
//JS对象
Page({
data: {
msg: "hello world!"
}
})
转换WXML模板为下面的JS对象
{
name:"view",
children: [{text: msg}]
}
使用Page实例的setData方法将msg替换为JS对象的值。
{
name:"view",
children: [{text: "hello world"}]
}
最终将JS对象转换为WXML模板
hello world
程序与页面
程序
1. 程序构造器
1. 宿主环境提供一个App()构造器来注册一个App.
2. 构造器接受一个Object参数,参数说明如表1.1,其中构造器的回调函数:onLaunch / onShow / onHide /onError
3. 这个App是单例的。
4. 构造器App()必须放在根目录的app.js里面。
5. 在其他js脚本可以通过getApp()来获取程序实例。
//app.js
App({
onLaunch: function(options) {},
onShow: function(options) {},
onHide: function() {},
onError: function(msg) {},
globalData: 'I am global data'
})
// other.js
var appInstance = getApp()
表1.1
参数属性 | 类型 | 描述 |
---|---|---|
onLaunch | Function | 当小程序初始化完成时,会触发 onLaunch(全局只触发一次) |
onShow | Function | 当小程序启动,或从后台进入前台显示,会触发onShow |
onHide | Function | 当小程序从前台进入后台,会触发 onHide |
onError | Function | 当小程序发生脚本错误,或者 API 调用失败时,会触发 onError 并带上错误信息 |
其他字段 | 任意 | 可以添加任意的函数或数据到 Object 参数中,在App实例回调用 this 可以访问 |
2. 程序的生命周期和打开场景
生命周期
- 初次进入小程序,微信客户端初始化好宿主环境,同时从网络下载或者从本地缓存拿到小程序的代码包,把它注入到宿主环境,初始化完毕,微信客户端就会给App实例派发onLauch事件,App构造器参数定义的onLaunch方法会被调用
- 进入小程序之后,用户可以点击右上角的关闭,或者按手机设备的Home键离开小程序,此时小程序并没有被直接销毁,我们把这种情况称为“小程序进入后台状态”,App构造器参数所定义的onHide方法会被调用。
- 当再次回到微信或者再次打开小程序时,微信客户端会把“后台”的小程序唤醒,我们把这种情况称为“小程序进入前台状态”,App构造器参数所定义的onShow方法会被调用。
特点:这些回调函数都是微信客户端根据用户操作主动触发的,为了避免程序上的混乱,我们不应该从其他代码主动调用App实例的生命周期函数。
打开场景
从群聊会话里打开,从小程序列表中打开,通过微信扫一扫二维码打开,从另外一个小程序打开当前小程序等
针对不同途径的打开方式,小程序有时需要做不同的业务处理,所以微信客户端会把打开方式带给onLaunch和onShow的调用参数options
字段 | 类型 | 描述 |
---|---|---|
path | String | 打开小程序的页面路径 |
query | Object | 打开小程序的页面参数query |
scene | Number | 打开小程序的场景值,详细场景值请参考小程序官方文档 |
shareTicket | String | shareTicket,详见小程序官方文档 |
referrerInfo Object 当场景为由从另一个小程序或公众号或App打开时,返回此字段
referrerInfo.appId String 来源小程序或公众号或App的 appId,详见下方说明
referrerInfo.extraData Object 来源小程序传过来的数据,scene=1037或1038时支持
页面
1.文件构成和路径
- 页面,逻辑,配置。页面由WXML模板和WXSS样式描述,逻辑由JS文件描述,配置由JSON文件描述。
- 一个页面的文件需要放置在同一个目录下,其中WXML文件和JS文件是必须存在的,JSON和WXSS文件是可选的。
- 页面路径需要在小程序代码根目录app.json中的pages字段声明,否则这个页面不会被注册到宿主环境中。例如两个页面的文件的相对路径分别为 pages/index/page. 和 pages/other/other.
2.页面构造器Page()
- 宿主环境提供了 Page() 构造器用来注册一个小程序页面
- Page()在页面脚本page.js中调用
- Page构造器接受一个Object参数,大致参数如下;
参数属性 | 类型 | 描述 |
---|---|---|
data | Object | 页面的初始数据 |
onLoad | Function | 生命周期函数--监听页面加载,触发时机早于onShow和onReady |
onReady | Function | 生命周期函数--监听页面初次渲染完成 |
onShow | Function | 生命周期函数--监听页面显示,触发事件早于onReady |
onHide | Function | 生命周期函数--监听页面隐藏 |
onUnload | Function | 生命周期函数--监听页面卸载 |
onPullDownRefresh | Function | 页面相关事件处理函数--监听用户下拉动作 |
onReachBottom | Function | 页面上拉触底事件的处理函数 |
onShareAppMessage | Function | 用户点击右上角转发 |
onPageScroll | Function | 页面滚动触发事件的处理函数 |
其他 | Any | 可以添加任意的函数或数据,在Page实例的其他函数中用 this 可以访问 |
3. 页面的生命周期和打开参数
- 页面初次加载的时候,微信客户端就会给Page实例派发onLoad事件,Page构造器参数所定义的onLoad方法会被调用,onLoad在页面没被销毁之前只会触发1次
- 页面显示之后,Page构造器参数所定义的onShow方法会被调用,一般从别的页面返回到当前页面时,当前页的onShow方法都会被调用。
- 在页面初次渲染完成时,Page构造器参数所定义的onReady方法会被调用,onReady在页面没被销毁前只会触发1次,onReady触发时,表示页面已经准备妥当,在逻辑层就可以和视图层进行交互了。以上三个事件触发的时机是onLoad早于 onShow,onShow早于onReady。
- 页面不可见时,Page构造器参数所定义的onHide方法会被调用,这种情况会在使用wx.navigateTo切换到其他页面、底部tab切换时触发。
- 当前页面使用wx.redirectTo或wx.navigateBack返回到其他页时,当前页面会被微信客户端销毁回收,此时Page构造器参数所定义的onUnload方法会被调用。
4.页面数据
上一节我们知道Page构造器有一个data参数,这个参数用来初始化页面需要的数据,那么我们怎么动态的改变数据呢?使用this.setData的方法进行动态的改变数据。
// page.js
Page({
onLoad: function(){
this.setData({
text: 'change data'
}, function(){
// 在这次setData对界面渲染完毕后触发
})
}
})
此外需要注意以下3点:
- 直接修改 Page实例的this.data 而不调用 this.setData 是无法改变页面的状态的,还会造成数据不一致。
- 由于setData是需要两个线程的一些通信消耗,为了提高性能,每次设置的数据不应超过1024kB。
- 不要把data中的任意一项的value设为undefined,否则可能会有引起一些不可预料的bug。
5.页面的用户行为
小程序宿主环境提供了四个和页面相关的用户行为回调:
1.下拉刷新 onPullDownRefresh
监听用户下拉刷新事件,需要在app.json的window选项中或页面配置page.json中设置enablePullDownRefresh为true。当处理完数据刷新后,wx.stopPullDownRefresh可以停止当前页面的下拉刷新。
2.上拉触底 onReachBottom
监听用户上拉触底事件。可以在app.json的window选项中或页面配置page.json中设置触发距离onReachBottomDistance。在触发距离内滑动期间,本事件只会被触发一次。
3.页面滚动 onPageScroll
监听用户滑动页面事件,参数为 Object,包含 scrollTop 字段,表示页面在垂直方向已滚动的距离(单位px)。
4.用户转发 onShareAppMessage
只有定义了此事件处理函数,右上角菜单才会显示“转发”按钮,在用户点击转发按钮的时候会调用,此事件需要return一个Object,包含title和path两个字段,用于自定义转发内容,如代码清单3-13所示。
6.页面跳转和路由
路由方式 | 触发时机 | 路由前页面生命周期 | 路由后页面生命周期 |
---|---|---|---|
初始化 | 小程序打开的第一个页面 | onLoad, onShow | |
打开新页面 调用 | API wx.navigateTo | onHide | onLoad, onShow |
页面重定向 调用 | API wx.redirectTo | onUnload | onLoad, onShow |
页面返回 调用 | API wx.navigateBack | onUnload | onShow |
重启动 调用 | API wx.reLaunch | onUnload | onLoad, onShow |