青銅選手帶你動手擼一個博客小程序給本身(也許是第一期)~(大佬請忽略此條)

前言

看掘金也又一年多了,感嘆各位大佬技術6的一批,剛上學的時候也給人搞過一兩個小程序,忽然心血來潮想總結總結經驗,給本身也搞一個爽一爽,順便也在寫一篇,讓各位大佬看看還有什麼問題,畢竟本屌虛心的一批php

無圖言雞兒=>成品圖

菜雞實踐中總結的一些tip

  • 小程序登錄獲取用戶信息(與服務器交互)
  • 小程序顯示富文本與markdown(使用了towxml)
  • 對wx.request的小封裝(搞成promise爽一爽)
  • iview weapp組件的使用
  • 小程序原生組件的使用
  • 針對上拉加載更多的小總結
  • 手動實現個簡單的假的瀑布流

功能需求

  1. 分類顯示博客
  2. 顯示博客內容(富文本/Markdown)
  3. 用戶在微信小程序登錄
  4. 用戶登錄後評論博客
  5. 顯示我愛看的一些書籍信息(特殊服務)
  6. 一鍵將書籍發送到kindle(特殊服務)

前期準備

  1. 去UI中國或者其餘網站扒拉個看着順眼的UI設計圖(畢竟審美是硬傷)
  2. 去阿里圖標網站找上一套順眼的圖標。選幾個用來給小程序的底部tab欄用,因爲小程序tab圖標切換是靠換圖片來實現的,所以能夠從阿里圖標上每一個圖標下兩份,灰色版的下一份,彩色版的下一份。稍微整理一下圖標的命名

3. 我的註冊個小程序帳號(直接去微信公衆平臺註冊,簡單的一批),註冊完後登錄一下,把appid與appsecrect搞到手

4. 數據接口準備(重要的一環,總不能都是空架子把),,根據各位看官的博客後臺的實際狀況搞,通常都有數據接口的,用來獲取文章啥的。這裏我用的是本身開發的後端,因此接口什麼的都是按需開發的~各位看官要是若是有興趣能夠找我要後臺(thinkphp+vue+element搞的管理後臺)

這個項目用到的接口主要有前端

  1. 獲取頂部輪播圖的接口(getSlides)
  2. 根據欄目獲取欄目下博客的接口(getPostOfCategory)
  3. 獲取文章的接口(getPost)
  4. 用戶登錄接口(wxLogin)
  5. 用戶綁定郵箱的接口(bindMail)
  6. 發送書籍的接口(sendBook)
  7. 獲取配置的接口(getConfig)
  8. 獲取文章評論的接口
  9. 用戶評論文章的接口 這裏要解釋一下getConfig,每日推薦、首頁博客等一些顯示博客的地方說白了其實就是從服務端取不一樣欄目下的文章,所以須要一個欄目id來調用getPostOfCategory接口,可是又不想把欄目id寫死在小程序裏,萬一哪天心情很差把欄目給刪了小程序豈不要改代碼?恰好當時作後臺的時候搞了一個配置管理的功能,此次恰好用到;專門爲小程序新建了一個配置組,把小程序用到的欄目id和其餘亂七八糟的東西(好比分頁的每頁大小,背景圖啥的)以key-value的形式存在後端,每次小程序啓動的時候從後端獲取一下配置存在localStorage裏,這樣在後臺改改配置,小程序顯示的欄目天然也就切換了。

不瞎bb了開整

基礎設施

  1. 根據需求規劃一下,在pages文件夾裏把所須要的頁面右鍵新建出來
  2. 因爲用到了iview組件和towxml,因此把這倆老哥也給放根目錄
  3. 新建一個netUtils.js(網絡層),封裝一下wx.request,放進netUtils裏,同時把服務器地址baseUrl也做爲常量放在netUtils裏,全部的網絡訪問url都從baseUrl拼接而來,方便切換測試與生成環境
  4. 再此基礎上搞出來一個dataUtils(即數據層),將全部的從服務器獲取數據的網絡請求行爲(全搞成promise)所有放在這裏
  5. (可選)因爲不少頁面都會跳轉到文章內容頁、搜索頁等頁面,所以後面又搞了一個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.jsonvue

{
  "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.jsthinkphp

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方法,該方法獲取的是首頁上,中下部分的那一坨博客,當頁面觸底時會會加載更多json

以爲單純的排列下來比較平庸,所以想搞個瀑布流,反正沒那麼多性能和外觀要求,因此就搞了個假的瀑布流,具體操做就是在獲取到數據後,把數據分紅兩半,左半邊和右半邊,在前端也是把這兩個數組分別循環一下便可。小程序

因爲頁面有下拉刷新與拉到底部會加載更多,所以,在這的邏輯就是,後端

當觸發上拉加載時微信小程序

  1. 從服務器獲取到數據
  2. 判斷下數據是否爲空,若爲空則說明無更多數據可加載將hasMore設爲false便可,page與blogs數據均不變
  3. 若不爲空則拼接到現有數據數組的尾部,同時將頁碼page在現有基礎上+1(即將頁碼的操做放在每次請求以後,這樣每次請求時候無需考慮頁碼是否+1直接用便可),即先拼接在處理頁碼最後setData

當觸發下拉刷新時api

  1. 先將page設置爲1,清空現有blogs數據
  2. 調用getBlog

這樣作能把下拉刷新與上拉加載的功能均用getBlog來作無需根據狀況來判斷如何處理(替換or拼接)從服務器請求來的數據數組

第一期結尾

羅裏吧嗦的有點長了,越寫越感受作的不咋地寫的更不咋地,看各位看官湊合的看下把(估計沒幾我的會看到這個地方...),我也整理整理思路,但願寫後續第二期的時候能把想表達的寫出來。

相關文章
相關標籤/搜索