前言
看掘金也又一年多了,感嘆各位大佬技術6的一批,剛上學的時候也給人搞過一兩個小程式,突然心血來潮想總結總結經驗,給自己也搞一個爽一爽,順便也在寫一篇,讓各位大佬看看還有什麼問題,畢竟本屌虛心的一批
無圖言雞兒=>成品圖
菜雞實踐中總結的一些tip
- 小程式登陸獲取使用者資訊(與伺服器互動)
- 小程式顯示富文字與markdown(使用了towxml)
- 對wx.request的小封裝(搞成promise爽一爽)
- iview weapp元件的使用
- 小程式原生元件的使用
- 針對上拉載入更多的小總結
- 手動實現個簡單的假的瀑布流
功能需求
- 分類顯示部落格
- 顯示部落格內容(富文字/Markdown)
- 使用者在微信小程式登陸
- 使用者登陸後評論部落格
- 顯示我愛看的一些書籍資訊(特殊服務)
- 一鍵將書籍傳送到kindle(特殊服務)
前期準備
- 去UI中國或者其他網站扒拉個看著順眼的UI設計圖(畢竟審美是硬傷)
- 去阿里圖示網站找上一套順眼的圖示。選幾個用來給小程式的底部tab欄用,由於小程式tab圖示切換是靠換圖片來實現的,因此可以從阿里圖示上每個圖示下兩份,灰色版的下一份,彩色版的下一份。稍微整理一下圖示的命名
這個專案用到的介面主要有
- 獲取頂部輪播圖的介面(getSlides)
- 根據欄目獲取欄目下部落格的介面(getPostOfCategory)
- 獲取文章的介面(getPost)
- 使用者登陸介面(wxLogin)
- 使用者繫結郵箱的介面(bindMail)
- 傳送書籍的介面(sendBook)
- 獲取配置的介面(getConfig)
- 獲取文章評論的介面
- 使用者評論文章的介面 這裡要解釋一下getConfig,每日推薦、首頁部落格等一些顯示部落格的地方說白了其實就是從服務端取不同欄目下的文章,因此需要一個欄目id來呼叫getPostOfCategory介面,但是又不想把欄目id寫死在小程式裡,萬一哪天心情不好把欄目給刪了小程式豈不要改程式碼?剛好當時做後臺的時候搞了一個配置管理的功能,這次剛好用到;專門為小程式新建了一個配置組,把小程式用到的欄目id和其他亂七八糟的東西(比如分頁的每頁大小,背景圖啥的)以key-value的形式存在後端,每次小程式啟動的時候從後端獲取一下配置存在localStorage裡,這樣在後臺改改配置,小程式顯示的欄目自然也就切換了。
不瞎bb了開整
基礎設施
- 根據需求規劃一下,在pages資料夾裡把所需要的頁面右鍵新建出來
- 由於用到了iview元件和towxml,所以把這倆老哥也給放根目錄
- 新建一個netUtils.js(網路層),封裝一下wx.request,放進netUtils裡,同時把伺服器地址baseUrl也作為常量放在netUtils裡,所有的網路訪問url都從baseUrl拼接而來,方便切換測試與生成環境
- 再此基礎上搞出來一個dataUtils(即資料層),將所有的從伺服器獲取資料的網路請求行為(全搞成promise)全部放在這裡
- (可選)由於很多頁面都會跳轉到文章內容頁、搜尋頁等頁面,因此後面又搞了一個navUtils.js把常用的跳轉都寫在這裡面,省的每次都在寫一遍
檔案結構:
netUtils.js關鍵部分
const BASEURL = "https://localhost:8888/";
const APIURL = "https://localhost:8888/api/";
/**
* 封裝request
*/
const requestPromise = function ({ url, data, header,
method = 'GET' }) {
return new Promise((resolve, reject) => {
wx.request({
url: url,
data: data,
header: header,
method: method,
success: (res) => { resolve(res) },
fail: (err) => { reject(err) }
})
});
};
module.exports={
BASEURL:BASEURL,
APIURL:APIURL,
request: requestPromise
}
複製程式碼
dataUtils關鍵部分
let netUtils = require('./netUtils.js');
/**
* 獲取伺服器資料基本方法
*/
function getServerDataPromise(url,data,header=null,method='GET'){
let dataUrl = netUtils.BASEURL+url;
return new Promise((resolve, reject) => {
netUtils.request({
url: dataUrl,
data: data,
header:header,
method:method
}).then(res => {
resolve(res);
}).catch(err => {
reject(err);
});
});
}
/**
* 獲取欄目下文章
*/
function getPostOfCategoryPromise(data) {
let url ='api/front/portal/getPostOfCategory';
return getServerDataPromise(url,data);
};
/**
* 獲取幻燈片
*/
function getSlidesPromise(data){
let url ='api/front/portal/getSlide';
return getServerDataPromise(url,data);
}
........各位老哥根據實際情況把自己的介面封裝一下搞一搞
module.exports = {
getPostOfCategory: getPostOfCategoryPromise,
getSlides: getSlidesPromise,
checkToken: checkToken,
userLogin:userLoginPromise,
getContent: getContentPromise,
addComment: addCommentPromise,
getComment:getCommentPromise,
getKindleEmail: getKindleEmailPromise,
bindKindleEmail: bindKindleEmailPromise,
sendBook: sendBookPromise,
getNav:getNavPromise,
search: searchPromise,
getUser: getUserPromise,
checkLogin: checkLoginPromise,
getConfig: getConfigPromise
};
複製程式碼
先搞個首頁
我的個人習慣是先把頁面的js獲取資料的部分寫好,然後再去寫wxml與wxss,有了資料填充,比教容易看出來頁面的效果,除錯完頁面後,在取js把點選事件、跳轉等其他的一些程式碼補全。 前端程式碼比較簡單,就不在這貼了,值得注意的一點是,首頁使用了iview的元件,因此在index.json中應先把使用的元件配置一下 index.json
{
"usingComponents": {
"i-row": "../../iview/row/index",
"i-col": "../../iview/col/index",
"i-spin": "../../iview/spin/index",
"i-icon": "../../iview/icon/index",
"i-message": "../../iview/message/index",
"i-divider": "../../iview/divider/index"
},
"enablePullDownRefresh":true
}
複製程式碼
index.js
let netUtils=require('../../utils/netUtils.js');
let dataUtils=require('../../utils/dataUtils.js');
let navUtils=require('../../utils/navUtils.js');
const { $Message } = require('../../iview/base/index');
// pages/index/index.js
Page({
/**
* 頁面的初始資料
*/
data: {
IMGURL: netUtils.BASEURL,
// 幻燈片
slides:[],
// 推薦
recommends:[],
recommendPage:1,
recommendPageSize:5,
// blog
blogs: [],
blogPage: 1,
blogPageSize: 10,
//config
slideId: getApp().globalData.StorageDB.get('config.slideId'),
recommendCategoryId: getApp().globalData.StorageDB.get('config.recommendCategoryId'),
blogCategoryId:getApp().globalData.StorageDB.get('config.blogCategoryId'),
hasMore:true
},
/**
* 生命週期函式--監聽頁面載入
*/
onLoad: function (options) {
this.getSlides();
this.getRecommend();
this.getBlog();
},
/**
* 頁面相關事件處理函式--監聽使用者下拉動作
*/
onPullDownRefresh: function () {
this.setData({
// 推薦
recommends: [],
recommendPage: 1,
recommendPageSize: 5,
// blog
blogs: [],
blogPage: 1,
blogPageSize: 10,
hasMore:true
});
this.getSlides();
this.getRecommend();
this.getKindleBook();
this.getBlog();
},
/**
* 頁面上拉觸底事件的處理函式
*/
onReachBottom: function () {
this.getBlog();
},
//自定義方法
getSlides(){
dataUtils.getSlides({id:1})
.then(res=>{
if(res.data.status=='200'){
this.setData({
slides:res.data.data.item
});
}
});
},
getRecommend(){
dataUtils.getPostOfCategory({
page:this.data.recommendPage,
pageSize:this.data.recommendPageSize,
id:this.data.recommendCategoryId
}).then(res=>{
if (res.data.status == '200') {
if(res.data.data.pageData.length==0){
return;
}
let recommendPage = this.data.recommendPage;
recommendPage = recommendPage + 1;
let recommends = this.data.recommends.concat(res.data.data.pageData);
this.setData({
recommends: recommends,
recommendPage: recommendPage
});
}
else{
$Message({
content: '未獲取到資料~',
type: 'error'
});
}
});
},
getBlog() {
dataUtils.getPostOfCategory({
page: this.data.blogPage,
pageSize: this.data.blogPageSize,
id: this.data.blogCategoryId
}).then(res => {
if (res.data.status == '200') {
if (res.data.data.pageData.length == 0) {
this.setData({
hasMore:false
});
return;
}
let blogPage = this.data.blogPage;
blogPage = blogPage + 1;
let blogs = this.data.blogs.concat(res.data.data.pageData);
let blogLeft=blogs.filter((value,index)=>{return index%2!=0});
let blogRight = blogs.filter((value, index) => { return index % 2 == 0 });
this.setData({
blogs: res.data.data.pageData,
blogLeft:blogLeft,
blogRight:blogRight,
blogPage: blogPage
});
}
else {
$Message({
content: '未獲取到資料~',
type: 'error'
});
}
});
},
navToContent(e){
navUtils.navToContent(e.currentTarget.dataset.id);
},
navToBlog(){
wx.switchTab({
url: '../blog/blog',
});
}
})
複製程式碼
程式碼比較簡單(畢竟技術比較菜),值得注意的是這個getBlog方法,該方法獲取的是首頁上,中下部分的那一坨部落格,當頁面觸底時會會載入更多
覺得單純的排列下來比較平庸,因此想搞個瀑布流,反正沒那麼多效能和外觀要求,所以就搞了個假的瀑布流,具體操作就是在獲取到資料後,把資料分成兩半,左半邊和右半邊,在前端也是把這兩個陣列分別迴圈一下即可。
由於頁面有下拉重新整理與拉到底部會載入更多,因此,在這的邏輯就是,
當觸發上拉載入時
- 從伺服器獲取到資料
- 判斷下資料是否為空,若為空則說明無更多資料可載入將hasMore設為false即可,page與blogs資料均不變
- 若不為空則拼接到現有資料陣列的尾部,同時將頁碼page在現有基礎上+1(即將頁碼的操作放在每次請求之後,這樣每次請求時候無需考慮頁碼是否+1直接用即可),即先拼接在處理頁碼最後setData
當觸發下拉重新整理時
- 先將page設定為1,清空現有blogs資料
- 呼叫getBlog
這樣做能把下拉重新整理與上拉載入的功能均用getBlog來做無需根據情況來判斷如何處理(替換or拼接)從伺服器請求來的資料
第一期結尾
羅裡吧嗦的有點長了,越寫越感覺做的不咋地寫的更不咋地,看各位看官湊合的看下把(估計沒幾個人會看到這個地方...),我也整理整理思路,希望寫後續第二期的時候能把想表達的寫出來。