應該每個前端開發者都有一顆全乾全棧的心💗吧。 那就讓雲開發知足你javascript
雲開發一出來就開始玩,雲數據庫,雲函數,全棧的體驗和開發速度,真的不是通常的爽。 接下來工做中要開發一款新聞類小程序,因而就開始了對頭條君的調研,此篇文章,是個人我的總結和分析,歡迎大佬拍磚。css
看看效果圖:(沒有展現全,下面有更多配圖以供學習這個項目)html
小程序雲開發是什麼???前端
開發者可使用雲開發開發微信小程序、小遊戲,無需搭建服務器,便可使用雲端能力。java
雲開發爲開發者提供完整的雲端支持,弱化後端和運維概念,無需搭建服務器,使用平臺提供的 API 進行核心業務開發,便可實現快速上線和迭代,同時這一能力,同開發者已經使用的雲服務相互兼容 > ,並不互斥。android
目前提供三大基礎能力支持:git
- 雲函數:在雲端運行的代碼,微信私有協議自然鑑權,開發者只需編寫自身業務邏輯代碼
- 數據庫:一個既可在小程序前端操做,也能在雲函數中讀寫的 JSON 數據庫
- 存儲:在小程序前端直接上傳/下載雲端文件,在雲開發控制檯可視化管理
這是官方文檔的描述github
其實簡單的來講小程序雲開發是一款Serverless服務,開發者可使用它開發微信小程序、小遊戲,無需搭建服務器,便可使用雲端能力。目前提供雲函數、數據庫、存儲三大基礎能力支持。,而且將這些能力封裝成特定的接口,以wx.cloud.xxx來進行調用。數據庫
根據需求創建了三個集合json
三個集合經過id字段「鏈接」,要是個新手這時會想那麼麻煩幹嗎,直接所有放一塊兒,梭哈一下,全給拿過來,直接用。這確定是不行的,想一想要是這個數據多怎麼辦,你要讓用戶等你多久? 做爲前端工程師確定是但願給用戶帶來極佳的使用體驗,因此你想在頁面上展現什麼,就設置一個對應的數據去關聯,要什麼取什麼。後臺數據無非就是一對一,一對多,你要啥數據就用傳相關字段進雲函數,在雲函數裏進行簡單的增刪改查。
千萬記住,要考慮你的集合數據的使用範圍進行權限設置,好比我添加的是articles文章,那這是公開的。那我就應該在權限設置中修改成全部用戶可讀、僅管理員可寫,默認的是僅建立者及管理員可讀寫。
如何建表,表和表之間的聯繫,在動手項目以前要考慮好,避免表裏的內容重複致使內存浪費,在能實行其功能的基礎上作到不浪費內存。我上面的表建的就有點問題,圖片URL的地址存錯了地方,並且在兩表裏都存儲了,你們作的時候能夠吸收個人教訓。
頭條功能那麼可能是不可能寫的完,咱們在這裏就選擇其首頁、新聞詳情、登陸界面這部分來實現一下。
首頁推薦:頭條裏面有不一樣的新聞樣式,有無圖的,一張圖的還有三張圖的,因此咱們確定要分離出不一樣的模板,根據後臺傳過來的數據去判斷新聞的樣式最後在頁面中顯示出來 數據驅動頁面
新聞詳情:打開新聞詳情頁,分析以後發現大致分爲三個部分 頭部的做者信息 新聞內容 底部的用戶評論 這些信息都是在雲開發數據庫中不一樣的集合裏面,這些數據的提取操做封裝在雲函數裏面以便調用 這三部分都是重複的結構抽離出來做爲組件,組件很是的靈活,這樣作的好處是頁面結構將會更加的清晰,增長代碼的複用性,而且耦合度下降,後續程序的維護也更爲方便。
登陸界面:直接使用wx.getUserInfo得到用戶信息 後面會貼出js代碼
該頁面採用頂部的固定搜索欄、scroll-view和swiper內容區三個模塊,三個模塊都可採用絕對定位,搜索欄flex佈局,swiper內容區內swiper-item有關注,推薦,熱點,南昌和視頻
<scroll-view class="navBar-box" scroll-x="true" style="white-space: nowrap; display:flex ">
<view class="cate-list {{curIndex==index?'on':''}}" wx:for="{{category}}"
wx:key="{{item.id}}" data-id="{{item.id}}" data-index="{{index}}" bindtap="switchCategory">
{{item.name}}
</view>
</scroll-view>
複製代碼
在jsdata裏面 設置curIndex, toView,用以監控不一樣版塊的狀態 實現了將上面的scroll-view 和下面的文章swiper創建關係
<swiper class="notes" current="{{curIndex}}" bindchange="swiperchange">
<swiper-item class="focus">
...
</swiper-item>
<swiper-item class="category" wx:key="{{item.id}}">
<scroll-view class="cate-box" scroll-y="true" bindscrolltolower="loadarticles">
<view class="note" wx:for="{{notes}}" wx:for-item="note" wx:key="{{index}}">
<block wx:if="{{note.image.length < 3 }}">
<template is="templateone" data="{{...note}}"></template>
</block>
<block wx:elif="{{note.image.length >= 3}} ">
<template is="templatetwo" data="{{...note}}"></template>
</block>
</view>
</scroll-view>
</swiper-item>
</swiper>
複製代碼
不一樣的新聞顯示模板 使用組件化的概念,建立一個template文件夾
<template name="templateone">
<view class="newList">
...
</view>
</template>
<template name="templatetwo">
<view class="newList">
...
</view>
</template>
複製代碼
在須要用到模板的地方就能夠直接使用
<import src="../../components/template/template.wxml"/>
複製代碼
既然要用到模板那接下來咱們就把模板給寫出來。分析一下模板裏面的內容
裏面的數據除時間之外都是能夠直接從後臺調取在頁面上顯示出來的數據,但時間不同,它是變化的 在數據庫中time字段以時間戳的形式保存。在後面的詳情頁中也要用到時間的格式化,so寫一個js封裝起來
在小程序中咱們時時刻刻須要去請求數據,數據的調取封裝起來是極好的,存在util裏想用的時候拿一下就ok
wx.cloud.init();
const db = wx.cloud.database();
const notes = db.collection('articles');
// 加載notes,page=1默認形參 ,limit = 4 ,fn
const loadNotes = (fn,page = 1,limit = 4) =>{
//return 數據集 異步
const skip = (page -1) * limit;
notes
.count()
.then(() =>{
return notes
.limit(limit)
.skip(skip)
.get()
})
.then(res =>{
//console.log(res.data)
fn({
data:res.data
})
})
// fn(data);
};
module.exports = {
loadNotes,
}
複製代碼
WXS 是小程序的一套腳本語言,結合WXML,能夠構建出頁面的結構。了該一哈
新建一個timeapi.wxs文件,在template.wxml引用,定義模塊名便可引用:
<wxs src="../../utils/timeapi.wxs" module="timeapi" ></wxs>
.....
<text class="newList-item-time">{{timeapi.formatTime(time)}}</text>
複製代碼
timeapi.wxs文件和時間格式實現方法:
var formatTime = function (time) {
// 獲取當前時間
var getUnix = function () {
var date = getDate()
return date.getTime()
}
// 獲取今天零點時間
var getTodayUnix = function () {
var date = getDate()
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date.getTime()
}
// 獲取今年的1月1日零點時間
var getYearUnix = function () {
var date = getDate()
date.setMonth(0)
date.setDate(1)
date.setHours(0)
date.setMinutes(0)
date.setSeconds(0)
date.setMilliseconds(0)
return date.getTime()
}
// 獲取標準時間
var getLastDate = function (time) {
var date = getDate(time)
var month = date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : date.getMonth() + 1
var day = date.getDay() < 10 ? '0' + (date.getDay()) : date.getDay()
return date.getFullYear() + '-' + month + '-' + day
}
// 轉換時間
var getFormatTime = function (timestamp) {
var now = getUnix()
var today = getTodayUnix()
var year = getYearUnix()
var timer = (now - timestamp) / 1000
var tip = ''
if (timer <= 0) {
tip = '剛剛'
} else if (Math.floor(timer / 60) <= 0) {
tip = '剛剛'
} else if (timer < 3600) {
tip = Math.floor(timer / 60) + '分鐘前'
} else if (timer >= 3600 && (timestamp - today >= 0)) {
tip = Math.floor(timer / 3600) + '小時前'
} else if (timer / 86400 <= 31) {
tip = Math.ceil(timer / 86400) + '天前'
} else {
tip = getLastDate(timestamp)
}
return tip
}
return getFormatTime(time)
}
module.exports = {
formatTime: formatTime
}
複製代碼
wxs有優勢有缺點,用的時候要考慮好。注意如下三點。
詳情頁涉及到三個表的內容。那就在雲函數裏完成表的查詢組裝輸出,多方便啊。 這個代碼就不貼了,要參考的話,底部會把GitHub貼出來,目錄在上面的圖片。 使用雲函數的話有個技巧。在開發工具裏看是否成功,且把數據傳輸過來了。 如圖。
項目中的數據是從網站上爬下來的,數據庫裏的文章內容就是html的格式,so咱們應該把tml =>wxmlwxParse-微信小程序富文本解析自定義組件,支持HTML及markdown解析本
githup 上有使用方法,這裏我就不作重複的描述 點着 我這裏使用這個工具徹底是爲項目實現的而去作,若是真正作一個小程序新聞類項目確定是不太好的,先不說wxParse解析可能會出現亂碼,再就是它所佔的內存也不小。真正開發的時候就別用了,數據庫裏的數據也不會是html格式的。
在手機上顯示時(坑)
wxparse 代碼的一個 bug,在一些特殊的手機裏面,在 wxparse/html2json.js 中的第 112 和 119 行,都有一個 console.dir 這個函數的使用,把這個函數註釋掉,內容就能夠正常顯示出來
評論組件
咱們這裏提一下點贊功能實現,借用大佬的話點讚的變化是由用戶產生的一個交互,傳統的觀點就是用戶點贊->後端更新數據->前端拉取數據->數據驅動視圖的變化。 真實的體驗就是,很是的慢,慢到點擊後2秒才能看到點讚的效果,這種差勁的交互簡直就是一場災難。
那咱們就先把樣式給用戶表現出來,數據交給後臺慢慢異步處理
頁面佈局就沒啥好說的,
getUserInfo
button type="bindUserInfo"
auth 受權登陸
高階函數
異步的處理
html部分
<view wx:if="{{auth === 0}}" > //判斷登陸狀況
<button open-type="getUserInfo"
bindgetuserinfo="getUserInfo">
登陸
</button>
</view>
<view wx:else>
<view class="top">
<image class="avatar" src="{{avatarUrl}}"/>
<view class="name">
<text class="nickname">{{nickname}}</text>
<button>申請認證</button>
</view>
<icon type="zhixiang" color="#848484"></icon> //封裝的icon
</view>
複製代碼
//獲取應用實例
const app = getApp()
const globalData= app.globalData;
Page({
data: {
auth:-1,
nickname:'',
avatarUrl:''
},
onLoad: function () {
this.getScope(this.getUserInfo,()=>{
this.setData({
auth:0
})
});
},
//高階函數 success 參數也是一個函數
getScope(success,fail,name='scope.userInfo'){
wx.getSetting({
success:(res) => {
// console.log(res);
if(res.authSetting[name]){
success();
}else{
fail();
}
}
})
},
getUserInfo (){
// console.log('userinfo')
if(!globalData.nickname||!globalData.avatarUrl){
// 1. wx.getUserInfo(nickname,avatar)函數
// 2. 放到全局 函數
this._getUserInfo((res) =>{
// console.log(res);
this.setData({
nickname:res.nickName,
avatarUrl:res.avatarUrl,
auth:-1
});
globalData.nickname=res.nickName;
globalData.avatarUrl=res.avatarUrl;
});
}
},
_getUserInfo(cb = () =>{}){
wx.getUserInfo({
success:(res) =>{
cb(res.userInfo);
}
})
}
})
複製代碼
將平凡的事堅持下去,就會變的不平凡,路漫漫其修遠兮,吾將上下而求索。
您的鼓勵是我前行最大的動力,歡迎點贊,歡迎送小星星✨ ~