模板结构: <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.这一篇文章的评分数据依据。 在开发中,评分功能由多个功能函数组成,大致可以分为渲染、用户操作、服务器操作回调、还有数据换算等一些函数方法。 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} |