摘要:根據(jù)上一小節(jié)的主要信息,我們可以初步推斷出一條迷你微博在云數(shù)據(jù)庫的里是這樣存儲的先來看。
0. 前言
本文將手把手教你如何寫出迷你版微博的一行行代碼,迷你版微博包含以下功能:
Feed 流:關注動態(tài)、所有動態(tài)
發(fā)送圖文動態(tài)
搜索用戶
關注系統(tǒng)
點贊動態(tài)
個人主頁
使用到的云開發(fā)能力:
云數(shù)據(jù)庫
云存儲
云函數(shù)
云調用
沒錯,幾乎是所有的云開發(fā)能力。也就是說,讀完這篇實戰(zhàn),你就相當于完全入門了云開發(fā)!
咳咳,當然,實際上這里只是介紹核心邏輯和重點代碼片段,完整代碼建議下載查看。
1. 取得授權作為一個社交平臺,首先要做的肯定是經過用戶授權,獲取用戶信息,小程序提供了很方便的接口:
這個 button 有個 open-type 屬性,這個屬性是專門用來使用小程序的開放能力的,而 getUserInfo 則表示 獲取用戶信息,可以從bindgetuserinfo回調中獲取到用戶信息。
于是我們可以在 wxml 里放入這個 button 后,在相應的 js 里寫如下代碼:
Page({ ... getUserInfo: function(e) { wx.navigateTo({ url: "/pages/circle/circle" }) }, ... })
這樣在成功獲取到用戶信息后,我們就能跳轉到迷你微博頁面了。
需要注意,不能使用 wx.authorize({scope: "scope.userInfo"}) 來獲取讀取用戶信息的權限,因為它不會跳出授權彈窗。目前只能使用上面所述的方式實現(xiàn)。
2. 主頁設計社交平臺的主頁大同小異,主要由三個部分組成:
Feed 流
消息
個人信息
那么很容易就能想到這樣的布局(注意新建一個 Page 哦,路徑:pages/circle/circle.wxml):
很好理解,畫面主要被分為上下兩個部分:上面的部分是主要內容,下面的部分是三個 Tab 組成的 Footer。重點 WXSS 實現(xiàn)(完整的 WXSS 可以下載源碼查看):
.footer { box-shadow: 0 0 15rpx #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: 3rpx solid #aaa; border-right: 3rpx solid #aaa; flex-grow: 1; } .footer-btn { width: 100%; height: 100%; display: flex; justify-content: center; align-items: center; border-radius: 0; font-size: 30rpx; }
核心邏輯是通過 position: fixed 來讓 Footer 一直在下方。
讀者會發(fā)現(xiàn)有一個 currentPage 的 data ,這個 data 的作用其實很直觀:通過判斷它的值是 main/msg/me 中的哪一個來決定主要內容。同時,為了讓首次使用的用戶知道自己在哪個 Tab,F(xiàn)ooter 中相應的 button 也會從白底黑字黑底白字,與另外兩個 Tab 形成對比。
現(xiàn)在我們來看看 main 部分的代碼(在上面代碼的基礎上擴充):
...... 無數(shù)據(jù)
這里用到了 列表渲染 和 條件渲染,還不清楚的可以點擊進去學習一下。
可以看到,相比之前的代碼,我添加一個 header,同時 main-area 的內部也新增了一個 scroll-view(用于展示 Feed 流) 和一個 button(用于編輯新迷你微博)。header 的功能很簡單:左側區(qū)域是一個 picker,可以選擇查看的動態(tài)類型(目前有 關注動態(tài) 和 所有動態(tài) 兩種);右側區(qū)域是一個按鈕,點擊后可以跳轉到搜索頁面,這兩個功能我們先放一下,先繼續(xù)看 main-area 的新增內容。
main-area 里的 scroll-view 是一個可監(jiān)聽滾動事件的列表,其中監(jiān)聽事件的實現(xiàn):
data: { ... addPosterBtnBottom: "190rpx", mainHeaderMaxHeight: "80rpx", mainAreaHeight: "calc(100vh - 200rpx)", mainAreaMarginTop: "80rpx", }, onMainPageScroll: function(e) { if (e.detail.deltaY < 0) { 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" }) } }, ...
結合 wxml 可以知道,當頁面向下滑動 (deltaY < 0) 時,header 和 button 會 “突然消失”,反之它們則會 “突然出現(xiàn)”。為了視覺上有更好地過渡,我們可以在 WXSS 中使用 transition :
... .main-area { position: relative; flex-grow: 1; overflow: auto; z-index: 1; transition: height 0.3s, margin-top 0.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: 3rpx solid #aaa; transition: max-height 0.3s; overflow: hidden; } .add-poster-btn { position: fixed; right: 60rpx; box-shadow: 5rpx 5rpx 10rpx #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: bottom 0.3s; background: #fff; z-index: 1; } ...3. Feed 流 3.1 post-item
前面提到,scroll-view 的內容是 Feed 流,那么首先就要想到使用 列表渲染。而且,為了方便在個人主頁復用,列表渲染中的每一個 item 都要抽象出來。這時就要使用小程序中的 Custom-Component 功能了。
新建一個名為 post-item 的 Component,其中 wxml 的實現(xiàn)(路徑:pages/circle/component/post-item/post-item.js):
{{data.author}} {{data.formatDate}} {{data.msg}}
可見,一個 poster-item 最主要有以下信息:
作者名
發(fā)送時間
文本內容
圖片內容
其中,圖片內容因為是可選的,所以使用了 條件渲染,這會在沒有圖片信息時不讓圖片顯示區(qū)域占用屏幕空間。另外,圖片內容主要是由 image-wrapper 組成,它也是一個 Custom-Component,主要功能是:
強制長寬 1:1 裁剪顯示圖片
點擊查看大圖
未加載完成時顯示 加載中
具體代碼這里就不展示了,比較簡單,讀者可以在 component/image-wrapper 里找到。
回過頭看 main-area 的其他新增部分,細心的讀者會發(fā)現(xiàn)有這么一句:
無數(shù)據(jù)
這會在 Feed 流暫時沒有獲取到數(shù)據(jù)時給用戶一個提示。
3.2 collections: poster、poster_users展示 Feed 流的部分已經編寫完畢,現(xiàn)在就差實際數(shù)據(jù)了。根據(jù)上一小節(jié) poster-item 的主要信息,我們可以初步推斷出一條迷你微博在 云數(shù)據(jù)庫 的 collection poster 里是這樣存儲的:
{ "username": "Tester", "date": "2019-07-22 12:00:00", "text": "Ceshiwenben", "photo": "xxx" }
先來看 username。由于社交平臺一般不會限制用戶的昵稱,所以如果每條迷你微博都存儲昵稱,那將來每次用戶修改一次昵稱,就要遍歷數(shù)據(jù)庫把所有迷你微博項都改一遍,相當耗費時間,所以我們不如存儲一個 userId,并另外把 id 和 昵稱 的對應關系存在另一個叫 poster_users 的 collection 里。
{ "userId": "xxx", "name": "Tester", ...(其他用戶信息) }
userId 從哪里拿呢?當然是通過之前已經授權的獲取用戶信息接口拿到了,詳細操作之后會說到。
接下來是 date,這里最好是服務器時間(因為客戶端傳過來的時間可能會有誤差),而云開發(fā)文檔里也有提供相應的接口:serverDate。這個數(shù)據(jù)可以直接被 new Date() 使用,可以理解為一個 UTC 時間。
text 即文本信息,直接存儲即可。
photo 則表示附圖數(shù)據(jù),但是限于小程序 image 元素的實現(xiàn),想要顯示一張圖片,要么提供該圖片的 url,要么提供該圖片在 云存儲 的 id,所以這里最佳的實踐是:先把圖片上傳到云存儲里,然后把回調里的文件 id 作為數(shù)據(jù)存儲。
綜上所述,最后 poster 每一項的數(shù)據(jù)結構如下:
{ "authorId": "xxx", "date": "utc-format-date", "text": "Ceshiwenben", "photoId": "yyy" }
確定數(shù)據(jù)結構后,我們就可以開始往 collection 添加數(shù)據(jù)了。但是,在此之前,我們還缺少一個重要步驟。
3.3 用戶信息錄入 與 云數(shù)據(jù)庫沒錯,我們還沒有在 poster_users 里添加一條新用戶的信息。這個步驟一般在 pages/circle/circle 頁面首次加載時判斷即可:
getUserId: function(cb) { let that = this var value = this.data.userId || wx.getStorageSync("userId") if (value) { if (cb) { cb(value) } return value } 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.length === 0) { 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" }) } } }) }
代碼實現(xiàn)比較復雜,整體思路是這樣的:
判斷是否已存儲了 userId,如果有直接返回并調用回調函數(shù),如果沒有繼續(xù) 2
通過 wx.getSetting 獲取當前設置信息
如果返回里有 res.authSetting["scope.userInfo"] 說明已經授權讀取用戶信息,繼續(xù) 3,沒有授權的話就跳轉回首頁重新授權
調用 wx.getUserInfo 獲取用戶信息,成功后提取出 signature(這是每個微信用戶的唯一簽名),并調用 wx.setStorageSync 將其緩存
調用 db.collection().where().get() ,判斷返回的數(shù)據(jù)是否是空數(shù)組,如果不是說明該用戶已經錄入(注意 where() 中的篩選條件),如果是說明該用戶是新用戶,繼續(xù) 5
提示新用戶錄入中,同時調用 db.collection().add() 來添加用戶信息,最后通過回調判斷是否錄入成功,并提示用戶
不知不覺我們就使用了云開發(fā)中的 云數(shù)據(jù)庫 功能,緊接著我們就要開始使用 云存儲 和 云函數(shù)了!
3.4 addPoster 與 云存儲發(fā)送新的迷你微博,需要一個編輯新迷你微博的界面,路徑我定為 pages/circle/add-poster/add-poster:
{{remainLen}}/140
wxml 的代碼很好理解:textarea 顯示編輯文本,image-wrapper 顯示需要上傳的圖片,最下面是一個發(fā)送的 button。其中,圖片編輯區(qū)域的 bindtap 事件實現(xiàn):
onImageTap: function() { let that = this wx.chooseImage({ count: 1, success: function(res) { const tempFilePaths = res.tempFilePaths that.setData({ imageSrc: tempFilePaths[0] }) } }) }
直接通過 wx.chooseImage 官方 API 獲取本地圖片的臨時路徑即可。而當發(fā)送按鈕點擊后,會有如下代碼被執(zhí)行:
onSendTap: function() { if (this.data.text === "" && this.data.imageSrc === "") { wx.showModal({ title: "錯誤", content: "不能發(fā)送空內容", showCancel: false, confirmText: "好的" }) return } const that = this wx.showLoading({ title: "發(fā)送中", mask: true }) const imageSrc = this.data.imageSrc if (imageSrc !== "") { const finalPath = imageSrc.replace("http://", "/").replace(":", "") wx.cloud .uploadFile({ cloudPath: finalPath, filePath: imageSrc // 文件路徑 }) .then(res => { that.sendToDb(res.fileID) }) .catch(error => { that.onSendFail() }) } else { that.sendToDb() } }, sendToDb: function(fileId = "") { const that = this const posterData = { authorId: that.data.userId, msg: that.data.text, photoId: fileId, date: db.serverDate() } db.collection("poster") .add({ data: { ...posterData } }) .then(res => { wx.showToast({ title: "發(fā)送成功" }) wx.navigateBack({ delta: 1 }) }) .catch(error => { that.onSendFail() }) .finally(wx.hideLoading()) }
首先判斷文本和圖片內容是否都為空,如果是則不執(zhí)行發(fā)送,如果不是繼續(xù) 2
提示發(fā)送中,上傳圖片到云存儲,注意需要將圖片中的臨時 url 的一些特殊字符組合替換一下,原因見 文件名命名限制
上傳成功后,調用 db.collection().add(),發(fā)送成功后退回上一頁(即首頁),如果失敗則執(zhí)行 onSendFail 函數(shù),后者見源碼,邏輯較簡單這里不贅述
于是,我們就這樣創(chuàng)建了第一條迷你微博。接下來就讓它在 Feed 流中顯示吧!
3.5 云函數(shù) getMainPageData這個函數(shù)的主要作用如前所述,就是通過處理云數(shù)據(jù)庫中的數(shù)據(jù),將最終數(shù)據(jù)返回給客戶端,后者將數(shù)據(jù)可視化給用戶。我們先做一個初步版本,因為現(xiàn)在 poster_users 中只有一條數(shù)據(jù),所以僅先展示自己的迷你微博。getMainPageData 云函數(shù)代碼如下:
// 云函數(shù)入口文件 const cloud = require("wx-server-sdk") cloud.init() const db = cloud.database() // 云函數(shù)入口函數(shù) exports.main = async (event, context, cb) => { // 通過 event 獲取入參 const userId = event.userId let followingResult let users // idNameMap 負責存儲 userId 和 name 的映射關系 let idNameMap = {} let followingIds = [] // 獲取用戶信息 followingResult = await db .collection("poster_users") .where({ userId: userId }) .get() users = followingResult.data followingIds = users.map(u => { return u.userId }) users.map(u => { idNameMap[u.userId] = u.name }) // 獲取動態(tài) const postResult = await db .collection("poster") .orderBy("date", "desc") .where({ // 通過高級篩選功能篩選出符合條件的 userId authorId: db.command.in(followingIds) }) .get() const postData = postResult.data // 向返回的數(shù)據(jù)添加 存儲用戶昵稱的 author 屬性、存儲格式化后的時間的 formatDate 屬性 postData.map(p => { p.author = idNameMap[p.authorId] p.formatDate = new Date(p.date).toLocaleDateString("zh-Hans", options) }) return postData }
最后在 pages/circle/circle.js 里補充云調用:
getMainPageData: function(userId) { const that = this wx.cloud .callFunction({ name: "getMainPageData", data: { userId: userId, isEveryOne: that.data.groupArrayIndex === 0 ? false : true } }) .then(res => { that.setData({ pageMainData: res.result, pageMainLoaded: true }) }) .catch(err => { wx.showToast({ title: "獲取動態(tài)失敗", image: "/images/error.png" }) wx.hideLoading() }) }
即可展示 Feed 流數(shù)據(jù)給用戶。
之后,getMainPageData 還會根據(jù)使用場景的不同,新增了查詢所有用戶動態(tài)、查詢關注用戶動態(tài)的功能,但是原理是一樣的,看源碼可以輕易理解,后續(xù)就不再說明。
4. 關注系統(tǒng)上一節(jié)中我們一口氣把云開發(fā)中的大部分主要功能:云數(shù)據(jù)庫、云存儲、云函數(shù)、云調用都用了一遍,接下來其他功能的實現(xiàn)也基本都依賴它們。
4.1 poster_user_follows首先我們需要建一個新的 collection poster_user_follows,其中的每一項數(shù)據(jù)的數(shù)據(jù)結構如下:
{ "followerId": "xxx", "followingId": "xxx" }
很簡單,followerId 表示關注人,followingId 表示被關注人。
4.2 user-data 頁面關注或者取消關注需要進入他人的個人主頁操作,我們在 pages/circle/user-data/user-data.wxml 中放一個 user-info 的自定義組件,然后新建該組件編輯:
用戶名: {{userName}} 動態(tài)數(shù): {{posterCount}} 關注數(shù): {{followingCount}} 粉絲數(shù): {{followerCount}}
這里注意條件渲染的 button:如果當前訪問個人主頁的用戶 id (originId) 和 被訪問的用戶 id (userId)的值是相等的話,這個按鈕就不會被渲染(自己不能關注/取消關注自己)。
我們重點看下 onFollowTap 的實現(xiàn):
onFollowTap: function() { const that = this // 判斷當前關注狀態(tài) 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()) } else if (this.data.isFollow !== undefined) { wx.showLoading({ title: "操作中", mask: true }) const data = { 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()) } } }
這里讀者可能會有疑問:為什么關注的時候直接調用 db.collection().add() 即可,而取消關注卻要調用云函數(shù)呢?這里涉及到云數(shù)據(jù)庫的設計問題:刪除多個數(shù)據(jù)的操作,或者說刪除使用 where 篩選的數(shù)據(jù),只能在服務端執(zhí)行。如果確實想在客戶端刪除,則在查詢用戶關系時,將唯一標識數(shù)據(jù)的 _id 用 setData 存下來,之后再使用 db.collection().doc(_id).delete() 刪除即可。這兩種實現(xiàn)方式讀者可自行選擇。當然,還有一種實現(xiàn)是不實際刪除數(shù)據(jù),只是加個 isDelete 字段標記一下。
查詢用戶關系的實現(xiàn)很簡單,云函數(shù)的實現(xiàn)方式如下:
// 云函數(shù)入口文件 const cloud = require("wx-server-sdk") cloud.init() const db = cloud.database() // 云函數(shù)入口函數(shù) exports.main = async(event, context) => { const followingResult = await db.collection("poster_user_follows") .where({ followingId: event.followingId, followerId: event.followerId }).get() return followingResult }
客戶端只要檢查返回的數(shù)據(jù)長度是否大于 0 即可。
另外附上 user-data 頁面其他數(shù)據(jù)的獲取云函數(shù)實現(xiàn):
// 云函數(shù)入口文件 const cloud = require("wx-server-sdk") cloud.init() const db = cloud.database() async function getPosterCount(userId) { return { value: (await db.collection("poster").where({ authorId: userId }).count()).total, key: "posterCount" } } async function getFollowingCount(userId) { return { value: (await db.collection("poster_user_follows").where({ followerId: userId }).count()).total, key: "followingCount" } } async function getFollowerCount(userId) { return { value: (await db.collection("poster_user_follows").where({ followingId: userId }).count()).total, key: "followerCount" } } async function getUserName(userId) { return { value: (await db.collection("poster_users").where({ userId: userId }).get()).data[0].name, key: "userName" } } // 云函數(shù)入口函數(shù) exports.main = async (event, context) => { const userId = event.userId const tasks = [] tasks.push(getPosterCount(userId)) tasks.push(getFollowerCount(userId)) tasks.push(getFollowingCount(userId)) tasks.push(getUserName(userId)) const allData = await Promise.all(tasks) const finalData = {} allData.map(d => { finalData[d.key] = d.value }) return finalData }
很好理解,客戶端獲取返回后直接使用即可。
5. 搜索頁面這部分其實很好實現(xiàn)。關鍵的搜索函數(shù)實現(xiàn)如下:
// 云函數(shù)入口文件 const cloud = require("wx-server-sdk") cloud.init() const db = cloud.database() const MAX_LIMIT = 100 async function getDbData(dbName, whereObj) { const totalCountsData = await db.collection(dbName).where(whereObj).count() const total = totalCountsData.total const batch = Math.ceil(total / 100) const tasks = [] for (let i = 0; i < batch; i++) { const promise = db .collection(dbName) .where(whereObj) .skip(i * MAX_LIMIT) .limit(MAX_LIMIT) .get() tasks.push(promise) } const rrr = await Promise.all(tasks) if (rrr.length !== 0) { return rrr.reduce((acc, cur) => { return { data: acc.data.concat(cur.data), errMsg: acc.errMsg } }) } else { return { data: [], errMsg: "empty" } } } // 云函數(shù)入口函數(shù) exports.main = async (event, context) => { const text = event.text const data = await getDbData("poster_users", { name: { $regex: text } }) return data }
這里參考了官網所推薦的分頁檢索數(shù)據(jù)庫數(shù)據(jù)的實現(xiàn)(因為搜索結果可能有很多),篩選條件則是正則模糊匹配關鍵字。
搜索頁面的源碼路徑是 pages/circle/search-user/search-user,實現(xiàn)了點擊搜索結果項跳轉到對應項的用戶的 user-data 頁面,建議直接閱讀源碼理解。
6. 其他擴展 6.1 poster_likes 與 點贊由于轉發(fā)、評論、點贊的原理基本相同,所以這里只介紹點贊功能如何編寫,另外兩個功能讀者可以自行實現(xiàn)。
毫無疑問我們需要新建一個 collection poster_likes,其中每一項的數(shù)據(jù)結構如下:
{ "posterId": "xxx", "likeId": "xxx" }
這里的 posterId 就是 poster collection 里每條記錄的 _id 值,likeId 就是 poster_users 里的 userId 了。
然后我們擴展一下 poster-item 的實現(xiàn):
...
即,新增一個 interact-area,其中 onLikeTap 實現(xiàn)如下:
onLikeTap: function() { if (!this.properties.originId) return const that = this if (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()) } }
細心的讀者會發(fā)現(xiàn)這和關注功能原理幾乎是一樣的。
6.2 數(shù)據(jù)刷新我們可以使用很多方式讓主頁面刷新數(shù)據(jù):
onShow: function() { wx.showLoading({ title: "加載中", mask: true }) const that = this function cb(userId) { that.refreshMainPageData(userId) that.refreshMePageData(userId) } this.getUserId(cb) }
第一種是利用 onShow 方法:它會在頁面每次從后臺轉到前臺展示時調用,這個時候我們就能刷新頁面數(shù)據(jù)(包括 Feed 流和個人信息)。但是這個時候用戶信息可能會丟失,所以我們需要在 getUserId 里判斷,并將刷新數(shù)據(jù)的函數(shù)們整合起來,作為回調函數(shù)。
第二種是讓用戶手動刷新:
onPageMainTap: function() { if (this.data.currentPage === "main") { this.refreshMainPageData() } this.setData({ currentPage: "main" }) }
如圖所示,當目前頁面是 Feed 流時,如果再次點擊 首頁 Tab,就會強制刷新數(shù)據(jù)。
第三種是關聯(lián)數(shù)據(jù)變更觸發(fā)刷新,比如動態(tài)類型選擇、刪除了一條動態(tài)以后觸發(fā)數(shù)據(jù)的刷新。這種可以直接看源碼學習。
6.3 首次加載等待當用戶第一次進入主頁面時,我們如果想在 Feed 流和個人信息都加載好了再允許用戶操作,應該如何實現(xiàn)?
如果是類似 Vue 或者 React 的框架,我們很容易就能想到屬性監(jiān)控,如 watch、useEffect 等等,但是小程序目前 Page 并沒有提供屬性監(jiān)控功能,怎么辦?
除了自己實現(xiàn),還有一個方法就是利用 Component 的 observers,它和上面提到的屬性監(jiān)控功能差不多。雖然官網文檔對其說明比較少,但摸索了一番還是能用來監(jiān)控的。
首先我們來新建一個 Component 叫 abstract-load,具體實現(xiàn)如下:
// pages/circle/component/abstract-load.js Component({ properties: { pageMainLoaded: { type: Boolean, value: false }, pageMeLoaded: { type: Boolean, value: false } }, observers: { "pageMainLoaded, pageMeLoaded": function (pageMainLoaded, pageMeLoaded) { if (pageMainLoaded && pageMeLoaded) { this.triggerEvent("allLoadEvent") } } } })
然后在 pages/circle/circle.wxml 中添加一行:
最后實現(xiàn) onAllLoad 函數(shù)即可。
另外,像這種沒有實際展示數(shù)據(jù)的 Component,建議在項目中都用 abstract 開頭來命名。
6.4 scroll-view 在 iOS 的 bug如果讀者使用 iOS 系統(tǒng)調試這個小程序,可能會發(fā)現(xiàn) Feed 流比較短的時候,滾動 scroll-view header 和 button 會有鬼畜的上下抖動現(xiàn)象,這是因為 iOS 自己實現(xiàn)的 WebView 對于滾動視圖有回彈的效果,而該效果也會觸發(fā)滾動事件。
對于這個 bug,官方人員也表示暫時無法修復,只能先忍一忍了。
6.5 關于消息 Tab讀者可能會疑惑我為什么沒有講解消息 Tab 以及消息提醒的實現(xiàn)。首先是因為源碼沒有這個實現(xiàn),其次是我覺得目前云開發(fā)所提供的能力實現(xiàn)主動提醒比較麻煩(除了輪詢想不到其他辦法)。
希望未來云開發(fā)可以提供 數(shù)據(jù)庫長連接監(jiān)控 的功能,這樣通過訂閱者模式可以很輕松地獲取到數(shù)據(jù)更新的狀態(tài),主動提醒也就更容易實現(xiàn)了。到那時我可能會再更新相關源碼。
6.6 關于云函數(shù)耗時讀者可能會發(fā)現(xiàn)我有一個叫 benchmark 的云函數(shù),這個函數(shù)只是做了個查詢數(shù)據(jù)庫的操作,目的在于計算查詢耗時。
詭異的是,我前天在調試的時候,發(fā)現(xiàn)查詢一次需要1秒鐘,而寫這篇文章時卻不到100ms。建議在一些需要多次操作數(shù)據(jù)庫的函數(shù)配置里,把超時時間設置長一點吧。目前云函數(shù)的性能不太穩(wěn)定。
7. 結語那么關于迷你版微博開發(fā)實戰(zhàn)介紹就到此為止了,更多資料可以直接下載源碼查看哦。
源碼鏈接https://github.com/TencentClo...
如果你有關于使用云開發(fā)CloudBase相關的技術故事/技術實戰(zhàn)經驗想要跟大家分享,歡迎留言聯(lián)系我們哦~比心
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/105991.html
摘要:時間永遠都過得那么快,一晃從年注冊,到現(xiàn)在已經過去了年那些被我藏在收藏夾吃灰的文章,已經太多了,是時候把他們整理一下了。那是因為收藏夾太亂,橡皮擦給設置私密了,不收拾不好看呀。 ...
摘要:讓你收獲滿滿碼個蛋從年月日推送第篇文章一年過去了已累積推文近篇文章,本文為年度精選,共計篇,按照類別整理便于讀者主題閱讀。本篇文章是今年的最后一篇技術文章,為了讓大家在家也能好好學習,特此花了幾個小時整理了這些文章。 showImg(https://segmentfault.com/img/remote/1460000013241596); 讓你收獲滿滿! 碼個蛋從2017年02月20...
摘要:但小程序的云開發(fā)也有一些局限性,我會在下一篇小程序云開發(fā)實戰(zhàn)系列云數(shù)據(jù)庫里分享我使用云開發(fā)數(shù)據(jù)庫的一些體會,敬請期待。 因為工作原因,使用過亞馬遜AWS的serverless構架,當時就覺得這是一個非常適合小程序的基礎設施。今年開始自己的微信小程序開發(fā),決定用serverless架構,于是便使用了騰訊云做為小程序的后端,總結下來有以下幾個痛點: 當暴露API給小程序時,需要使用已備案的...
閱讀 2993·2021-10-13 09:39
閱讀 2694·2021-09-27 13:34
閱讀 2031·2019-08-30 15:55
閱讀 3260·2019-08-30 15:43
閱讀 3631·2019-08-30 11:16
閱讀 1748·2019-08-26 18:28
閱讀 1283·2019-08-26 13:56
閱讀 914·2019-08-26 13:35