微信小程序> 小程序·云开发实战-迷你微博-微博怎样不分享小程序-新浪微博小程序

小程序·云开发实战-迷你微博-微博怎样不分享小程序-新浪微博小程序

浏览量:1377 时间: 来源:weixin_30364147
0.前言1.

本文将手把手教你如何写出迷你版微博的一行行代码,迷你版微博包含以下功能:

Feed流:关注动态、所有动态发送图文动态搜索用户关注系统点赞动态个人主页2.

使用到的云开发能力:

云数据库云存储云函数云调用3.

没错,几乎是所有的云开发能力。也就是说,读完这篇实战,你就相当于完全入门了云开发!

4.

咳咳,当然,实际上这里只是介绍核心逻辑和重点代码片段,完整代码建议下载查看。

1.取得授权5.

作为一个社交平台,首先要做的肯定是经过用户授权,获取用户信息,小程序提供了很方便的接口:

buttonopen-type"getUserInfo"bindgetuserinfo"getUserInfo"进入小圈圈/button6.

这个button有个open-type属性,这个属性是专门用来使用小程序的开放能力的,而getUserInfo则表示获取用户信息,可以从bindgetuserinfo回调中获取到用户信息。

7.

于是我们可以在wxml里放入这个button后,在相应的js里写如下代码:

Page({...getUserInfo:function(e){wx.navigateTo({url:"/pages/circle/circle"})},...})8.

这样在成功获取到用户信息后,我们就能跳转到迷你微博页面了。

9.

需要注意,不能使用wx.authorize({scope:"scope.userInfo"})来获取读取用户信息的权限,因为它不会跳出授权弹窗。目前只能使用上面所述的方式实现。

2.主页设计10.

社交平台的主页大同小异,主要由三个部分组成:

Feed流消息个人信息11.

那么很容易就能想到这样的布局(注意新建一个Page哦,路径:pages/circle/circle.wxml):

viewclass"circle-container"viewstyle"display:{{currentPage'main'?'block':'none'}}"class"main-area"/viewviewstyle"display:{{currentPage'msg'?'flex':'none'}}"class"msg-area"/viewviewstyle"display:{{currentPage'me'?'flex':'none'}}"class"me-area"/viewviewclass"footer"viewclass"footer-item"buttonclass"footer-btn"bindtap"onPageMainTap"style"background:{{currentPage'main'?'#111':'rgba(0,0,0,0)'}};color:{{currentPage'main'?'#fff':'#000'}}"首页/button/viewviewclass"footer-item"buttonclass"footer-btn"bindtap"onPageMsgTap"style"background:{{currentPage'msg'?'#111':'rgba(0,0,0,0)'}};color:{{currentPage'msg'?'#fff':'#000'}}"消息/button/viewviewclass"footer-item"buttonclass"footer-btn"bindtap"onPageMeTap"style"background:{{currentPage'me'?'#111':'rgba(0,0,0,0)'}};color:{{currentPage'me'?'#fff':'#000'}}"个人/button/view/view/view12.

很好理解,画面主要被分为上下两个部分:上面的部分是主要内容,下面的部分是三个Tab组成的Footer。重点WXSS实现(完整的WXSS可以下载源码查看):

.footer{box-shadow:0015rpx#ccc;display:flex;position:fixed;height:120rpx;bottom:0;width:100%;flex-direction:row;justify-content:center;z-index:100;background:#fff;}.footer-item{display:flex;justify-content:center;align-items:center;height:100%;width:33.33%;color:#333;}.footer-item:nth-child(2){border-left:3rpxsolid#aaa;border-right:3rpxsolid#aaa;flex-grow:1;}.footer-btn{width:100%;height:100%;display:flex;justify-content:center;align-items:center;border-radius:0;font-size:30rpx;}13.

核心逻辑是通过position:fixed来让Footer一直在下方。

14.

读者会发现有一个currentPage的data,这个data的作用其实很直观:通过判断它的值是main/msg/me中的哪一个来决定主要内容。同时,为了让首次使用的用户知道自己在哪个Tab,Footer中相应的button也会从白底黑字黑底白字,与另外两个Tab形成对比。

15.

现在我们来看看main部分的代码(在上面代码的基础上扩充):

...viewclass"main-header"style"display:{{currentPage'main'?'flex':'none'}};max-height:{{mainHeaderMaxHeight}}"viewclass"group-picker-wrapper"pickerbindchange"bindGroupPickerChange"value"{{groupArrayIndex}}"range"{{groupArray}}"class"group-picker"buttonclass"group-picker-inner"{{groupArray[groupArrayIndex]}}/button/picker/viewviewclass"search-btn-wrapper"buttonclass"search-btn"bindtap"onSearchTap"搜索用户/button/view/viewviewclass"main-area"style"display:{{currentPage'main'?'block':'none'}};height:{{mainAreaHeight}};margin-top:{{mainAreaMarginTop}}"scroll-viewscroll-yclass"main-area-scroll"bindscroll"onMainPageScroll"blockwx:for"{{pageMainData}}"wx:for-index"idx"wx:for-item"itemName"wx:key"_id"post-itemis"post-item"data"{{itemName}}"class"post-item-wrapper"//blockviewwx:if"{{pageMainData.length0}}"class"item-placeholder"无数据/view/scroll-viewbuttonclass"add-poster-btn"bindtap"onAddPosterTap"hover-class"add-poster-btn-hover"style"bottom:{{addPosterBtnBottom}}"+/button/view...16.

这里用到了列表渲染和条件渲染,还不清楚的可以点击进去学习一下。

17.

可以看到,相比之前的代码,我添加一个header,同时main-area的内部也新增了一个scroll-view(用于展示Feed流)和一个button(用于编辑新迷你微博)。header的功能很简单:左侧区域是一个picker,可以选择查看的动态类型(目前有关注动态和所有动态两种);右侧区域是一个按钮,点击后可以跳转到搜索页面,这两个功能我们先放一下,先继续看main-area的新增内容。

18.

main-area里的scroll-view是一个可监听滚动事件的列表,其中监听事件的实现:

data:{...addPosterBtnBottom:"190rpx",mainHeaderMaxHeight:"80rpx",mainAreaHeight:"calc(100vh-200rpx)",mainAreaMarginTop:"80rpx",},onMainPageScroll:function(e){if(e.detail.deltaY0){this.setData({addPosterBtnBottom:"-190rpx",mainHeaderMaxHeight:"0",mainAreaHeight:"calc(100vh-120rpx)",mainAreaMarginTop:"0rpx"})}else{this.setData({addPosterBtnBottom:"190rpx",mainHeaderMaxHeight:"80rpx",mainAreaHeight:"calc(100vh-200rpx)",mainAreaMarginTop:"80rpx"})}},...19.

结合wxml可以知道,当页面向下滑动(deltaY0)时,header和button会“突然消失”,反之它们则会“突然出现”。为了视觉上有更好地过渡,我们可以在WXSS中使用transition:

....main-area{position:relative;flex-grow:1;overflow:auto;z-index:1;transition:height0.3s,margin-top0.3s;}.main-header{position:fixed;width:100%;height:80rpx;background:#fff;top:0;left:0;display:flex;justify-content:space-around;align-items:center;z-index:100;border-bottom:3rpxsolid#aaa;transition:max-height0.3s;overflow:hidden;}.add-poster-btn{position:fixed;right:60rpx;box-shadow:5rpx5rpx10rpx#aaa;display:flex;justify-content:center;align-items:center;color:#333;padding-bottom:10rpx;text-align:center;border-radius:50%;font-size:60rpx;width:100rpx;height:100rpx;transition:bottom0.3s;background:#fff;z-index:1;}...3.Feed流3.1post-item20.

前面提到,scroll-view的内容是Feed流,那么首先就要想到使用列表渲染。而且,为了方便在个人主页复用,列表渲染中的每一个item都要抽象出来。这时就要使用小程序中的Custom-Component功能了。

21.

新建一个名为post-item的Component,其中wxml的实现(路径:pages/circle/component/post-item/post-item.js):

viewclass"post-item"hover-class"post-item-hover"bindlongpress"onItemLongTap"bindtap"onItemTap"viewclass"post-title"viewclass"author"hover-class"author-hover"catchtap"onAuthorTap"{{data.author}}/viewviewclass"date"{{data.formatDate}}/view/viewviewclass"msg-wrapper"textclass"msg"{{data.msg}}/text/viewviewclass"image-outer"wx:if"{{data.photoId!''}}"catchtap"onImgTap"image-wrapperis"image-wrapper"src"{{data.photoId}}"//view/view22.

可见,一个poster-item最主要有以下信息:

作者名发送时间文本内容图片内容23.

其中,图片内容因为是可选的,所以使用了条件渲染,这会在没有图片信息时不让图片显示区域占用屏幕空间。另外,图片内容主要是由image-wrapper组成,它也是一个Custom-Component,主要功能是:

强制长宽1:1裁剪显示图片点击查看大图未加载完成时显示加载中24.

具体代码这里就不展示了,比较简单,读者可以在component/image-wrapper里找到。

25.

回过头看main-area的其他新增部分,细心的读者会发现有这么一句:

viewwx:if"{{pageMainData.length0}}"class"item-placeholder"无数据/view26.

这会在Feed流暂时没有获取到数据时给用户一个提示。

3.2collections:poster、poster_users27.

展示Feed流的部分已经编写完毕,现在就差实际数据了。根据上一小节poster-item的主要信息,我们可以初步推断出一条迷你微博在云数据库的collectionposter里是这样存储的:

{"username":"Tester","date":"2019-07-2212:00:00","text":"Ceshiwenben","photo":"xxx"}28.

先来看username。由于社交平台一般不会限制用户的昵称,所以如果每条迷你微博都存储昵称,那将来每次用户修改一次昵称,就要遍历数据库把所有迷你微博项都改一遍,相当耗费时间,所以我们不如存储一个userId,并另外把id和昵称的对应关系存在另一个叫poster_users的collection里。

{"userId":"xxx","name":"Tester",...(其他用户信息)}29.

userId从哪里拿呢?当然是通过之前已经授权的获取用户信息接口拿到了,详细操作之后会说到。

30.

接下来是date,这里最好是服务器时间(因为客户端传过来的时间可能会有误差),而云开发文档里也有提供相应的接口:serverDate。这个数据可以直接被newDate()使用,可以理解为一个UTC时间。

31.

text即文本信息,直接存储即可。

32.

photo则表示附图数据,但是限于小程序image元素的实现,想要显示一张图片,要么提供该图片的url,要么提供该图片在云存储的id,所以这里最佳的实践是:先把图片上传到云存储里,然后把回调里的文件id作为数据存储。

33.

综上所述,最后poster每一项的数据结构如下:

{"authorId":"xxx","date":"utc-format-date","text":"Ceshiwenben","photoId":"yyy"}34.

确定数据结构后,我们就可以开始往collection添加数据了。但是,在此之前,我们还缺少一个重要步骤。

3.3用户信息录入与云数据库35.

没错,我们还没有在poster_users里添加一条新用户的信息。这个步骤一般在pages/circle/circle页面首次加载时判断即可:

getUserId:function(cb){letthatthisvarvaluethis.data.userId||wx.getStorageSync("userId")if(value){if(cb){cb(value)}returnvalue}wx.getSetting({success(res){if(res.authSetting["scope.userInfo"]){wx.getUserInfo({withCredentials:true,success:function(userData){wx.setStorageSync("userId",userData.signature)that.setData({userId:userData.signature})db.collection("poster_users").where({userId:userData.signature}).get().then(searchResult{if(searchResult.data.length0){wx.showToast({title:"新用户录入中"})db.collection("poster_users").add({data:{userId:userData.signature,date:db.serverDate(),name:userData.userInfo.nickName,gender:userData.userInfo.gender}}).then(res{console.log(res)if(res.errMsg"collection.add:ok"){wx.showToast({title:"录入完成"})if(cb)cb()}}).catch(err{wx.showToast({title:"录入失败,请稍后重试",image:"/images/error.png"})wx.navigateTo({url:"/pages/index/index"})})}else{if(cb)cb()}})}})}else{wx.showToast({title:"登陆失效,请重新授权登陆",image:"/images/error.png"})wx.navigateTo({url:"/pages/index/index"})}}})}36.

代码实现比较复杂,整体思路是这样的:

判断是否已存储了userId,如果有直接返回并调用回调函数,如果没有继续2通过wx.getSetting获取当前设置信息如果返回里有res.authSetting["scope.userInfo"]说明已经授权读取用户信息,继续3,没有授权的话就跳转回首页重新授权调用wx.getUserInfo获取用户信息,成功后提取出signature(这是每个微信用户的唯一签名),并调用wx.setStorageSync将其缓存调用db.collection().where().get(),判断返回的数据是否是空数组,如果不是说明该用户已经录入(注意where()中的筛选条件),如果是说明该用户是新用户,继续5提示新用户录入中,同时调用db.collection().add()来添加用户信息,最后通过回调判断是否录入成功,并提示用户37.

不知不觉我们就使用了云开发中的云数据库功能,紧接着我们就要开始使用云存储和云函数了!

3.4addPoster与云存储38.

发送新的迷你微博,需要一个编辑新迷你微博的界面,路径我定为pages/circle/add-poster/add-poster:

viewclass"app-poster-container"viewclass"body"viewclass"text-area-wrapper"textareabindinput"bindTextInput"placeholder"在此填写"value"{{text}}"auto-focus"true"/viewclass"text-area-footer"text{{remainLen}}/140/text/view/viewviewbindtap"onImageTap"class"image-area"viewclass"image-outer"image-wrapperis"image-wrapper"src"{{imageSrc}}"placeholder"选择图片上传"//view/view/viewviewclass"footer"buttonclass"footer-btn"bindtap"onSendTap"发送/button/view/view39.

wxml的代码很好理解:textarea显示编辑文本,image-wrapper显示需要上传的图片,最下面是一个发送的button。其中,图片编辑区域的bindtap事件实现:

onImageTap:function(){letthatthiswx.chooseImage({count:1,success:function(res){consttempFilePathsres.tempFilePathsthat.setData({imageSrc:tempFilePaths[0]})}})}40.

直接通过wx.chooseImage官方API获取本地图片的临时路径即可。而当发送按钮点击后,会有如下代码被执行:

onSendTap:function(){if(this.data.text""this.data.imageSrc""){wx.showModal({title:"错误",content:"不能发送空内容",showCancel:false,confirmText:"好的"})return}constthatthiswx.showLoading({title:"发送中",mask:true})constimageSrcthis.data.imageSrcif(imageSrc!""){constfinalPathimageSrc.replace("//","/").replace(":","")wx.cloud.uploadFile({cloudPath:finalPath,filePath:imageSrc//文件路径}).then(res{that.sendToDb(res.fileID)}).catch(error{that.onSendFail()})}else{that.sendToDb()}},sendToDb:function(fileId""){constthatthisconstposterData{authorId:that.data.userId,msg:that.data.text,photoId:fileId,date:db.serverDate()}db.collection("poster").add({data:{...posterData}}).then(res{wx.showToast({title:"发送成功"})wx.navigateBack({delta:1})}).catch(error{that.onSendFail()}).finally(wx.hideLoading())}首先判断文本和图片内容是否都为空,如果是则不执行发送,如果不是继续2提示发送中,上传图片到云存储,注意需要将图片中的临时url的一些特殊字符组合替换一下,原因见文件名命名限制上传成功后,调用db.collection().add(),发送成功后退回上一页(即首页),如果失败则执行onSendFail函数,后者见源码,逻辑较简单这里不赘述41.

于是,我们就这样创建了第一条迷你微博。接下来就让它在Feed流中显示吧!

3.5云函数getMainPageData42.

这个函数的主要作用如前所述,就是通过处理云数据库中的数据,将最终数据返回给客户端,后者将数据可视化给用户。我们先做一个初步版本,因为现在poster_users中只有一条数据,所以仅先展示自己的迷你微博。getMainPageData云函数代码如下:

//云函数入口文件constcloudrequire("wx-server-sdk")cloud.init()constdbcloud.database()//云函数入口函数exports.mainasync(event,context,cb){//通过event获取入参constuserIdevent.userIdletfollowingResultletusers//idNameMap负责存储userId和name的映射关系letidNameMap{}letfollowingIds[]//获取用户信息followingResultawaitdb.collection("poster_users").where({userId:userId}).get()usersfollowingResult.datafollowingIdsusers.map(u{returnu.userId})users.map(u{idNameMap[u.userId]u.name})//获取动态constpostResultawaitdb.collection("poster").orderBy("date","desc").where({//通过高级筛选功能筛选出符合条件的userIdauthorId:db.command.in(followingIds)}).get()constpostDatapostResult.data//向返回的数据添加存储用户昵称的author属性、存储格式化后的时间的formatDate属性postData.map(p{p.authoridNameMap[p.authorId]p.formatDatenewDate(p.date).toLocaleDateString("zh-Hans",options)})returnpostData}43.

最后在pages/circle/circle.js里补充云调用:

getMainPageData:function(userId){constthatthiswx.cloud.callFunction({name:"getMainPageData",data:{userId:userId,isEveryOne:that.data.groupArrayIndex0?false:true}}).then(res{that.setData({pageMainData:res.result,pageMainLoaded:true})}).catch(err{wx.showToast({title:"获取动态失败",image:"/images/error.png"})wx.hideLoading()})}44.

即可展示Feed流数据给用户。

45.

之后,getMainPageData还会根据使用场景的不同,新增了查询所有用户动态、查询关注用户动态的功能,但是原理是一样的,看源码可以轻易理解,后续就不再说明。

4.关注系统46.

上一节中我们一口气把云开发中的大部分主要功能:云数据库、云存储、云函数、云调用都用了一遍,接下来其他功能的实现也基本都依赖它们。

4.1poster_user_follows47.

首先我们需要建一个新的collectionposter_user_follows,其中的每一项数据的数据结构如下:

{"followerId":"xxx","followingId":"xxx"}48.

很简单,followerId表示关注人,followingId表示被关注人。

4.2user-data页面49.

关注或者取消关注需要进入他人的个人主页操作,我们在pages/circle/user-data/user-data.wxml中放一个user-info的自定义组件,然后新建该组件编辑:

viewclass"user-info"viewclass"info-item"hover-class"info-item-hover"用户名:{{userName}}/viewviewclass"info-item"hover-class"info-item-hover"bindtap"onPosterCountTap"动态数:{{posterCount}}/viewviewclass"info-item"hover-class"info-item-hover"bindtap"onFollowingCountTap"关注数:{{followingCount}}/viewviewclass"info-item"hover-class"info-item-hover"bindtap"onFollowerCountTap"粉丝数:{{followerCount}}/viewviewclass"info-item"hover-class"info-item-hover"wx:if"{{originIdoriginId!''originId!userId}}"buttonbindtap"onFollowTap"{{followText}}/button/view/view50.

这里注意条件渲染的button:如果当前访问个人主页的用户id(originId)和被访问的用户id(userId)的值是相等的话,这个按钮就不会被渲染(自己不能关注/取消关注自己)。

51.

我们重点看下onFollowTap的实现:

onFollowTap:function(){constthatthis//判断当前关注状态if(this.data.isFollow){wx.showLoading({title:"操作中",mask:true})wx.cloud.callFunction({name:"cancelFollowing",data:{followerId:this.properties.originId,followingId:this.properties.userId}}).then(res{wx.showToast({title:"取消关注成功"})that.setData({isFollow:false,followText:"关注"})}).catch(error{wx.showToast({title:"取消关注失败",image:"/images/error.png"})}).finally(wx.hideLoading())}elseif(this.data.isFollow!undefined){wx.showLoading({title:"操作中",mask:true})constdata{followerId:this.properties.originId,followingId:this.properties.userId}db.collection("poster_user_follows").add({data:{...data}}).then(res{wx.showToast({title:"关注成功"})that.setData({isFollow:true,followText:"取消关注"})}).catch(error{wx.showToast({title:"关注失败",image:"/images/error.png"})}).finally(wx.hideLoading())}}}52.

这里读者可能会有疑问:为什么关注的时候直接调用db.collection().add()即可,而取消关注却要调用云函数呢?这里涉及到云数据库的设计问题:删除多个数据的操作,或者说删除使用where筛选的数据,只能在服务端执行。如果确实想在客户端删除,则在查询用户关系时,将唯一标识数据的_id用setData存下来,之后再使用db.collection().doc(_id).delete()删除即可。这两种实现方式读者可自行选择。当然,还有一种实现是不实际删除数据,只是加个isDelete字段标记一下。

53.

查询用户关系的实现很简单,云函数的实现方式如下:

//云函数入口文件constcloudrequire('wx-server-sdk')cloud.init()constdbcloud.database()//云函数入口函数exports.mainasync(event,context){constfollowingResultawaitdb.collection("poster_user_follows").where({followingId:event.followingId,followerId:event.followerId}).get()returnfollowingResult}54.

客户端只要检查返回的数据长度是否大于0即可。

55.

另外附上user-data页面其他数据的获取云函数实现:

//云函数入口文件constcloudrequire("wx-server-sdk")cloud.init()constdbcloud.database()asyncfunctiongetPosterCount(userId){return{value:(awaitdb.collection("poster").where({authorId:userId}).count()).total,key:"posterCount"}}asyncfunctiongetFollowingCount(userId){return{value:(awaitdb.collection("poster_user_follows").where({followerId:userId}).count()).total,key:"followingCount"}}asyncfunctiongetFollowerCount(userId){return{value:(awaitdb.collection("poster_user_follows").where({followingId:userId}).count()).total,key:"followerCount"}}asyncfunctiongetUserName(userId){return{value:(awaitdb.collection("poster_users").where({userId:userId}).get()).data[0].name,key:"userName"}}//云函数入口函数exports.mainasync(event,context){constuserIdevent.userIdconsttasks[]tasks.push(getPosterCount(userId))tasks.push(getFollowerCount(userId))tasks.push(getFollowingCount(userId))tasks.push(getUserName(userId))constallDataawaitPromise.all(tasks)constfinalData{}allData.map(d{finalData[d.key]d.value})returnfinalData}56.

很好理解,客户端获取返回后直接使用即可。

5.搜索页面57.

这部分其实很好实现。关键的搜索函数实现如下:

//云函数入口文件constcloudrequire('wx-server-sdk')cloud.init()constdbcloud.database()constMAX_LIMIT100asyncfunctiongetDbData(dbName,whereObj){consttotalCountsDataawaitdb.collection(dbName).where(whereObj).count()consttotaltotalCountsData.totalconstbatchMath.ceil(total/100)consttasks[]for(leti0;ibatch;i++){constpromisedb.collection(dbName).where(whereObj).skip(i*MAX_LIMIT).limit(MAX_LIMIT).get()tasks.push(promise)}constrrrawaitPromise.all(tasks)if(rrr.length!0){returnrrr.reduce((acc,cur){return{data:acc.data.concat(cur.data),errMsg:acc.errMsg}})}else{return{data:[],errMsg:"empty"}}}//云函数入口函数exports.mainasync(event,context){consttextevent.textconstdataawaitgetDbData("poster_users",{name:{$regex:text}})returndata}58.

这里参考了官网所推荐的分页检索数据库数据的实现(因为搜索结果可能有很多),筛选条件则是正则模糊匹配关键字。

59.

搜索页面的源码路径是pages/circle/search-user/search-user,实现了点击搜索结果项跳转到对应项的用户的user-data页面,建议直接阅读源码理解。

6.其他扩展6.1poster_likes与点赞60.

由于转发、评论、点赞的原理基本相同,所以这里只介绍点赞功能如何编写,另外两个功能读者可以自行实现。

61.

毫无疑问我们需要新建一个collectionposter_likes,其中每一项的数据结构如下:

{"posterId":"xxx","likeId":"xxx"}62.

这里的posterId就是postercollection里每条记录的_id值,likeId就是poster_users里的userId了。

63.

然后我们扩展一下poster-item的实现:

viewclass"post-item"hover-class"post-item-hover"bindlongpress"onItemLongTap"bindtap"onItemTap"...viewclass"interact-area"viewclass"interact-item"buttonclass"interact-btn"catchtap"onLikeTap"style"color:{{liked?'#55aaff':'#000'}}"赞{{likeCount}}/button/view/view/view64.

即,新增一个interact-area,其中onLikeTap实现如下:

onLikeTap:function(){if(!this.properties.originId)returnconstthatthisif(this.data.liked){wx.showLoading({title:"操作中",mask:true})wx.cloud.callFunction({name:"cancelLiked",data:{posterId:this.properties.data._id,likeId:this.properties.originId}}).then(res{wx.showToast({title:"取消成功"})that.refreshLike()that.triggerEvent('likeEvent');}).catch(error{wx.showToast({title:"取消失败",image:"/images/error.png"})}).finally(wx.hideLoading())}else{wx.showLoading({title:"操作中",mask:true})db.collection("poster_likes").add({data:{posterId:this.properties.data._id,likeId:this.properties.originId}}).then(res{wx.showToast({title:"已赞"})that.refreshLike()that.triggerEvent('likeEvent');}).catch(error{wx.showToast({title:"赞失败",image:"/images/error.png"})}).finally(wx.hideLoading())}}65.

细心的读者会发现这和关注功能原理几乎是一样的。

6.2数据刷新66.

我们可以使用很多方式让主页面刷新数据:

onShow:function(){wx.showLoading({title:"加载中",mask:true})constthatthisfunctioncb(userId){that.refreshMainPageData(userId)that.refreshMePageData(userId)}this.getUserId(cb)}67.

第一种是利用onShow方法:它会在页面每次从后台转到前台展示时调用,这个时候我们就能刷新页面数据(包括Feed流和个人信息)。但是这个时候用户信息可能会丢失,所以我们需要在getUserId里判断,并将刷新数据的函数们整合起来,作为回调函数。

68.

第二种是让用户手动刷新:

onPageMainTap:function(){if(this.data.currentPage"main"){this.refreshMainPageData()}this.setData({currentPage:"main"})}69.

如图所示,当目前页面是Feed流时,如果再次点击首页Tab,就会强制刷新数据。

70.

第三种是关联数据变更触发刷新,比如动态类型选择、删除了一条动态以后触发数据的刷新。这种可以直接看源码学习。

6.3首次加载等待71.

当用户第一次进入主页面时,我们如果想在Feed流和个人信息都加载好了再允许用户操作,应该如何实现?

72.

如果是类似Vue或者React的框架,我们很容易就能想到属性监控,如watch、useEffect等等,但是小程序目前Page并没有提供属性监控功能,怎么办?

73.

除了自己实现,还有一个方法就是利用Component的observers,它和上面提到的属性监控功能差不多。虽然官网文档对其说明比较少,但摸索了一番还是能用来监控的。

74.

首先我们来新建一个Component叫abstract-load,具体实现如下:

//pages/circle/component/abstract-load.jsComponent({properties:{pageMainLoaded:{type:Boolean,value:false},pageMeLoaded:{type:Boolean,value:false}},observers:{"pageMainLoaded,pageMeLoaded":function(pageMainLoaded,pageMeLoaded){if(pageMainLoadedpageMeLoaded){this.triggerEvent("allLoadEvent")}}}})75.

然后在pages/circle/circle.wxml中添加一行:

abstract-loadis"abstract-load"pageMainLoaded"{{pageMainLoaded}}"pageMeLoaded"{{pageMeLoaded}}"bind:allLoadEvent"onAllLoad"/76.

最后实现onAllLoad函数即可。

77.

另外,像这种没有实际展示数据的Component,建议在项目中都用abstract开头来命名。

6.4scroll-view在iOS的bug78.

如果读者使用iOS系统调试这个小程序,可能会发现Feed流比较短的时候,滚动scroll-viewheader和button会有鬼畜的上下抖动现象,这是因为iOS自己实现的WebView对于滚动视图有回弹的效果,而该效果也会触发滚动事件。

79.

对于这个bug,官方人员也表示暂时无法修复,只能先忍一忍了。

6.5关于消息Tab80.

读者可能会疑惑我为什么没有讲解消息Tab以及消息提醒的实现。首先是因为源码没有这个实现,其次是我觉得目前云开发所提供的能力实现主动提醒比较麻烦(除了轮询想不到其他办法)。

81.

希望未来云开发可以提供数据库长连接监控的功能,这样通过订阅者模式可以很轻松地获取到数据更新的状态,主动提醒也就更容易实现了。到那时我可能会再更新相关源码。

6.6关于云函数耗时82.

读者可能会发现我有一个叫benchmark的云函数,这个函数只是做了个查询数据库的操作,目的在于计算查询耗时。

83.

诡异的是,我前天在调试的时候,发现查询一次需要1秒钟,而写这篇文章时却不到100ms。建议在一些需要多次操作数据库的函数配置里,把超时时间设置长一点吧。目前云函数的性能不太稳定。

7.结语84.

那么关于迷你版微博开发实战介绍就到此为止了,更多资料可以直接下载源码查看哦。

源码链接85.

https://github.com/TencentCloudBase/Good-practice-tutorial-recommended

86.

如果你有关于使用云开发CloudBase相关的技术故事/技术实战经验想要跟大家分享,欢迎留言联系我们哦~比心!

版权声明

即速应用倡导尊重与保护知识产权。如发现本站文章存在版权问题,烦请提供版权疑问、身份证明、版权证明、联系方式等发邮件至197452366@qq.com ,我们将及时处理。本站文章仅作分享交流用途,作者观点不等同于即速应用观点。用户与作者的任何交易与本站无关,请知悉。

产品经理

手机 : 13312967497

擅长 : 小程序流量变现

扫码领取礼包

最新资讯

热门模板

  • 头条
  • 搜狐
  • 微博
  • 百家
  • 一点资讯
  • 知乎