最近自己正做一個仿今日頭條移動端App的小程序,是基於小程序雲開發的,在做小程序項目的時候使用雲開發確實方便是很多。有關於雲開發後面我也會講到,畢竟這個項目就是使用的雲開發,更多的有關雲開發內容有需要的小夥伴可以去看官方文檔,
雲開發文檔。
項目的開發也有一段時間了雖然只是實現了部分功能,但是我還是忍不住來寫篇文章來分享一下這段時間的成果和項目中遇到的問題,後面我也會逐步的完善項目。項目源碼在github上,如果小夥伴們覺得不錯可以給個star,仿頭條項目地址。
項目介紹
今日頭條現在應該是最火的新聞類的app了,很多人空閒下來的時候都會逛一下頭條看看新聞什麼的,我自己也挺喜歡逛頭條的,剛好騰訊也推出了雲開發,所以我打算仿一個頭條的app同時也在實戰中用一下雲開發功能。
新聞首頁
使用雲函數從數據庫獲取新聞信息,每一個標籤頁都是對應的新聞信息,向下拉獲取最新信息,網上拉獲取更多新聞,在推薦頁有一個置頂的新聞
詳情頁
在首頁點擊任意一個新聞,進入到對應新聞的詳情頁
進入詳情頁後顯示新聞的詳細信息
未登錄不能發佈評論
登錄後發佈評論
每一項評論可以點贊和取消點贊
分享,收藏,點擊評論按鈕直接到底部的達評論頁面
評論插入圖片
項目詳解
下面將詳細的介紹項目,雖然使用的雲開發節省了很多時間但是前後端的東西都需要做工作量有點大,在這短時間內我沒有完成整個項目,只是實現了首頁,詳情頁,和登錄頁等主要功能
我首先將界面需要獲取數據的地方設計好數據庫爲後面數據的獲取做準備,數據庫使用的是小程序雲開發的MongoDB數據庫,將數據存儲在雲數據庫上,並且使用雲函數來操作數據庫
新聞首頁
首頁相對於詳情頁要簡單一些,在頭部使用了一個搜索框和搜索按鈕,然後下面是一個tab標籤欄含有多個標籤頁,每一個標籤頁顯示標籤對應有關的新聞信。在標籤頁的右邊有一個按鈕,點擊按鈕會出現一個彈出框。這裏有個特別的地方,就是在推薦頁的頂部設置了一個置頂的新聞
輸入框綁定了一個tap事件,使得在點擊輸入框但不輸入值的時候改變placeholder的值。
在tab欄的右邊有一個按鈕點擊按鈕將會出現一個彈出層,前面的gif中有演示,是新聞種類的選擇框,點擊關閉按鈕可以關閉彈出層
最後就是首頁最重要的新聞顯示頁面了,爲了節省項目的時間,這裏使用了有讚的框架vant-weapp有興趣的小夥伴可以去了解下。在tab標籤欄設置了6個標籤頁,但是隻會顯示4個標籤頁想要顯示其他的可以左右拖動標籤欄,這裏將推薦頁設置爲了默認激活的。由於每個每個標籤頁代碼基本都相同的,只是在推薦頁是的第一欄是置頂信息,還有就是獲取的數據不同,有關數據獲取在下面介紹代碼將會細講,爲了提高代碼的複用,這裏使用了模板,將複用的代碼寫在寫在另外的文件下,使用時直接調用就可以了。
每個標籤對應都創建了一個集合,這裏我爲置頂新聞也另外創建了一個集合,並且給每條信息設計好需要用的字段方便自己獲取數據和使用數據,由於雲數據庫是可以導入json文件或者csv文件,並且每個新聞也都需要上拉加載數據需要更多的數據,自己造數據費時間又麻煩,所以我這裏自己寫了爬蟲爬取自己需要的數據並保存到json文件中,直接將數據導入到數據庫中。
這樣設計數據庫也是使得從數據庫獲取數據方便了一些。寫一個module函數就可以獲取每個標籤的數據。
每條數據的字段如下,其中news_id起到很重要的作用,將首頁的每條新聞和對應的詳情頁面聯繫起來。
在每一個標籤頁使用模板,並且設置了一個data(給不同頁面傳入需要顯示的對應新聞信息,用於在頁面顯示),由於默認激活頁面是推薦頁所以在onload事件觸發時將默認加載推薦頁的數據,同時將推薦頁設置爲已被激活頁面,數據加載這裏寫了一個加載函數
module: function(title) {
let counter = this.data.counter
// console.log(title)
wx.cloud.callFunction({
name: 'module',
data: {
counter: counter,
title: title
}
}).then(res => {
// console.log(res)
let cnews = this.data.cnews
let data = res.result.data
// console.log(data)
for(let i = 0; i < data.length; i++) {
// console.log(data[i].date)
data[i].date = data[i].date.slice(0, 10)
cnews.push(data[i])
this.imgCheck(data[i].images, data.new_id, title)
}
// console.log(data)
this.setData({
hiddenLoading: true,
cnews: cnews,
counter: counter+1
})
})
}
傳入一個title就是當前顯示的標籤的標題,默認的是推薦,使用一個counter計數,每次只會加載5條新聞條數據,從數據庫獲取新聞的信息是由一個雲函數來解決的
// 雲函數入口文件
const cloud = require('wx-server-sdk')
cloud.init()
const db = cloud.database()
// 雲函數入口函數
exports.main = async (event, context) => {
return await db.collection(event.title).skip((event.counter-1)*5).limit(5).get()
}
給雲函數傳入兩個數據,一個是title就是從對應集合獲取信息,還有一個就是counter用來計算獲取信息的位置,因爲,在向上拉取加載更多新聞的時候需要加載數據,我這裏設置每次加載5條數據,所以傳遞給雲函數一個counter,每次調用了雲函數從與數據庫獲取一次數據counter就會+1,從而使得每次上拉加載數據時忽略已經加載的數據從後面加載數據。每次加載數據都會更新一次保存數據的數組,在主頁的index.wxml頁面將會判斷並獲取數據使用一個for循環將數據顯示到對應的標籤頁
在置頂新聞那部分在數據獲取的數據其實也沒很特別,我只是將置頂新聞集合中最新的新聞從雲數據庫拿下來,然後展示頁面中
這裏也實現了下拉刷新,使用了小程序的onPullDownRefresh函數下拉刷新將會獲取最新的數據,並且將最新的數據插顯示在最上部分,由於每次下拉需要插入數據到集合的前面,所以我這裏顯示不明顯
onPullDownRefresh: function () { // 監聽下拉動作來獲取最新新聞信息
wx.showToast({
title: '推薦中',
image: '../../../image/加載.png'
})
let title = this.data.title;
wx.cloud.callFunction({
name: 'module',
data: {
counter: 1,
title: title
}
}).then(res => {
// console.log(res)
let cnews = this.data.cnews
let datas = res.result.data
let data = datas.concat(cnews)
this.setData({
hiddenLoading: true,
cnews: data,
})
})
}
在module函數有使用了一個圖片鑑黃功能,使用了騰訊的一個圖片識別接口,畢竟不是什麼圖片都能顯示出來,所以寫了一個imgCheck函數來檢測每一條新聞的所有圖片,當圖片不合格的時候則將這條新聞刪除。我沒有使用圖片去測試,更不可能展示出來是吧,相信大家都懂(猥瑣笑)。
當不同標籤頁進行切換的時候會有一個onchange事件,onchange事件會獲得title和index。並且將onchange之後的數據保存到一個數組中,在onchange事件裏面使用module函數來獲取對應的title的數據並且判斷這條數據是否是最近加載過的,如果是上次onchange事件加載過的函數將會顯示上次事件保存的數據,在wxml中也會判斷是否是上次激活的頁面來顯示對應的數據。
前面一直再講後端的東西,我也這篇文章也是爲了講雲開,說實話有了雲開發真的方便了很多,一個人就可以搞定前後端的東西。不過爲了方便大家的理解,我還是講一下界面的內容。
既然前都已經將數據拿過來了,最後要做的就是將數據展示出來了,每條數據
<van-tab title="推薦" >
<!-- 推薦tab標籤頁的置頂新聞框 每一個新聞框可以點擊進入到詳情頁-->
<van-panel class="topping" title="{{topTitle}}" status="置頂" use-footer-slot bind:tap="showTopDetail" data-item="{{topNew_id}}">
<view class="images">
<image src="{{images[0]}}" />
<image src="{{images[1]}}" />
<image src="{{images[2]}}" />
</view>
<view solt="footer" class="footer">
<view class="author">{{topAuthor}}</view>
<view class="comment">評論{{topComment}}</view>
<view class="Date">{{topDate}}</view>
</view>
</van-panel>
<template is="container" data="{{news: active1 == 'news'?news:cnews, hiddenLoading}}"></template>
</van-tab>
<template name="container">
<view class="container">
<loading hidden="{{hiddenLoading}}"></loading>
<view class="news" wx:for="{{news}}" wx:for-item="info" wx:key="info.new_id">
<van-panel class="new" title="{{info.title}}" bind:tap="showDetail" use-footer-slot data-item="{{info.new_id}}">
<view class="images" wx:if="{{info.images.length > 0}}">
<image src="{{info.images[0]}}"/>
<image src="{{info.images[1]}}"/>
<image src="{{info.images[2]}}"/>
</view>
<view solt="footer" class="footer">
<view class="author">{{info.author}}</view>
<view class="comment">評論{{info.comments}}</view>
<view class="Date">{{info.date}}</view>
</view>
</van-panel>
</view>
</view>
</template>
這裏用的就是MVVM思想,將數據綁定到UI界面,在js文件中獲取到數據後,這裏將數據拿過來使用。
這裏由於每條新聞的圖片數量是不確定的,並且最多隻顯示三張圖片。所以直接固定了3個image標籤並且固定了image的大小,當圖片沒有的時候就不會顯示圖片。
詳情頁
很多在首頁講過的東西我在詳情頁也就不再多說了,大家有不懂可以去看源碼,畢竟講那麼多廢話就是浪費時間,我儘量挑出最精彩的部分來寫。
在首頁的每一條新聞都綁定了一個跳轉tap事件,當點擊新聞後將會跳轉到詳情頁,並且將新聞的id和title作爲參數傳給詳情頁。
showDetail: function(e) { // 點文章顯示文章詳情
let item = e.currentTarget.dataset.item;
let title = this.data.title;
// console.log(e)
wx.navigateTo({
url:`../detail/detail?contentId=${item}&title=${title}`
})
}
在點擊跳轉到詳情頁後,將會在onload的事件中獲取到對應的新聞id,並將id存到data裏面。由於在爬取詳情頁的時候沒有爬下來,所以我隨便將一些簡單的內容放在content裏面。
詳情頁這部分我將頁面分爲了內容部分和評論。然後還有就是使用了一個fixed將輸入框等按鈕固定在屏幕底部
內容部分又分爲了四部分,分別是標題部分,作者頭像和暱稱,內容部分,點贊轉發部分。
第二部分爲顯示像和暱稱我使用了一個flex的浮動佈局將並且將暱稱部分的flex設置爲1使得頭像和關注按鈕分別在兩邊。頭像使用了一個image標籤並且將image標籤的大小固定,畢竟用戶上傳的圖片肯定大小不一樣。第四部分只要使用4個view在把圖片和內容放進去再使用一個flex佈局就可以搞定。
既然界面佈局已經搞定現在就是要拿數據了,在點擊新聞進入來詳情頁的時候會的到新聞的id和title,這樣可以通過唯一id(每條doc的id)的和title(集合的名字)從雲數據庫拿出對應新聞數據。這裏代碼就不貼出來了,跟前面首頁的差不多,有需要的可以去github看源碼。
接下來就是評論部分的內容了,個人認爲這個地方還是挺有趣而且在更新數據庫的時候還有權限問題,前面沒有講這個問題就是打算放到評論部分一起來講。
在頁面的底部固定了一個評論框,包含輸入框,跳轉到評論的按鈕,收藏按鈕,轉發按鈕。
點擊轉發按鈕會出現一個彈窗,可以選擇需要轉發到的渠道,並且給彈出層背景添加了蒙層效果,只有在點擊蒙層或者取消按鈕彈出框纔會消失。
這裏只實現了轉發到微信的功能,只需要調用一下微信小程序的onShareAppMessage接口就可以搞定,當點擊微信的圖標後可以轉發給朋友或者微信羣。
收藏按鈕我就是用了一個wx:if來判斷顯示的是那個image點擊一個隊bool值取反。
點擊評論按鈕可以從直接跳轉到評論的頂部,使用一個scrollview將整個詳情包裹住然後
使用它的一個屬性scroll-into-view當點擊底部的評論按鈕時將評論部分的id賦值給scroll-into-view就可以實現錨點跳轉了。在這個地方我踩了一個坑,沒有給scroll-into-view設置一個高度導致效果一直出不來,由於詳情頁需要評論頁面高度是改變的,所以直接給它設置一個100vh就可以完美搞定這個地方的錨點跳轉了。
最後就是輸入框了點擊輸入框或者左邊的輸入按鈕就可以彈出評論輸入框了,當輸入框內有值的時候發佈按鈕會改變顏色。當未授權登錄將無法發佈評論
這裏就需要在我的頁面點擊登錄進行授權,獲取獲取用戶信息。
登錄功能的實現在頁面登錄按鈕設置屬性爲open-type=getUserInfo,bindgetuserinfo=getUserInfo
點擊登錄按鈕授權登錄將會獲取用戶信息,並將用戶信息保存到全局上,這樣在詳情頁面便可以判斷或者使用用戶信息。
授權就可以發佈新聞評論了,由於在登錄的時候獲取到了用戶使用,所以在評論的是就有用戶avatar和nickname。當在評論輸入框中輸入了值並且用戶授權了登錄的時候點擊發布,同時將數據保存到數據庫中。下面是評論功能函數
submit: function() { // 實現評論功能,將發佈的評論同步到雲數據庫
let value = this.data.inputValue;
let new_id = this.data.new_id;
let userInfo = this.data.userInfo
// let new_id = '6594157273642172936'
if(userInfo){
comments.where({
new_id: new_id
}).get({
success: (res) => {
// console.log(res)
let comms= res.data[0].comments;
let people = {
content: value,
like: 0,
avatar: userInfo.avatarUrl,
nickname: userInfo.nickname
}
comms.unshift(people);
// console.log(comm)
this.setData({
comms: comms,
input: '',
})
wx.cloud.callFunction({
name: 'updateComments',
data: {
new_id: new_id,
comms: comms
}
}).then(res =>{
console.log(res)
})
}
})
}
}
評論部分的數據庫我只創建了一個comments集合,開始的新聞new_id就起到作用了,每一comment都有一個new_id,新聞的每一條評論就是設置爲一個對象,畢竟評論還包括頭像暱稱,點贊數,評論內容等。這樣設置評論數據庫好處就是,只要獲取新聞id在一個集合中就可以獲取到新聞對應的評論。
開始在更新數據庫的時候我沒有使用雲函數,而是在js中直接更新數據,獲得返回信息顯示是請求成功但是update數爲0,但是當我到數據庫中查看是發現數據並沒有更新成功,查了一下文檔發現是權限的問題,因爲數據的修改只能是管理者或者數據的創建者,而數據又是我自己手動輸入到雲數據庫的,在js中直接更新的數據庫的時候不是創建者而在小程序端又沒有管理者權限,所以沒有權限修改數據。既然無法是創建者想要修改數據只能是管理員了,所以這裏我使用了雲函數來修改數據。說到這裏大家應該意識到了雲函數的權限是什麼級別了吧,這裏給大家看下官方文檔的說明。
從官方文檔能看出雲函數是有多強大了,還就是雲函數也不能亂用,畢竟權限是最高的。
既然可以評論那就少不了點贊功能吧,雖然點贊是很普通的功能但是這裏涉及到了雲數據而且具體實現還是很有趣的。
每條評論都可以點贊一次再次點擊時將會取消點贊。評論部分的點讚我這裏寫的addLike函數綁定到點贊按鈕,由於每條評論都綁定了相同的點贊函數,所以需要區分是那條評論被點贊所以給每條評論設置了data-item="{{index}}"i(index是在使用for循環展示評論使所產生的)同時對應了評論在數據庫保存的位置,這樣一來就方便來區分被點讚的那一條評論了。
當被點贊後點贊按鈕將換爲紅色的按鈕,同時數據庫中like也要加一。再次點贊按鈕則還原,like也將還原。其實點贊功能還是很有趣的,這完全是我個人的想法,可能還有不好的地方,但是我還是推薦大家看一下。下面就是具體實現的代碼
addLike: function(e) { // 點擊點贊圖標增加點贊數同時保存到數據庫
let item = e.currentTarget.dataset.item;
let new_id = this.data.new_id;
let comms = this.data.comms;
let likeItem = this.data.likeItem;
let likebool = 'likeItem['+item+'].bool'
let liken = 'likeItem['+item+'].n'
if(typeof(likeItem[item]) == "undefined"){
this.setData({
[likebool]: false,
[liken]: 0,
})
}
if(likeItem[item]){
likeItem[item].n += 1;
if(likeItem[item].n%2){
comms[item].like += 1;
}else{
comms[item].like -= 1;
}
likeItem[item].bool = !(likeItem[item].bool);
}else{
likeItem[item].bool = true;
likeItem[item].n = 0;
comms[item].like += 1;
}
this.setData({
comms: comms,
likeItem: likeItem
})
// console.log(comms)
wx.cloud.callFunction({
name: 'updateComments',
data: {
new_id: new_id,
comms: comms
}
}).then(res =>{
// console.log(res)
})
},
在評論輸入框的下邊欄有一個複選框按鈕,圖片按鈕等這裏我使用了了flex佈局輕鬆搞定,這是我實現了下評論插入圖片功能,同時將圖片保存到雲端。其實評論插入圖片還需要優化,我在寫完文章後也還會繼續優化。
我這是使用了小程序雲開發的一個文件上傳接口wx.cloud.uploadFile,將圖片上傳後會生成一個fileID,我將fileID(也就是圖片地址)保存到當前評論對象的image下,同時更新本地的數據,再通過一個if來判斷當前的評論是否含有圖片,有的話就將圖片顯示在評論中。這裏代碼我就不貼出來,有需要的可以看源碼。
源碼地址:雲開發仿頭條地址