大風起兮'雲'飛揚 來一場實戰擁抱雲開發

寫在前面:

  小程序雲開發發佈有一段時間了,最近着手做了一個基於雲開發的小程序項目--仿《微博鮮知》,來自新浪的這款全新風格的小程序雖然界面非常簡約清新,但是內部還是內藏了很多玄機,在實現的路上遇上了不少坎坷,在這裏分享給大家。希望給大家提供一些思路。


先展示一下最終結果:更多圖片資源在這裏

一、 組件化思想

開發一個完整的小程序時,我們應該先分析其內部的結構。重複的結構抽離出來作爲組件,組件非常的靈活,可以嵌入一個頁面或多個頁面。

  在上面的gif圖中我們可以看到首頁的內容是一個個的新聞塊。
雖然這個新聞塊只在首頁中使用到,但是我還是把它抽離成了一個組件。這樣做的好處是頁面結構將會更加的清晰,並且耦合度降低,比如想換個主界面風格時,你可以直接換另一個組件添加進來。

  還有新聞內部頁面中,有多個小標題,每個小標題裏面嵌入了不等數量的新聞。如果不是採用組件化的話,到時候inner頁面的wxml結構就會亂成一鍋粥。所以這裏的建議是儘量組件化分離開來。

對於組件很陌生可以先看我的之前的這篇文章 組件化開發tabbar

下面是項目的頁面與組件目錄:

二、數據庫設計

既然是“全棧”,後端肯定要搞搞。後端的核心就是數據。那麼我們就先把數據庫分析一下。這裏我是這樣分析的,

  • 從頁面獲得字段,
  • 然後再理解數據間的關聯,如一對多,一對一。

這裏我構建了5個集合

fresh-mainNews 主頁新聞集合

subNews字段是一個數列,存儲着fresh-subNews Doc的_id,這樣就將這兩個集合綁定了起來,在後面我們會講到在雲函數中把這兩個集合融合起來返回一個新的數據變得完整一些的集合。

有人可能會問,雲數據庫不是noSQL嗎,爲什麼不把所有數據全部整合到一個全部的JSON,那樣就可以只調用一次JSON。

我的理解是:

  我們查詢只是需要查詢我們想要的數據,不需要的數據可以等需要的時候再根據關聯去請求。
比如這個項目中的首頁新聞塊,每一個新聞塊內部都關聯着大量的子新聞,第一次加載就全部把這個小程序需要的所有數據都加載出來就有點瘋狂了。

  • fresh-subNews     內部頁面新聞小標題集合
  • fresh-comments    評論集合
  • fresh-detailNews    詳細新聞集合
  • fresh-users        用戶集合

這裏查看更多的數據庫信息

三、頁面構建

  講到這裏就該說頁面的構建了。頁面可以想象成一個架子,一個承載數據的容器。頁面通上數據,就變得活起來。MVVM,數據驅動視圖。交互靠數據,組件間的通信,組件與頁面間的通信都是數據。
  {{}} -> 就像是流浪法師大招神奇的傳送門。後面會將給出一個精彩的組件通信例子(點擊目錄如何實現標題欄置頂)。

四、關於雲開發。

雲開發三大核心:

雲函數:通俗的理解就是你寫的函數在雲端運行,可以把複雜的業務邏輯放在雲函數裏

數據庫:一個既可在小程序前端操作,也能在雲函數中讀寫的 JSON 數據庫

存儲:在小程序前端直接上傳/下載雲端文件,在雲開發控制檯可視化管理,可以上傳照片下載照片,或者一些其他文件。

在這裏詳細介紹一下操作雲函數提取數據庫的流程,
這裏我們以獲取首頁數據爲例:

  1. 先在雲函數目錄新建一個函數:mianNewsGet

  1. 打開該雲函數的index.js

我這裏用的是vsCode+node+yarn環境。
open in terminal(在終端中打開),yarn一下,添加依賴。
或者參考雲函數官方文檔

  1. 編寫雲函數查詢數據
// 雲函數入口文件
const cloud = require('wx-server-sdk')
// 雲函數初始化
cloud.init()
//獲取數據庫句柄
const db = cloud.database()

// 雲函數入口函數
exports.main = async () => {
    const mainNewsList = [];
    //向fresh-mainNews集合中獲得全部數據、因爲數據庫裏面現在存的數據不多,
    //如果多的話可以設置一個limit以及skip來獲取特定數量的數據
    const mainNews = await db.collection("fresh-mainNews").get();  
    for(let i = 0; i < mainNews.data.length; i++) {
      const mainNew = mainNews.data[i];
      let user_id = mainNew.setMan;
      //條件查詢 獲取特定id的docments
      const user = await db.collection('fresh-users').where({
        _id: user_id
      }).get();
      //限定條件如果有多條,只添加一條進去
      if (user.data.length > 0) {
        mainNew.setMan = user.data[0]
      }
      //這個循環是集合的拼接
      for (let i = 0; i < mainNew.subNews.length; i++) {
        const subNews = await db.collection("fresh-subNews").where({
          _id: mainNew.subNews[i]
        }).get();
        if (subNews.data.length > 0) {
          mainNew.subNews[i] = subNews.data[0]
        }
      }  
      //把拼好的docments挨個放進mainNewsList裏面也就是形成了一個全新的
      //融合的數據更爲完整的JSON數組   
      mainNewsList.push(mainNew);
    }
    return mainNewsList;
}
  1. 在首頁index.js裏面onLoad函數裏面調用雲函數
var that = this;
    wx.cloud.callFunction({
    // 聲明調用的函數名
      name: 'mainNewsGet',
    // data裏面存放的數據可以傳遞給雲函數的event  效果:event.a = 1
      data: {
        a: 1
      }
    }).then(res => {
    //res.result的值是雲函數的return的值
    //這裏將查詢的結果放入mainNewsList中,然後就可以在wxml中調用數據
      that.setData({
        mainNewsList: res.result
      })
    //打印一下結果看看有沒有成功獲取數據
      console.log(this.data.mainNewsList)
    }).catch(err => {
      console.log(err)
    })

獲取的數據:

我們可以看到原本的subNews裏面本來存放的是_id的數值,融合後變成_id對應的整個doc

變化:
[_id1.value,_id2.value] ---> [{_id1:value,key1:value1,key2:value2},~]

雲函數的調用,數據庫的查詢。就是這麼簡單的四步,雲開發的門檻很低,功能也很強大,只要你去嘗試,很輕鬆的就能夠實現。

五、關於時間格式化。

  1. 寫在utils文件夾裏添加xx.js
const formatTime = date => {
    var dateNow = new Date();
    var date = new Date(date);
    const hour = date.getHours()
    const minute = date.getMinutes()
    var times = (dateNow - date) / 1000;
    let tip = '';
    if (times <= 0) {
        tip = '剛剛'
        return tip;
    } else if (Math.floor(times / 60) <= 0) {
        tip = '剛剛'
        return tip;
    } else if (times < 3600) {
        tip = Math.floor(times / 60) + '分鐘前'
        return tip;
    }
    else if (times >= 3600 && (times <= 86400)) {
        tip = Math.floor(times / 3600) + '小時前'
        return tip;
    } else if (times / 86400 <= 1) {
        tip = Math.ceil(times / 86400) + '昨天'
    }
    else if (times / 86400 <= 31 && times / 86400 > 1) {
        tip = Math.ceil(times / 86400) + '天前'
    }
    else if (times / 86400 >= 31) {
        tip = '好幾光年前~~'
    }
    else tip = null;
    return tip + [hour, minute].map(formatNumber).join(':')
}

const formatNumber = n => {
    n = n.toString()
    return n[1] ? n : '0' + n
}

//將這個接口暴露
module.exports = {
    formatTime: formatTime,
}
  1. 在需要的頁面的xx.js裏面引入

import { formatTime } from '../../utils/api.js';

  1. 格式化獲取的時間數據
let mainNewsList = that.data.mainNewsList
      for(let i =0; i < mainNewsList.length;i++) {
        let time = formatTime(mainNewsList[i].time)
    //這是setData()的數組用法,會經常用到
        var str = 'mainNewsList['+i+'].time' 
        that.setData({
          [str]:time
        }) 
    }

六、 關於一些很有用但是你可能不知道的小程序技巧

  1. 全屏顯示圖片,能夠實現多張圖片左右滑動並且還有數字索引現在在屏幕上,並且長按還能收藏以及下載(之前不知道這個API還特地做了一個組件來實現類似功能,簡直吐血)
 wx.previewImage({
        current: imgUrl, // 當前顯示圖片的http鏈接
        urls: imagePack // 需要預覽的圖片http鏈接列表
      })
  1. 非常方便的一個API能夠滑動到某個位置
wx.pageScrollTo({
    scrollTop: 一個數值(自帶px單位),   //滾動到數值所在的位置
    duration: 50                          //執行滾動所花的時間
    })
  1. 查詢節點query.selectAll('類名')及query.select('#id')

官方文檔

var that = this
let catalogIndex = that.data.catalogIndex;
query.selectAll('類名').boundingClientRect(function (rects) {
        rects.forEach(function (rect) {
          rect.top     // 節點的上邊界座標st,
//還有一些別的屬性,這個查詢節點是後面講到的目錄跳轉關鍵API
          })
        })
      }).exec()
    },
  1. setData()一些技巧。
//給數組設置值 還可以有var xx = 'xx['+idx+'].key'的形式
var doneList = 'doneList['+idx+']'
      that.setData({
        [doneList]: true,
      })

有時候我們還可以先改變某個數的值再去setData()它,這是setData()的一個很好用的技巧,不過需要去運用一下才好理解
如:

 dataPack.likeNum = (supLikeNum===-1 ? dataPack.likeNum: supLikeNum);
    this.setData({
      comment: dataPack,
    })

七、 項目最精彩的兩個部分

1.點擊目錄欄頁面將相應新聞欄置頂,先看下效果


  這個效果在別的小程序裏面都沒有見過,應該是微博鮮知獨創的,在這裏先對原作者表達一下敬意。內部的構造也是非常巧妙,不同於我們常見的外賣的錨點定位。

我們先來分析一波:

mvvm,視圖是由數據驅動的,我們要透過現象看本質,去思考底層的數據,這樣我們很快就會有思路:

  • 點擊目錄欄的item項如果綁定了一個data-idx等於循環的索引,可以在e.currentTarget.dataset.idx拿到這個item的索引。
  • 我們把這個數據通過組件通信傳遞到inner頁面,然後在由inner頁面把數據轉交給subNews
  • 並且在inner頁面的js中綁定subNews的goTop事件,這樣產生了一個catalog組件->inner頁面->subNews的關聯,數據爲item的索引。觸發catalog就能夠控制subNews組件的移動,是不是還有點繞, ok

<font color=red size=4>show the code:</font>

1.catalog/index.wxml

<block wx:for="{{subNews}}" wx:for-item="subNewsItem" wx:for-index="idx" wx:key="index">
            <view class="subTitle-item" bind:tap="scrollFind"
    //關鍵1:綁定item索引
            data-hi="{{idx}}">
 <text>{{subNewsItem.title}}</text>
                </view>
        </block>
  1. 獲得索引,並綁定inner頁面

catalog/index.js

 scrollFind: function(e) {
      //點擊後 實現inner頁面特定新聞小標題置頂
      let curIndex = e.currentTarget.dataset.hi
      // 關鍵2: 與inner頁面取得聯繫
      var myEventDetail = {index: curIndex} // detail對象,提供給事件監聽函數
      var myEventOption = {} // 觸發事件的選項
      this.triggerEvent('catalog', myEventDetail)
    }
  1. inner/inner.js 取得與catalog的通信
 onCatalog: function(e) {
    e.detail // 自定義組件觸發事件時提供的detail對象
    console.log(e.detail.index)
    //關鍵:3 把索引存儲到data
    this.setData({
      catalogIndex : e.detail.index
    })
    
//關鍵4: 頁面可以通過組件的id取得其頁面引用組件的方法
// this.subNews=this.selectComponent("#subNews")
    this.subNews.goTop();
  },
  1. 給subNews傳catalogIndex,並且標上id
<subNews ~省略~ catalogIndex="{{catalogIndex}}" id="subNews"></subNews>
  1. 在subNews中先定義一個圖片加載事件,這樣在頁面加載完成時會觸發其綁定的事件,這是來自瀑布流的靈感。可以在圖片加載出來的時候觸發onImageLoad函數,而在這個函數裏我們可以幹一些準備的事情。
//subNews/index.wxml
//一個看不見的圖片,來自瀑布流的靈感,能夠產生主動觸發的事件
<view style="display:none">
  <image src="{{mainImg}}" bindload="onImageLoad"></image>
</view>
//subNews/index.js
onImageLoad: function () {
      var that = this
      let offsetList = that.data.offsetList;
      const query = wx.createSelectorQuery().in(this)
//之前講到過的API獲取節點信息,我們把它存儲到offsetList偏移量數組,他存儲着每一個節點在屏幕的位置,
//配合wx.pageScrollTo可以達到新聞欄置頂的效果
      query.selectAll('.subNews-wrapper').boundingClientRect(function (rects) {
        rects.forEach(function (rect) {
          rect.top     // 節點的上邊界座標
          offsetList.push(rect.top)
          that.setData({
            offsetList,
          })
        })
      }).exec()
    },
  1. 給標題欄綁定上goTop事件
goTop: function (e) {
      var that = this
      let catalogIndex = that.data.catalogIndex;
      //這裏offsetList是一個data裏面的數據,來保存所有的節點的上邊距座標
      let offsetList = that.data.offsetList;
      wx.pageScrollTo({
            scrollTop: offsetList[catalogIndex],   //滾動到具體數值所在的位置
            duration: 50                          //執行滾動所花的時間
          })
    }

至此,你就實現了這個看似簡單卻非常巧妙的功能,組件->頁面->組件,秀得眼花繚亂。如果還是有些不理解的話,等下可以下載我的代碼去看。
至於爲什麼要弄一個圖片的加載然後觸發那個事件呢,這是因爲如果你把獲取offsetList偏移量數組的函數放在goTop裏的話,進入頁面第一次的點擊會無效,這樣產生的體驗肯定是非常不舒服的。

2. 點贊優化

先展示一下效果:


先說一下優化的是什麼:點贊效果的延遲極大降低

因爲點讚的變化是由用戶產生的一個交互,傳統的觀點就是用戶點贊->後端更新數據->前端拉取數據->數據驅動視圖的變化。
真實的體驗就是,非常的慢,慢到點擊後2秒才能看到點讚的效果,這種差勁的交互簡直就是一場災難。

  • 先給傳統的、局部刷新優化的,效果還是很差的一段代碼:
for(let i = 0; i< that.data.comments.length; i++)
        {
    //當點擊該個評論時,只更新這一條數據
          if (i == idx) {
            var str = 'comments['+idx+'].likeNum'
            that.setData({
              [str]:res.result.data.likeNum,
            })
            console.log(likeNumList[idx])
          }
        } 
  • 優化後:
  data: {
    doneList: [],      //是否按下
    likeNumList: [],  //模擬點贊數數組
    likeAdd: 10,      //點贊每次增加數,根據你的設置來,你後端每次加1這裏就寫1
  },
  
var doneList = 'doneList['+idx+']'
likeNumList[idx] = (that.data.comments[idx].likeNum + that.data.likeAdd);
      that.setData({
        likeNumList,
       [doneList]: true,
        likeAdd: that.data.likeAdd+10
      })
<text class="dianzanNum">{{likeNumList[idx]?likeNumList[idx]:item.likeNum}}</text>

優化思路是怎麼樣的呢?

  用一個數組來存放/模擬更新的數據,如果數字的索引位置被賦值,則頁面直接顯示這個更新的數字,也是異曲同工之妙。因爲用戶關心的是數據的變化,我們可以先把數據的變化產生,至於數據後端的變化讓他異步慢慢的去做。

  從這裏發散思想,是不是評論功能也能夠用這樣的思路同樣去達到極致的速度與交互體驗呢。

點讚的延遲幾乎爲無,體驗到<font color=red size=3>點贊</font>的極致快感,讓人幾乎停不下來~~(暗示一波)


篇幅所限,文章到這裏就差不多了。

項目地址:https://github.com/HappyBirdwe/newsDance/tree/master/weiboFresh奉上
精心寫的項目,細節很不錯喲,歡迎大家☆☆☆ star ☆☆☆☆


結語

學習的道路上免不了坎坷,希望文章的分享能夠爲大家提供一些思路,學習的過程減少一點彎路,這就是這篇文章最大的價值,歡迎大家提問及指正。

最後在這裏感謝一下:

      騰訊雲提供的技術支持
    新浪團隊的微博鮮知作者
  掘金這個優秀的平臺
點贊動作超帥的你

微博鮮知小程序官方傳送門:

體驗真的很不錯哦,界面非常簡約,大家可以體驗一波

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章