模板结构:
<view class="box_start {{item.have == 1 ? 'on':'out'}}" catchtap="onRecommStart" data-id="{{item.id}}" data-parentid="{{item.first_letter}}" data-have="{{item.have}}"></view>
收藏按钮处理方法:
onRecommStart:function(e){
let that = this;
let user = wx.getStorageSync('user') || {};
let id = e.currentTarget.dataset.id;
let parentid = e.currentTarget.dataset.parentid;
let have = e.currentTarget.dataset.have;
let list = vm.$data().authors; //新增
let alist = {"pid":parentid,"aid":id}
wx.showLoading({
title: '正在处理',
});
if (!have){
Api.fetchPost(Api.collection,{userid:user.openid,id:id,type:1,have:1}, (err, res) => {
if (res.ret == 200){
wx.hideLoading();
} else {...}
});
}else{
Api.fetchPost(Api.collection,{userid:user.openid,id:id,type:1,have:0}, (err, res) => {
if (res.ret == 200){
wx.hideLoading();
} else {...}
});
}
//此处找到操作的元素位置
list[parentid].map(item => {
if(item.id == id){
item.have = !item.have;
}
return item
});
vm.$set({authors:list});
setTimeout(function(){
//此处为刷新顶部收藏栏数据
that.getColAutData();
},1000)
},
4处为栏目收藏区域,使用了scroll-view组件,左右滑动方式方便用户查看自己已经收藏的栏目。需要注意的是需要在小程序onLoad或onShow时,取到栏目的个数,再计算组件整体宽度。
模板:
<scroll-view scroll-x class="scrollcolumns">
<view class="scroll-view" style="width:{{wWidhth}}rpx">
<view class="first"></view>
<block wx:for="{{columnColtDatas}}" wx:key="item">
....
</block>
</view>
</scroll-view>
逻辑(请注意wWidhth值的计算):
...
colData.data.map(item => {
Api.fetchGet(Api.column + item, (err, res) => {
if (res.data) {
columnAutData.push(res.data.channel);
if (columnAutData.length == colData.data.length) {
vm.$set({ columnColtDatas: columnAutData, wWidhth: (colData.data.length * 694) + 44, dataReady: true });
};
};
});
});
...
1.8 个人中心功能

个人中心主简单呈现个人信息、用户收藏的作者/栏目统计、用户已浏览的文章记录。值得注意的是,页面onShow周期时需要刷新用户的收藏统计信息。
1.9 浏览记录功能
浏览记录模块在个人中心页面中:
1.数据来源为用户浏览文章时的上报,服务端做时间戳记录(浏览去重)等工作。
2.在开发列表加载逻辑时,需要注意验证一下拿到数据的一致性。因为运营端可能已经删掉某篇文章,而用户的上报的浏览记录又是过去时,所以对于这种情况的发生,需要在数据字段做标记、或者在删稿流程上形成通知机制。
1.10 评论功能
因为信息审核和登录态的问题,腾讯大家小程序评论功能折中选择调用【珊瑚评论】记录接口,仅做评论内容展示。
1.11 分享功能(含首页)

分享功能都在onShareAppMessage()函数里,不同于右上角分享按钮,如果在页面中某个地方添加分享功能,需要button绑定属性open-type=”share”。除此之外,还需要相关分享属性如:
<button class="choice-share-b" catchtap="onShareAppMessage" open-type="share" data-title="{{item.title}}" data-tid="{{item.tid}}"></button>
1.12 评分功能

评分功能在文章底层页中,用户对文章的评分操作会形成:
1.这一篇文章的评分数据依据。
2.这一篇文章在栏目的栏目评分依据
3.这一篇文章作者的评分依据
在开发中,评分功能由多个功能函数组成,大致可以分为渲染、用户操作、服务器操作回调、还有数据换算等一些函数方法。
1.13 海报生成功能

此功能报用于单篇文章及作者朋友圈传播海报生成。
生成功能需要注意以下:
1.海报的生成使用小程序canvas组件(canvas功能及api能力详见官网文档)。
2.对于图片素材,例如背景,二维码等图标,需要wx.downloadFile()函数支持(详见文后封装的常用函数)。
3.图片保存使用wx.canvasToTempFilePath()方法,调试阶段建议使用wx.previewImage()来调试。
4.对于二维码及素材的加载时机,根据自己业务场景来处理。
5.不同机型每行的文字大小及换行,需要用函数来处理。
6.熟悉理解scene参数,理解小程序不同方式(如扫码)打开场景值。
7.理解wx.createSelectorQuery()接口。
8.对于圆角的头像处理,最好交给后端进行图像处理。前端canvas处理的话需要考虑内存开销,当图片太大时不适合。
9.文中的小程序码为B码,微信官方给到的为图片二进制流,需要做接口类型指定处理。
10.适当将素材进行base64,并进行本地缓存。
对于文字类型的canvas绘图,需要经常计算字符多少,换行计算。分享一下这两个函数:
getTrueLength: function(str) {
let len = str.length,
truelen = 0;
for (let x = 0; x < len; x++) {
if (str.charCodeAt(x) > 128) {
truelen += 2;
} else {
truelen += 1;
}
}
return truelen;
},
cutString: function(str, leng) {
let len = str.length,
tlen = len,
nlen = 0;
for (let x = 0; x < len; x++) {
if (str.charCodeAt(x) > 128) {
if (nlen + 2 < leng) {
nlen += 2;
} else {
tlen = x;
break;
}
} else {
if (nlen + 1 < leng) {
nlen += 1;
} else {
tlen = x;
break;
}
}
}
return tlen;
}
1.14 消息模板(暂未上线)

消息模板根据产品的实际业务来做开发,建议低频的推送用户。
小程序模板功能中需要向接口传递formId。在发送给用户id上,建议合理的进行分组(如用户订阅栏目或者作者openid进行分组)。如果发生文章更新,推送文章更新的消息模板。
二、样式表现
2.1 雪碧图合并技巧
小程序中出现了一个新单位rpx(responsive pixel),官方规定屏幕宽度为20rem,规定屏幕宽为750rpx。(在开发前尽量和视觉设计老师约定好设计文稿,例如750像素宽的设计稿能方便我们开展工作)。
雪碧图:

雪碧图自动生成图片及代码(建议灵活使用,本工具用于移动端项目雪碧图生成):

工具链接: https://code.ahthw.com/tools/csssprite/
2.2 tabBar导航栏图标大小建议
tabBar常规为图标搭配标题,具体配置可参考官方文档:链接 https://developers.weixin.qq.com/miniprogram/dev/framework/config.html

对比官方参考,大家小程序略去了tabBar.list.text配置,这样的处理方式主要是为还原设计稿。每个图标素材的像素大小为81px*81px,通过尝试:文字区域建议35px。

2.3 wxml数据绑定中巧用三元运算
合理的使用三元运算,使代码更简洁。
样式模板举例:
<view class="box_start {{item.have == 1 ? 'on':'out'}}" catchtap="onRecommStart">...</view>
<view wx:if="{{item.tag == 'title' || item.tag == 'text'}}" class="{{item.tag}} {{item['level'] ? 'h2' : ''}}">...</view>
<view class="choice-dajia-view" style='height:{{viewIsshow == false ? windowHeight:"auto"}}' wx:if="{{!choiceWarp}}">...</view>
内容显示:
<view ...>{{item.have == 1 ? '取消收藏:'收藏''}}</view>
2.4 wxss技巧
1.text-align:justify;可以将内容左右对齐,使内容外观更整齐。
2.原生组件层级特别高,例如canvas,在长页面时会留下阴影,巧用position:fixed属性,在父层级可以使页面整体长度等于视窗高度,避免阴影出现。
3.巧用-webkit-line-clamp属性,如
word-break:break-all;
display:-webkit-box;
-webkit-line-clamp:2;
-webkit-box-orient:vertical;
overflow:hidden;
表示当一段文字超过2行时,出现省略号。这样在开发时不担心文字多少,也不要求接口数据对字符串长度限制,并且不需要前端进行函数截取。
4.当页面未加载成功时,loading展示尽量以样式、本地的base64文件及css3动画组成,提高页面性能。
5.对于可以预处理的数据,可以先提前加载渲染好,用样式操控显示隐藏。
6.rpx可以用在背景元素等css less属性上。
7.@import “*.wxss”的使用能更好的进行样式复用。
8.为显示的图片view做一个背景样式,容错图片打不开等意外因素。
9.使用image组件的mode=”widthFix”,可以保证文章底层中配图宽度不变的情况下高度自适应。
三、代码开发维护
3.1 Wxpage框架
腾讯大家小程序选用wxpage框架。【链接】 https://github.com/tvfe/wxpage
WXPage 是一个极其轻量的微信小程序开发框架,其中的API蕴含了“极致页面打开速度的思想”,为可维护性与开发效率而设计的功能,框架来自“腾讯视频”小程序的项目沉淀。
框架对小程序生命周期的扩展(如onNavigate、onAppLaunch等很多有意思的扩展)、组件的依赖、实例方法(如 emit、$put)、实用函数等都有一系列独特的包装,适用于组件开发。
特别感谢sendguan(关开设)在大家小程序开发中无私支持。
3.2 工具方法模块化管理
这里的工具方法指的是一些公用的方法或代码。通常根据业务的需要,我们可以建立一到多个模块,在模块里封装一些公用方法,一来方便调用,二来方便维护,如:
//Api.js let Api = {
fun1: function() {
...
},
fun2: function() {
...
},
};//接口module.exports = {
fun1: fun1
};
这里分享一些大家小程序开发中封装的方法:
function downFile(url, callback) {
wx.downloadFile({
url: url,
success: function(res) {
callback(res.tempFilePath)
}
})
}// get请求方法function fetchGet(url, callback) {
wx.request({
url: url,
header: { 'Content-Type': 'application/json' },
success(res) {
callback(null, res.data)
},
fail(e) {
console.error(e)
callback(e)
}
})
}// post请求方法function fetchPost(url, data, callback) {
wx.request({
url: url,
method: 'POST',
header: { "Content-Type": "application/x-www-form-urlencoded" },
data: data,
success(res) {
callback(null, res.data)
},
fail(e) {
console.error(e)
callback(e)
}
})
}function fetchData(url, data, callback) {
wx.request({
method: 'GET',
url: url,
data: data,
success(res) {
callback(res.data)
},
fail(e) {
console.error(e)
callback(e)
}
})
}
function removeBlock(s) {
let regex = "\\((.+?)\\)";
return s.match(regex)[1]
};
function removeAt(target, index) {
return !!target.splice(index, 1).length;
}
function remove(target, item) {
let index = target.indexOf(item);
return index > -1 ? removeAt(target, index) : false;
}
function getDateDay(str) {
let string = str.toString().substr(0, 10)
return string.replace(/-/g, '.');
}
function sliceArray(array, size) {
let result = [];
for (let x = 0; x < Math.ceil(array.length / size); x++) {
let start = x * size;
let end = start + size;
result.push(array.slice(start, end));
}
return result;
};
function getArrayItems(arr, num) {
let temp_array = new Array();
for (let index in arr) {
temp_array.push(arr[index]);
}
let return_array = new Array();
for (let i = 0; i < num; i++) {
//判断如果数组还有可以取出的元素,以防下标越界
if (temp_array.length > 0) {
let arrIndex = Math.floor(Math.random() * temp_array.length);
return_array[i] = temp_array[arrIndex];
temp_array.splice(arrIndex, 1);
} else { break; }
}
return return_array;
}
function reWirteUrl(url) {
//console.log(url);
if (url !== null) {
if (!/^(http:\/\/)/i.exec(url)) {
return url.replace(/(http:)?(?=\/\/img1\.gtimg\.com\/)/g, 'https:');
} else if (url.indexOf("https://") == -1) {
return url.replace("http://", "https://");
}
} else {
return 'https://mat1.gtimg.com/news/images/static/weixin/wxss/basicprofile_r3.png';
}
}
3.3 巧用wxml模板
wxml支持import,在大家小程序开发过程中,实际结合了Wxpage对子模板、组件的定义方法。
模板示例(代码来源:何润锋工作室小程序):
<!-- 引入子组件模板 -->
<import src="/comps/header.wxml" />
<import src="/comps/player.wxml" />
<import src="/comps/playerintro.wxml" />
<import src="/comps/recommvideo.wxml" />
<import src="/comps/recommnote.wxml" />
<import src="/comps/comment.wxml" />
<view class="wxpage" style="height:{{windowHeight}
|