最近做了一个投票的微信小程序,开发过程主要还是参考官方文档:https://mp.weixin.qq.com/debug/wxadoc/dev/ 由于一直是做 Android 开发的,所以写小程序界面时还需要大概看一下前端的东西,对于这些东西微信自己也做了一些封装,但总体来说差别不大,这里进行一下总结。
注册并创建项目
登陆 https://mp.weixin.qq.com 进行注册,获取 AppID并在设置中进行一些配置(如服务器域名),下载小程序的开发工具,扫描二维码登陆,创建新项目。在创建项目时可以选择创建一个 quick start 项目,这样会自动生成一个简单的 demo,有助于我们了解项目的结构和组成。
但是请注意,坑爹的小程序是不支持个人用户注册的,所以如果不是公司项目,而只是个人想体验一下,要么在创建项目时选择无 AppID,但这样就只能在开发工具的模拟器上运行,不能在手机上运行也不能发布,要么参考 https://zhuanlan.zhihu.com/p/24810538 这篇文章钻个漏洞获取一个AppID,当然还是没办法发布,但至少能在手机上运行了。
框架
小程序的框架分为视图层(View)和逻辑层(App Service),它提供了自己的视图层描述语言 WXML 和 WXSS,以及基于 JavaScript 的逻辑层框架,并在视图层与逻辑层间提供了数据传输和事件系统。
这里的 WXML 和 WXSS 类似于前端的 HTML 和 CSS,但是 WXML 只能使用微信自己定义的组件而不能使用 HTML 里面的标签,WXSS 则和 CSS 无太大差别。
框架的核心是一个响应的数据绑定系统,也就是说当做数据修改的时候,只需要在逻辑层修改数据,视图层就会做相应的更新。
代码结构
上图是我做的投票小程序里面的代码结构:
1、一个小程序主体部分由 app.js、app.json、app.wxss 三个文件组成,必须放在项目的根目录,分别是整个程序的逻辑、全局配置及样式。
(1) app.js 是小程序的脚本代码。通过App()
函数用来注册一个小程序,接受一个 Object 参数,指定小程序的生命周期函数等,如下图所示。
类似于 Android 中 的 Application,我们可以在这个文件中监听并处理小程序的生命周期函数、声明全局变量。
其他地方使用时通过var app = getApp()
即可获取其实例,并调用其中定义的方法和变量,但不要调用生命周期的方法。在App()
的外面还可以另外定义 function 和变量,但只能在本文件内使用。
(2) app.json 是对整个小程序的全局配置。我们可以在这个文件中配置小程序是由哪些页面组成,配置小程序的窗口背景色,配置导航条样式,配置默认标题。
pages
指定了小程序的组成页面,第一个代表小程序的初始页面。
window
用于设置小程序的状态栏、导航条、标题、窗口背景色。
tabBar
用于配置客户端窗口的底部或顶部 tab 栏的样式以及 tab 切换时显示的对应页面。
另外还可以配置各种网络请求的超时时间networkTimeout
和是否开启调试模式debug
。
(3) app.wxss 是整个小程序的公共样式表。可以配置一些通用的样式。
2、pages 里面则是小程序的各个页面,其中 index 一般作为主界面(当然这并不是由名字决定的,而是在app.json里面配置的第一个page),可以看到,一个界面由 wxml、wxss、js、json 等四个文件组成,分别是页面的逻辑、界面结构,样式以及配置。小程序规定这四个文件必须具有相同的路径和名字。
(1) js 是页面的脚本代码。通过Page()
函数用来注册一个页面。接受一个 Object 参数,其指定页面的初始数据、生命周期函数、事件处理函数等,如下图所示。
其中data
定义了页面的初始数据,会以 JSON 的形式由逻辑层传至渲染层,所以其数据必须是可以转成 JSON 的格式:字符串,数字,布尔值,对象,数组。渲染层可以通过 WXML 对数据进行绑定。
onLoad、onShow、onReady、onHide、onUnload
是页面的生命周期函数,分别在页面加载、显示、初次渲染完成、隐藏和卸载时调用。其中onLoad
和onReady
只会在页面加载时调用一次,onShow
则每次显示页面都会调用一次。
onPullDownRefresh
用于监听用户下拉刷新事件,需要在 json 配置文件中开启enablePullDownRefresh
。当处理完数据刷新后,wx.stopPullDownRefresh
可以停止当前页面的下拉刷新。
onShareAppMessage
只有定义了该方法才会在微信的右上角菜单显示分享按钮,需要 return 一个 Object,用于自定义分享内容,包括title
标题和path
分享的页面的完整路径。
viewTap
是事件处理函数,函数名是自己取的,在渲染层可以在组件中加入事件绑定
,当达到触发事件时,就会执行 Page 中定义的事件处理函数。
当需要改变data
中的数据时,不能直接修改this.data
,而需要调用this.setData()
方法进行修改。
在Page()
的外面同样可以另外定义 function 和变量,也只能在本文件内使用。
(2) wxml 是页面的布局文件,只能使用微信自己定义的组件。https://mp.weixin.qq.com/debug/wxadoc/dev/component/ 这里是微信提供的所有组件的列表和属性,其中使用得最多的是 view 以及一些表单组件如 button 等等。具体的布局方式跟HTML差不多,这里不再多说。
(3) wxss 是样式表,具有 CSS 大部分特性,并进行了特性扩展,主要包括:
① 尺寸单位:rpx(responsive pixel)可以根据屏幕宽度进行自适应。规定屏幕宽为750rpx。如在 iPhone6 上,屏幕宽度为375px,共有750个物理像素,则750rpx = 375px = 750物理像素,1rpx = 0.5px = 1物理像素。
② 样式导入:使用@import语句可以导入外联样式表,@import后跟需要导入的外联样式表的相对路径,用;表示语句结束。例如:
/** common.wxss **/
.small-p {
padding:5px;
}
/** app.wxss **/
@import "common.wxss";
.middle-p {
padding:15px;
}
定义在 app.wxss 中的样式为全局样式,作用于每一个页面。在 page 的 wxss 文件中定义的样式为局部样式,只作用在对应的页面,并会覆盖 app.wxss 中相同的选择器。
(4) json 是页面的配置文件,页面的配置比app.json全局配置简单得多,只是设置 app.json 中的 window 配置项的内容,页面中配置项会覆盖 app.json 的 window 中相同的配置项,无需写 window 这个键。上面说的开启下拉刷新功能就需要在这个文件里面进行配置:
由于是 json 格式的文件,即使不需要配置任何东西也需要写{}
,否则会报错。
3、utils 里面包含了一些将公共的代码抽离出来的 js 文件,作为一个模块可以方便被任何地方使用。模块只有通过 module.exports 才能对外暴露接口。下图即为utils/util.js文件中的内容,包含了一个formatDate的方法,并将方法 exports 给外部使用。
在其他地方使用时需要通过var utils = require('../../utils/util.js');
进行引用,之后就可以通过变量 utils 调用 util.js 文件中定义的方法。
4、images 里面则放了一些图片资源。
数据绑定
WXML 中的动态数据均来自对应 Page 的 data。数据绑定使用双大括号将变量包起来,可以作用于内容、组件属性(需要在双引号之内)、控制属性(需要在双引号之内)、关键字(需要在双引号之内)。
例如:
Page({
data: {
message: "Hello",
id: 0,
condition: true
}
})
{{message}}
还可以在 {{}} 内进行简单的运算,如:
Page({
data: {
flag: true,
a: 1,
b: 2,
c: 3
length: 6,
name: 'MINA',
object: {
key: 'Hello '
},
array: ['MINA']
}
})
Hidden
{{a + b}} + {{c}} + d // 结果为3 + 3 + d
{{"hello" + name}}
{{object.key}} {{array[0]}}
条件渲染
在框架中,我们用 wx:if="{{condition}}" 来判断是否需要渲染该代码块:
True
也可以用 wx:elif 和 wx:else 来添加一个 else 块:
1
2
3
因为 wx:if 是一个控制属性,需要将它添加到一个标签上。但是如果我们想一次性判断多个组件标签,我们可以使用一个
view1
view2
注意:
一般来说,wx:if 有更高的切换消耗而 hidden 有更高的初始渲染消耗。因此,如果需要频繁切换的情景下,用 hidden 更好,如果在运行时条件不大可能改变则 wx:if 较好。
列表渲染
在组件上使用wx:for控制属性绑定一个数组,即可使用数组中各项的数据重复渲染该组件。
默认数组的当前项的下标变量名默认为index,数组当前项的变量名默认为item
{{index}}: {{item.message}}
Page({
data: {
array: [{
message: 'foo',
}, {
message: 'bar'
}]
}
})
使用 wx:for-item 可以指定数组当前元素的变量名,使用 wx:for-index 可以指定数组当前下标的变量名:
{{idx}}: {{itemName.message}}
wx:for也可以嵌套,下边是一个九九乘法表
{{i}} * {{j}} = {{i * j}}
类似block wx:if,也可以将wx:for用在
{{index}}:
{{item}}
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 中的输入内容,
① 字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
② 保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字。
Page({
data: {
objectArray: [
{id: 0, unique: 'unique_0'},
{id: 1, unique: 'unique_1'},
{id: 2, unique: 'unique_2'},
],
numberArray: [1, 2, 3, 4]
}
})
{{item.id}}
{{item}}
事件
上面简单提到过
事件分为冒泡事件(当一个组件上的事件被触发后,该事件会向父节点传递)和非冒泡事件(当一个组件上的事件被触发后,该事件不会向父节点传递)。
冒泡事件有:touchstart(手指触摸动作开始)、touchmove(手指触摸后移动)、touchcancel(手指触摸动作被打断,如来电提醒,弹窗)、touchend(手指触摸动作结束)、tap(手指触摸后马上离开)、longtap(手指触摸后,超过350ms再离开)。
除此之外其他组件自定义事件如无特殊申明都是非冒泡事件,如