前段时间完成了公司小程序的开发,一个做 iOS App 的人,做起小程序的感觉就是,JS 写原生 App 也可以这么流畅,原生开发者要丢饭碗啦!原先也研究过一段时间的 React Native,因为列表滚动太卡顿的性能限制,也就失去了研究的兴趣,现在看小程序,别有一番滋味。

也还是在前几天,Apple 封杀了 JSPatch。微博里流传的是这样一则笑话:“Apple: 听说 iOS 开发者没人要了?”。 不管历史的车轮怎么转,小程序很值得原生开发者学习下。

断断续续,写了好几篇小程序的文章,感觉自己写的东西都在微信开发者文档里,就一扔再扔。反复几次,终于有了这么一篇文章。不说基础,直接上 Demo:

效果

从 4 个地方说起:

  • 布局
  • 模板
  • 请求接口数据
  • 页面传值
  • 下拉刷新 上拉加载
  • 分享

布局

采用 Flex 布局,对于 Flex 布局可以参考阮一峰老师的这两篇文章:

Flex 布局教程:语法篇

Flex 布局教程:实例篇

我们的页面每一行是长这个样子的(有时候会有图片 1 张或者多张,有时会没有):

长图片

页面结构代码如下:

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
<view class="postItem">
<view>
<image class="userAvatar" src="{{ userAvatar }}"></image>
</view>
<view class="rightView">
<text class="userName">{{ user_nick_name }}</text>
<text class="postTitle">
{{ title }}
</text>
<text class="postContent">
{{ subject }}
</text>
<view class="imagesBgView">
<view class="imageBgView" wx:key="property" wx:for="{{ imageList }}">
<image src="{{ item }}" class="postImage" catchtap='imageTapped' data-images="{{ imageList }}" data-current="{{ item }}" mode='aspectFill'/>
</view>
</view>
<view class="postMoreInfoView">
<view>
<text class="postTimeText">{{ last_reply_date }}</text>
<text class="postTypeText">{{ board_name }}</text>
</view>
<view class="postViewCountCommentBgView">
<image src="http://iyiming-10052166.cos.myqcloud.com/eye.png" class="eyeImage"/>
<text class="viewCountCommentCountText">{{ hits }}</text>
<image src="http://iyiming-10052166.cos.myqcloud.com/comment.png" class="commentImage" />
<text class="viewCountCommentCountText">{{ replies }}</text>
</view>
</view>
<view class="splitLineView" />
</view>
</view>

页面样式如下:

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
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
.postItem {
display: flex;
flex-direction: row;
}
.userAvatar {
width:80rpx;
height:80rpx;
margin-top: 20rpx;
margin-left:20rpx;
border-radius: 50%;
background-color:#F3F3F3;
}
.userName {
color: #78C51C;
font-size:32rpx;
}
.rightView {
display: flex;
flex-direction: column;
margin-top: 20rpx;
margin-left: 10rpx;
width: 100%
}
.postTitle {
font-size: 32rpx;
margin-right: 20rpx;
margin-top: -20rpx;
}
.postContent {
margin-top: -10rpx;
font-size: 28rpx;
margin-right: 20rpx;
line-height: 38rpx;
color: #393939
}
.imagesBgView {
display: flex;
flex-wrap: wrap
}
.imageBgView {
margin-right: 20rpx;
margin-top: 10px
}
.postImage {
width: 190rpx;
height: 190rpx;
background-color:#F3F3F3;
}
.postMoreInfoView {
display: flex;
justify-content: space-between;
margin-top: 10rpx
}
.postTimeText {
margin-top: 20rpx;
font-size: 22rpx;
color: #999999;
}
.postTypeText {
margin-left:10rpx;
margin-top: 20rpx;
font-size: 22rpx;
color: #999999;
}
.postViewCountCommentBgView {
margin-right: 28rpx;
}
.eyeImage {
width: 26rpx;
height: 18rpx;
}
.viewCountCommentCountText {
margin-top: 20rpx;
margin-left: 10rpx;
font-size: 22rpx;
color: #999999;
}
.commentImage {
width: 26rpx;
height: 18rpx;
margin-left: 10rpx;
}
.addPost {
position: fixed;
bottom: 30rpx;
right: 30rpx;
width: 90rpx;
height: 90rpx
}

模板

对于我们重复的每一行,微信提供了模板的功能,让我们更方便的重用代码,具体用法如下:

在 templates 文件夹里,添加一个文件夹 postItem 并创建文件:postItem.wxml。

1
2
3
<template name="postItem">
...
</template>

在使用的地方,需要先引入再使用:

1
2
3
<import src="../../templates/postItem/postItem.wxml" />
<template is="postItem" data="{{ ...postInfo }}" />

请求接口数据

微信提供了很方便的接口请求方式:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
wx.request({
url: 'https://URL',
data: {},
method: 'GET', // OPTIONS, GET, HEAD, POST, PUT, DELETE, TRACE, CONNECT
// header: {}, // 设置请求的 header
success: function(res){
// success
},
fail: function() {
// fail
},
complete: function() {
// complete
}
})

返回的数据都存在 res.data 里。

页面传值

从 A 页面传值到 B 页面,这个很简单:

1
2
3
wx.navigateTo({
url: '../postDetail/postDetail?board_id=' + boardID + '&topic_id=' + topicID
})

从 A 页面导航到 B 页面,B 页面点击按钮后,需要传值给 A 页面,可以在 B 页面将数据保存起来,再在 A 页面中获取。这样做显然不太好,有没有更好的办法?我这里使用了页面栈:

比如说页面 B 的某个 item 点击时,可以使用下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
itemTapped: function (e) {
var item = e.currentTarget.dataset.info
var pages = getCurrentPages();
var currPage = pages[pages.length - 1]; //当前页面
var prevPage = pages[pages.length - 2]; //上一个页面
//直接调用上一个页面的setData()方法,把数据存到上一个页面中去
prevPage.setData({
plate: item
})
console.log(prevPage.data)
wx.navigateBack()
}

在 gif 图这个例子里,我们有个预览图片的功能,点击某张图片进入图片预览,左右滑动,查看上一张下一张图片,先来看看微信给提供的预览图片的 API:

1
2
3
4
5
6
7
8
9
10
11
12
13
wx.previewImage({
// current: 'String', // 当前显示图片的链接,不填则默认为 urls 的第一张
urls: [StringArray],
success: function(res){
// success
},
fail: function() {
// fail
},
complete: function() {
// complete
}
})

点击图片时,我们需要传入当前点击的图片,还需要传入图片数组:

wxml 内容如下:

1
<image src="{{ item }}" class="postImage" catchtap='imageTapped' data-images="{{ imageList }}" data-current="{{ item }}" mode='aspectFill'/>

js 内容如下:

1
2
3
4
5
6
imageTapped: function (e) {
wx.previewImage({
current: e.currentTarget.dataset.current, // 当前图片
urls: e.currentTarget.dataset.images // 图片数组
});
}

下拉刷新 上拉加载

对于下拉刷新、上拉加载,微信有直接给我们使用的 API:

1
2
3
4
5
6
onPullDownRefresh: function () {
// 上拉刷新
},
onReachBottom: function () {
// 到达底部 实现上拉加载
}

一开始,我没有弄出这种上拉刷新效果,使用的 scroll-view 代替的,虽然能达到目的。体验不是很好走,了些弯路,还是换成了上面的这种方式,但是这里有一个小小的坑:

  1. 在要实现上拉刷新页面的 json 文件里,添加 "enablePullDownRefresh": true
  2. app.json 文件里 window 里要把 backgroundTextStyle 设置为 dark。要不和默认的白色背景都是白色,看不出来是下拉刷新,这个就是上面所说的坑。

知道上面的两点,就能很好的实现下拉刷新。下拉加载顺其自然。

分享

要为某个页面实现分享功能,在页面的 js 文件里添加如下代码就可以了:

1
2
3
4
5
6
7
8
Page({
onShareAppMessage: function () {
return {
title: '自定义分享标题',
path: '/page/user?id=123'
}
}
})

注意下路径,写对就好了。

另外微信阅读里有本《微信小程序入门指南》值得初学者看下,整体上讲的很细。小程序审核现在还很严格,要各种良民证。