微信小程序学习笔记

Author Avatar
dev.liang 6月 08, 2017
  • 在其它设备中阅读本文章

自从2017年1月9日,张小龙在 2017 微信公开课 Pro 上发布的小程序正式上线以来,小程序一直都是一个很火的话题,谁都不敢肯定的说小程序可以成为一种趋势,影响原生 APP 的市场,但小程序确实有很多自身独有的特点和优势。

最近因公司的一个小程序开发比赛,燃气了我学习小程序得热情,网上的资料也不多,学习的话也只能查阅官方提供的文档,所以想在这里记录下自己学习过程中的重点和一些开发技巧,笔记写的比较零碎,因之前没有好好学习过 JS,所以以下笔记有些理解是按照移动开发思想来写的,后期有时间会好好整理下,笔记会随着学习持续更新的,相关代码可以在我的 GitHub 上查看。

小程序的一些特点

小程序目前处于公测状态,个人开发者想获取到微信开发者账号,目前还是比较困难。

小程序适合做简单的、用完即走的应用。

小程序适合低频的应用。

小程序适合性能要求不高的应用。

小程序不支持加载 WebView。

没有小程序账号的一些限制

不能上传和发布

不能真机运行,只可以在 PC的模拟器中运行

录音、网络状态、罗盘、拨打电话等功能 无法使用

获取用户信息的流程是模拟的而不是真实的

目前小程序的这些限制,但是不影响我们学习小程序开发。

学习细节记录,关于调试

扫一扫后,点击小程序本地项目

调试界面会有以 .js 和 .js[sm] 后缀结尾的文件,调试的话 要在 带有 .js[sm] 后缀的文件中打断点。

编写第一个小程序

.js javascript 文件

.json 是系统或者页面的配置文件,配置文件中不能添加注释,否则编译时候报错。

.wxml 是编写小程序的骨架文件,可以理解为 HTML 文件

.wxss 样式文件

在工程下,以 app 开头的文件属于全局配置或者样式文件。<view> 和 <div> 标签 的作用相当;只有写在 text 标签里包括的文字,才可以在手机里实现长按选中复制操作

分辨率:物理分辨率,逻辑分辨率。

page 标签

可在 page 里面设置背景色,以及在状态栏里通过 windows 标签设置小程序的状态栏、导航条、标题、窗口背景色。
page 标签,在样式表里书写时候不能写成 .page。

1
2
3
4
page{
height: 100%;
background-color: #b3d4db;
}

关于小程序设计效果图:

以 iphone6 的 750 个像素的宽度做效果图,假如 一个图片的像素是 200 个像素,则在小程序里 直接就写 200rpx,他们的转换关系是 1 :1 的。

在开发过程中,水平布局的样式推荐使用 rpx 为单位,横向如果元素单一视情况也可使用 px为单位,纵向布局的间距推荐 px。

小程序实战

Swiper 组件使用

基础属性大家可以在官方文档中查找并使用,
swiper 里有个特别的属性需要提一下 vertical=”true” 纵向滚动,使图片可以纵向滚动。

js 文件结构,和 page 页面的声明周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
Page({

/**
* 页面的初始数据
*/
data: {
// 设置数据绑定,定义数据变量
},

/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {

},

/**
* 生命周期函数--监听页面初次渲染完成
*/
onReady: function () {

},

/**
* 生命周期函数--监听页面显示
*/
onShow: function () {

},

/**
* 生命周期函数--监听页面隐藏
*/
onHide: function () {

},

/**
* 生命周期函数--监听页面卸载
*/
onUnload: function () {

},

/**
* 页面相关事件处理函数--监听用户下拉动作
*/
onPullDownRefresh: function () {

},

/**
* 页面上拉触底事件的处理函数
*/
onReachBottom: function () {

},

/**
* 用户点击右上角分享
*/
onShareAppMessage: function () {

}
})

弹性模型

/**需要先声明是弹性模型*/
display: flex;
/**设置弹性模型的方向*/
flex-direction:column;

数据绑定

如果绑定的数据是在标签的属性上,需要添加添加双引号 “{{ }}”
如果绑定的数据是在标签的属性上,需要添加添加双引号 “{{ }}”

如果绑定的数据在标签之间,则不需要添加 “”, eg:<text>{{ }}</text>

数据绑定扩展

所有属性值是 boolean 的时候,如果有需要设置为 false 的时候,都需要写成 false 才可生效。因为如果写成 false ,小程序会默认为是有值的,从而编译为 true。

两个花括号结合起来的数据绑定也是可以生效的
<text>{{data1}}{{data2}}</text>

用 wx:if=”{{}}” 来控制 image 的显示和隐藏
<image> wx:if=”{{img_visible}}”></image>

列表渲染 wx-for(for循环)

for 循环的布局样式都需要写在 <block/> 中,大家可以把
<block/> 标签理解为一个大括号 {}。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- 
item 可以理解为条目数据的对象
index 可以理解为 索引
且 item 和 index 两个值都是 <block/> 标签中引用时候的默认值,可直接打 . 来调用
wx:for-item="item" wx:for-index="index" 这两个标签都可以不用再 <block/>标签中声明,直接使用 item index 打 . 调用即可。
-->

<block wx:for="{{数据源或者放置数据源的 key}}"
wx:for-item="item"
wx:for-index="index">
<image src="{{item.leaderliang.png}}"></image>
<text>{{item.title}}{{index}}</text>
</block>

在使用 wx:for 的时候,会报一个警告;类似于这样:

1
2
3
4
5
6
7
8
9
./pages/post/post.wxml
Now you can provide attr "wx:key" for a "wx:for" to improve performance.
| </swiper>
|
> | <block wx:for="{{posts_key}}" wx:for-item="item" wx:for-index="index">
| ^
| <view catchtap="onPostItemTap">
| <!-- is = 的是模板的名字-->
| <template is="postItem" data="{{...item}}" />

官方给的解释是这样:

1
2
3
4
5
6
7
8
9
10
11
12
这个不是bug,是一个关于性能优化方面的提示

具体参见 wx:key
如果列表中项目的位置会动态改变或者有新的项目添加到列表中,并且希望列表中的项目保持自己的特征和状态(如 <input/>中的输入内容,<switch/> 的选中状态),需要使用 wx:key 来指定列表中项目的唯一的标识符。

wx:key 的值以两种形式提供

字符串,代表在 for 循环的 array 中 item 的某个 property,该 property 的值需要是列表中唯一的字符串或数字,且不能动态改变。
保留关键字 *this 代表在 for 循环中的 item 本身,这种表示需要 item 本身是一个唯一的字符串或者数字,如:
当数据改变触发渲染层重新渲染的时候,会校正带有 key 的组件,框架会确保他们被重新排序,而不是重新创建,以确保使组件保持自身的状态,并且提高列表渲染时的效率。

如不提供 wx:key,会报一个 warning, 如果明确知道该列表是静态,或者不必关注其顺序,可以选择忽略。

个人尝试了下 设置 wx:key=””,暂且可以消除警告,不知道后期会不会有其他问题,随后会再研究一下。

小程序的事件机制

bindtap=”方法名”
view 的事件监听,但是会有冒泡的情况发生

catchtap=”方法名”
可以阻止冒泡的执行

template 的使用

目前 template 只支持 复用标签和样式,还不支持 js

在创建 template 后, .wxml 中会把公共的 view 布局放置进去,数据绑定的参数 item.title/date/avatar 等等属性 可以直接把 item 去掉,但是需要配合在相应 依赖的 .wxml 中把之前 设置的数据绑定处 data 的值前加 “…”,三个点,加了三个点之后,相当于把这个对象平铺了。如

1
2
3
4
<block wx:for="{{posts_key}}" wx:for-item="item" wx:for-index="index">
<!-- is = 的是模板的名字-->
<template is="postItem" data="{{...item}}" />
</block>

另外在使用 template 模板的时候,需要注意,当模板里面有用到图片的地方,路径切忌不能是相对路径,要写为绝对路径,因为毕竟是模板文件,会有很多界面使用到,层级什么的会有不一样,为了避免后期不必要的 bug,建议统一使用绝对路径!

组件自定义属性 及 获取属性

在一个 view 标签里添加 catchtap 点击事件后,可以在 view 标签里添加自定义数据,通过事件传递数据。

如:

1
2
3
<view catchtap="onPostItemTap" data-postItemId="{{item.postId}}">
......
</view>

在 js 文件中接收 data 数据,示例代码如下:

1
2
3
4
5
onPostItemTap: function(event){
var postId = event.currentTarget.dataset.postitemid;
var postId = event.currentTarget.dataset.dataId;
console.log("postId=" + postId);
}

event.currentTarget.dataset.dataId;

event 框架提供的事件对象

currentTarget 当前点击的组件,对应的就是上面的 view

currentTarget 和 target 区别
target 指的是当前点击的组件
currentTarget 指的是事件最终捕获的组件

dataset 所有自定义数据的集合,可以在这个集合下面寻找我们定义的数据

currentTarget、dataset 都是 Object 的。

另外在 view 标签里面定义的 data-postItemId 数据,在 onPostItemTap 事件中接收的时候 postItemId 中的连接符 和 大写的字母 都会被去掉和转换成小写,所以接收的时候,需要多注意一下。

bug 专栏

另外在这里记录一下在编写小程序时候报的一些 bug:

在创建新的界面时候我们或许会把 .js、.json、.wxml、.wxss 这四个文件都给创建,但是在页面跳转的过程中,假如新创建的界面里面只有一个文本,或者没有样式和配置的时候,就会报下面这些错:

1
2
navigateTo:fail url "pages/post/post-detail/post-detail" is not in app.json
遇到类似于这样的就你的 系统配置文件 app.json 中没有配置 对应新创建的页面,配置一下路径就好啦。

再比如:

1
2
3
pages/post/post-detail/post-detail.json
未找到入口 pages/post/post-detail/post-detail.json 文件,或者文件读取失败,请检查后重新编译。
遇到这样的就是因为新创建的页面的 .json 文件是空的,如果没有配置需要配置,可以在文件中写入 “{}”,一个大括号,就搞定啦

再或者:

1
2
pages/post/post-detail/post-detail 出现脚本错误或者未正确调用 Page()
遇到这样的,就是因为新创建的页面的 .js 文件 中需要配置一下 page 方法。

以上都是比较基础的问题,仔细看下报错日志,多试一下,再不行的话可以在官方的开发者社区搜一下相关问题,或者发起提问,基本都可以解决的。

属性解释

z-index

z-index 属性设置元素的堆叠顺序。拥有更高堆叠顺序的元素总是会处于堆叠顺序较低的元素的前面。
注释:元素可拥有负的 z-index 属性值。
注释:Z-index 仅能在定位元素上奏效(例如 position:absolute;)!
说明:该属性设置一个定位元素沿 z 轴的位置,z 轴定义为垂直延伸到显示区的轴。如果为正数,则离用户更近,为负数则表示离用户更远

font-weight 字体的粗细

1
2
3
font-weight:normal;
font-weight:bold;
font-weight:900;

切换图片的两种方式

可通过 if else 来进行设置

1
2
<image wx:if="{{checked}}" catchtap="onCheckTap"  src="/images/icon/check.png"/>
<image wx:else catchtap="onCheckTap" src="/images/icon/un_check.png"/>

通过数据绑定的形式

1
2
3
<image class="audio" catchtap="onAudioTap" 
src="{{isPlayingMusic ? '/images/music/music-stop.png' : '/images/music/music-start.png'}}">
</image>

应用程序的生命周期

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
以下代码需要在工程根目录下的 app.js 中进行编写,目前小程序应用程序的生命周期有四个方法

App({

gloableData:{
// 音乐是否在播放
g_isPlayingMusic: false,
// 哪一个音乐在播放
g_currentMusicPostId: null
},

/**
* 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
*/
onLaunch: function () {

},

/**
* 当小程序启动,或从后台进入前台显示,会触发 onShow
*/
onShow: function (options) {

},

/**
* 当小程序从前台进入后台,会触发 onHide
*/
onHide: function () {

},

/**
* 当小程序发生脚本错误,或者 api 调用失败时,会触发 onError 并带上错误信息
*/
onError: function (msg) {

}
})

如果我们的某个界面需要用到小程序 app.js 中的相关功能或函数,则需在相应的界面中声明一个 app 变量

1
2
// 获取小程序实例
var app = getApp();

进而即可通过 app 变量 来调用 app.js 中对象的属性

1
2
3
app.gloableData.g_isPlayingMusic = true;
// 音乐播放,设置存储对应播放的 postId
app.gloableData.g_currentMusicPostId = that.data.currentPostId;

tab 选项卡

tabBar
如果我们的小程序是一个多 tab 应用(客户端窗口的底部或顶部有 tab 栏可以切换页面),那么我们可以通过 tabBar 配置项指定 tab 栏的表现,以及 tab 切换时显示的对应页面。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
在 app.json 文件中进行配置 tab
{
"tabBar": {
"borderStyle": "white",
"position": "top",
"selectedColor": "#000000",
"list": [
{
"pagePath": "pages/post/post",
"text": "文章",
"iconPath": "images/tab/yuedu.png",
"selectedIconPath": "/images/tab/yuedu_hl.png",
"backgroundColor": "#000000",
"selectedColor": "#000000"
},
{
"pagePath": "pages/movies/movies",
"text": "电影",
"iconPath": "/images/tab/dianying.png",
"selectedIconPath": "images/tab/dianying_hl.png",
"backgroundColor": "#000000"
}
]
}
}

tabBar 页面跳转方法

wx.switchTab(OBJECT)
跳转到 tabBar 页面,并关闭其他所有非 tabBar 页面

Tip:

需要配置在 list 外边,否则无效

“borderStyle”: “white”,
“position”: “top/bottom”,
“selectedColor”: “#000000”,
当设置 position 为 top 时,将不会显示 icon
tabBar 是一个数组,只能配置最少2个、最多5个 tab,tab 按数组的顺序排序

在小程序中快速创建文件和文件夹的方式

1
2
3
4
5
6
7
8
9
10
11
/**
* 在 app.json 文件中 pages 配置项下直接编写文件路径,然后 command + s 或 (ctrl + s)
* ,即可在左侧工程下生成相应的目录和文件;但目前我使用的开发版本号是 0.17.171900
* ,系统已经支持一次性创建四个文件的功能,直接在你想要的目录下新建目录即可实
*/
"pages": [
"pages/welcome/welcome",
"pages/post/post",
"pages/movies/movies",
"pages/post/post-detail/post-detail"
],

小程序调用服务器数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
官方示例代码
wx.request({
url: 'test.php', //仅为示例,并非真实的接口地址
data: {
x: '' ,
y: ''
},
header: {
'content-type': 'application/json'
},
success: function(res) {
console.log(res.data)
}
})

个人实际操作时代码如下


onLoad: function (options) {
wx.request({
url: 'https://api.douban.com/v2/movie/top250',
data: {},
method: 'GET',// 默认为 GET,有效值:OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
header: {
'content-type': 'application/xml'
},
success: function (res) {
console.log(res.data)
},
fail: function(){
console.log("request fail")
},
complete: function(){

}
})
}

其中填写 header 时,遇到些 bug,在这里总结下:

因为临时用的豆瓣的电影 api,在配置时 header 中按照官方示例填写如下

1
2
3
header: {
'content-type': 'application/json'
},

请求豆瓣服务器返回如下

1
2
3
4
5
GET https://api.douban.com/v2/movie/top250 400 (Bad Request)
Object {msg: "Invalid request", code: 999, request: "GET /v2/top250"}
code:999
msg:"Invalid request"
request:"GET /v2/top250"

后来尝试把 vaule 修改为空字符串,或者随便的其他字符,后来多次测试发现随便改个值就可以请求成功啦。

以下是官方的一些 Bug & Tip:

content-type 默认为 ‘application/json’
开发者工具 0.10.102800 版本,header 的 content-type 设置异常;
客户端的 HTTPS TLS 版本为1.2,但 Android 的部分机型还未支持 TLS 1.2,所以请确保 HTTPS 服务器的 TLS 版本支持1.2及以下版本;
要注意 method 的 value 必须为大写(例如:GET);
url 中不能有端口;
request 的默认超时时间和最大超时时间都是 60s;
request 的最大并发数是 5;
网络请求的 referer 是不可以设置的,格式固定为 https://servicewechat.com/{appid}/{version}/page-frame.html 其中 {appid} 为小程序的 appid,{version} 为小程序的版本号,版本号为 0 表示为开发版。