十一期间没什么事,我试着写了一个微信小程序WeSJTU。现在,回过头来,把自己开发小程序的心得给总结一下。
构思
微信小程序的文档,比较难的部分,如数据绑定,其思想和AngularJS是非常相近的,而所谓的WXSS,基本上可以认为是CSS,只是不方便用SCSS了——WXSS原则上不支持级联,所以整个文档看下来基本上就能上手了。
先从简单有意思的开始吧,所以我设想了三个Tab:
同去。同去网是交大活动发布的主要阵地,它有很完善的API,可以通过GET请求返回活动列表、活动详情的JSON数据,另外还支持分页。
2048. 根据别人写的数字2048,打算改成文字2048——苟利国家生死以,岂因祸福避趋之,蛤蛤...
反馈。这一部分,想做一个简单的反馈功能,用户可以在小程序里反馈,我可以在网页上查看反馈内容列表。
代码
同去
活动列表页面,前端可以用小程序的列表渲染很方便地实现。
<scroll-view class="acts-list" scroll-y="true" bindscrolltolower="lower">
<block wx:for="{{actsList}}">
<view class="act-item" index="{{index}}" id="{{item.actid}}" catchtap="redictDetail">
...
</view>
</block>
<view class="load-more" hidden="{{moreHidden}}">
<view class="load-content">
<text class="weui-loading"></text>
<text class="loading-text">玩命加载中</text>
</view>
</view>
</scroll-view>由于同去网不支持HTTPS,而小程序的wx.request发起的是HTTPS请求,所以需要在后端将请求转发一下。后端我用的是Node.js.
router.get('/acts', function(req, res, next) {
var query = require('url').parse(req.url, true).query;
var offset = query.offset;
var order = query.order;
var indexUrl = "http://tongqu.me/index.php/api/act/type?type=0&offset=" + offset + "&order=" + order;
http.get(indexUrl, function(response) {
var source = "";
response.on('data', function(data) {
source += data;
});
response.on('end', function() {
source = JSON.parse(source);
var actsList = [];
var acts = source.result.acts;
var actsLength = acts.length;
for (i = 0; i < actsLength; i++) {
var poster = acts[i].poster;
var status = acts[i].time_status_str;
var status_style = '';
if (!poster) {
var index = Math.round(Math.random() * 6);
var array = [1, 2, 4, 5, 7, 8, 9];
poster = "http://tongqu.me/images/act_default_poster/" + array[index] + ".jpg";
}
if (status == "人数已满") {
status_style = "act-status-full";
}
if (status == "未开始报名") {
status_style = "act-status-todo";
}
if (status == "报名已结束") {
status_style = "act-status-done";
}
var act = {
actid: acts[i].actid,
poster: poster,
name: acts[i].name,
status: status,
status_style: status_style,
time: acts[i].start_time.substr(5) + " ~ " + acts[i].end_time.substr(5),
location: acts[i].location,
source: acts[i].source,
views: acts[i].view_count,
members: acts[i].member_count
};
actsList.push(act);
}
res.send({
actsList: actsList
});
});
});
});活动详情页面,由于活动内容有文字有图片,所以列表渲染的思路是——文字就渲染文字,图片就渲染图片。
<scroll-view class="act-contents" scroll-y="true">
<block wx:for="{{actContents}}">
<view wx:if="{{item.viewIf}}" class="act-paragraph">
<text wx:if="{{item.textIf}}" class="act-text">{{item.text}}</text>
<view wx:if="{{item.imageIf}}" class="{{item.imageCenter}}"><image mode="aspectFit" src="{{item.image}}"></image></view>
</view>
</block>
</scroll-view>为了配合前端,后端需要做好数据的处理。
router.get('/act', function(req, res, next) {
var query = require('url').parse(req.url, true).query;
var id = query.id;
var indexUrl = "http://tongqu.me/index.php/api/act/detail?id=" + id;
http.get(indexUrl, function(response) {
var source = "";
response.on('data', function(data) {
source += data;
});
response.on('end', function() {
source = JSON.parse(source);
var main_info = source.main_info;
var poster = main_info.photolink;
var status = main_info.time_status_str;
var status_style = '';
if (!poster) {
var index = Math.round(Math.random() * 6);
var array = [1, 2, 4, 5, 7, 8, 9];
poster = "http://tongqu.me/images/act_default_poster/" + array[index] + ".jpg";
}
if (status == "人数已满") {
status_style = "act-status-full";
}
if (status == "未开始报名") {
status_style = "act-status-todo";
}
if (status == "报名已结束") {
status_style = "act-status-done";
}
var actContents = [];
var $ = cheerio.load(source.body);
if ($("p").length) {
$("p").each(function() {
var text = $(this).text().trim();
var textIf = true;
if (!text) {
textIf = false;
}
var $img = $(this).children("img");
var imageIf = false;
var image = "";
var imageCenter = "";
if ($img.length !== 0) {
imageIf = true;
image = $img.attr("src");
imageCenter = "image-center";
}
var viewIf = true;
if (!(textIf || imageIf)) {
viewIf = false;
}
var actContent = {
viewIf: viewIf,
text: text,
textIf: textIf,
image: image,
imageIf: imageIf,
imageCenter: imageCenter
};
actContents.push(actContent);
});
} else {
var actContent = {
viewIf: true,
text: $.text().trim(),
textIf: true,
image: "",
imageIf: false,
imageCenter: ""
};
actContents.push(actContent);
}
var actDetail = {
poster: poster,
name: main_info.name,
status: status,
status_style: status_style,
time: main_info.start_time.substr(5) + " ~ " + main_info.end_time.substr(5),
location: main_info.location,
source: main_info.source,
actContents: actContents
};
res.send(actDetail);
});
});
});2048
2048,代码的思路还是比较清晰的——通过触摸动作返回的坐标值判断手指滑动方向,然后移动合并,判断输赢,随机插入等。其中,数字转文字的代码是通过循环实现的,简单粗暴。
number2word: function(arr) {
var numbersArray = [0, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16384];
var wordsArray = ['他', '苟', '利', '国', '家', '生', '死', '以', '岂', '因', '祸', '福', '避', '趋', '之'];
var wordnumbers = arr;
for (var i = 0; i < 4; i++) {
for (var j = 0; j < 4; j++) {
var index = numbersArray.indexOf(wordnumbers[i][j].number);
wordnumbers[i][j].word = wordsArray[index];
}
}
return wordnumbers;
}用模态框提醒游戏结束。
<modal class="modal" confirm-text="吼啊" cancel-text="不吼" hidden="{{failHidden}}" bindconfirm="modalConfirm" bindcancel="modalCancle">
<view>图样图森破!</view>
<view>重新开始,吼不吼啊?</view>
</modal>
<modal class="modal" confirm-text="吼啊" cancel-text="不吼" hidden="{{successHidden}}" bindconfirm="modalConfirm" bindcancel="modalCancle">
<view>比一般人不知高到哪里去!</view>
<view>再来一次,吼不吼啊?</view>
</modal>感觉膜得也太暴力了...
反馈
前端是一个表单,反馈内容由用户输入,反馈者的基本信息由小程序的wx.getUserInfo获得。
<form class="feedback-form" bindsubmit="formSubmit">
<view class="feedback-form-wrapper">
<view class="feedback-title">期待您的反馈</view>
<view class="input-wrapper">
<input class="feedback-input {{inputStyle}}" name="feedback" placeholder="建议或吐槽..." bindfocus="inputFocus" bindblur="inputBlur" />
</view>
<view class="btn-wrapper">
<button class="feedback-btn" formType="submit" type="primary" plain="true">提交</button>
</view>
</view>
</form>后端将前端通过POST请求提交的表单数据写入到SQLite中。
router.post('/feedback', function(req, res, next) {
var nickname = req.body.nickname;
var gender = req.body.gender;
var content = req.body.content;
if (gender == 1) {
gender = "男";
} else if (gender == 2) {
gender = "女";
} else {
gender = "未知";
}
db.run("INSERT INTO feedback (time, nickname, gender, content) VALUES ($time, $nickname, $gender, $content)", {
$time: moment().format('MM-DD HH:mm'),
$nickname: nickname,
$gender: gender,
$content: content
}, function(err) {
var data = "success";
if (err) {
data = "fail";
}
res.send(data);
});
});然后,通过网页就可以很方便地查看了。
<div class="feedback">
<div class="feedback-wrap">
<div class="feedback-title">
<p>反馈列表</p>
</div>
<table class="feedback-table">
<thead>
<tr>
<th class="col1">时间</th>
<th class="col2">昵称</th>
<th class="col3">性别</th>
<th class="col4">内容</th>
</tr>
</thead>
<tbody>
<% var i=0; feedbackList.forEach(function(item) { if(i<5) { %>
<tr>
<td class="col1td"><%= item.time %></td>
<td class="col2td"><%= item.nickname %></td>
<td class="col3td"><%= item.gender %></td>
<td class="col4td"><%= item.content %></td>
</tr>
<% } i++; }) %>
</tbody>
</table>
</div>
</div>效果
同去

tongqu

tongqu-detail
2048

2048-entry

2048
反馈

about
小程序WeSJTU的前端代码已经在GitHub上开源了——点此前往,欢迎学习交流。
个人技术博客 biebu.xin,原文链接——微信小程序WeSJTU开发心得 - 别不信