小程式 網易雲課堂雲開發初體驗

三月源發表於2018-11-25

前不久,小程式推出了雲開發的功能,使開發者們無需搭建伺服器,用雲端能力直接邁入全棧開發。正巧用著網易雲課堂充電,介面精緻、細節到位,於是決定用雲開發來仿一仿網易雲課堂。

來,先看一波效果圖

購物車

小程式 網易雲課堂雲開發初體驗

直接購買

小程式 網易雲課堂雲開發初體驗


工具

在專案開發中選用好的工具使得工作事半功倍

  1. 微信開發者工具   (雲開發
  2. vscode  vs中如emmet之類的各種外掛能讓coding爽的不行,我是在vs中coding 在開發者工具裡除錯  。
  3. easymock 快速的模擬出資料請求,(主頁的資料請求量較多且只是可讀,就直接用easymock了)
  4. Mark man 讓你成為快樂切圖仔


專案結構

|-study163 專案名
    |-cloudfunctions  雲函式
        |-getMyCourse     獲取我的課程  
        |-getCourseInfo   獲取課程資訊
        |-getCart         獲取購物車
    |-miniprogram  專案模組
        |-components  自定義元件
            |-box-module       盒子 
            |-myCourse-module  我的課程
            |-special-module   專題 
        |-utils  工具
            |-indexMock        獲取主頁資料
            |-viewContent      文字處理
        |-pages  頁面
            |-account          賬號
            |-cart             購物車
            |-confirm          確認訂單
            |-courseInfo       課程資訊
            |-myStudy          我的學習
            |-index            首頁
        |-vant-weapp   有贊vant框架元件庫
            |-···      
        app.js         全域性js
        app.json       全域性json配置
        app.wxss       全域性wxss
複製程式碼

雲開發

在新版的開發者工具中,具有了雲開發的功能,且在啟動模板中新增了雲開發快速啟動模板

小程式 網易雲課堂雲開發初體驗

雲開發的三個核心

小程式 網易雲課堂雲開發初體驗

  • 資料庫 文件(JSON)資料庫 類似MongoDB
  • 儲存管理 儲存空間,類似百度雲盤
  • 雲函式 獨立的node專案
小程式 網易雲課堂雲開發初體驗

資料庫

課程的資料項是多出了我的預料的,這裡我只定義了我所使用了的部分。每個頁面顯示不同的課程。這裡要注意的一點是,資料滾動欄裡的、課程盒子裡的、課程資訊裡的圖片雖然看起來相似,但並不是同一張,所以儲存的連結不同。小tips:設計好資料庫後記得設定相對應的許可權管理,否則可能出現讀取不到資料的情況。

_id是資料庫自動生成的屬性(唯一),而我定義了一個id屬性,串聯個個資料表(collection),類似傳統sql資料庫中的主鍵。

course_info小程式 網易雲課堂雲開發初體驗

course_cart小程式 網易雲課堂雲開發初體驗

my_course小程式 網易雲課堂雲開發初體驗

儲存管理

當檔案儲存在此後,會生成一個下載地址,可以直接拿出來用。

小程式 網易雲課堂雲開發初體驗

當然也可以用相應的API直接進行上傳或下載

雲函式

雲函式就是部署在雲端的獨立執行的node後端專案,根據自身的業務具體實現。在生成雲函式後,首先記得安裝依賴,在終端中yarn。寫完後不光得儲存還得記得上傳並部署。

雲函式(小demo)的門檻不高,可以輕鬆助你實現全棧開發。

我的雲函式業務其實很簡單,就是簡單的查詢

// 雲函式入口檔案
const cloud = require('wx-server-sdk')
cloud.init()const db = cloud.database();const course_cart = db.collection('course_cart');

// 雲函式入口函式
exports.main = async (event, context) => { 
    let info = course_cart.where({ id: event.id }).get();  
    return info
}複製程式碼

眾所周知js是個單執行緒語言,而資料互動是個非同步過程。這一段沒有處理好的話,可能出現即使獲取到了資料也沒顯示出來的問題。所以啊,非同步問題還是得交給promise處理!

 const promiseArr = cart_coursesId.map(course => new Promise((resolve, reject) =>{    
    wx.cloud.callFunction({      
    name: 'getCart',     
    data: { id: course}    })
    .then(res =>{      
        resolve(res.result.data[0]);    
    })
  }))
    Promise      
    .all(promiseArr)      
    .then(data => {        
        console.log(data);        
        wx.hideLoading()        
        this.setData({          
            courses: data        
        })     
     }) 複製程式碼

部分頁面解析

首頁

小程式 網易雲課堂雲開發初體驗

小程式 網易雲課堂雲開發初體驗

關於主頁的課程盒子,我想用一個模板相容所有的表現效果,所以這也是坑我最多的地方。

為了相容大圖的展現,我給圖片加上了一個isBig屬性(bool)。

當isBig為false時,圖片寬度為48.7%,margin-right為 2.6%;,並通過父類的ntd-child(even)將偶數子類(處於右邊)的margin-right改為0%,形成 (48.7%+2.6%)+48.7%的結構分割。但當isBig為true,第一行就只有一張大圖,所以從第二行開始偶數子元素出現在左邊,結構變成48.7%+(48.7%+2.6%)本來在中間的間隙到了右邊,兩張圖片貼在了一起,很不美觀。所以我在每處理到一張大圖的同時,再生成一個display為none的小盒子。這樣第一行依然只顯示一張大圖,但有了兩個子元素。所以下面的行列,依然是奇數子類在左邊,偶數子類在右邊。

效果實現了,但是當我想把它封裝成元件時,天坑出現了 component不支援ntd-child選擇器,並且元件的slot也並不只是直白的整段插入。不過日後還是會重新封裝~

<view class="courses">                          
    <block wx:for="{{mainPage.sellWell.content}}" wx:key="index" >  
        <block wx:if="{{item.isBig}}">                    
            <view class="course_big"  >                      
                <navigator  url="../courseInfo/courseInfo?id={{item.id}}" >                             
                    <image class="course-image_big " src="{{item.image}}" />                         
                    <text class="course-title">{{item.title}}</text>                        
                    <text class="course-price" wx:if="{{item.price > 0}}">¥{{item.price}}</text>                     
                 </navigator>                       
            </view>                     
            <view style="display:none"></view>                  
         </block>
                  
        <block wx:else>                    
            <view class="course" >                       
                <navigator  url="../courseInfo/courseInfo?id={{item.id}}" >                              
                    <image class="course-image " src="{{item.image}}" />                        
                    <text class="course-title">{{item.title}}</text>                       
                    <text class="course-price" wx:if="{{item.price > 0}}">¥{{item.price}}</text>                      
                </navigator>                      
             </view>                   
        </block>
    </block>          
</view>複製程式碼

我的學習

小程式 網易雲課堂雲開發初體驗

小程式 網易雲課堂雲開發初體驗

雖然簡單,但這是典型的資料驅動介面效果,資料繫結,也就是MVVM。頁面通過對資料status的判斷來決定顯示對應的部分。

<ul class="status">
    <li>
      <text class="{{status == '1' ? 'active':''}}" bindtap="showStatus" data-status="1">我的課程</text>
    </li>
    <li>
      <text class="{{status == '2' ? 'active':''}}" bindtap="showStatus" data-status="2">我的微專業</text>
    </li>
 </ul>


<block wx:if="{{status == '1'}}">
</block>
<block wx:else>
</block>複製程式碼

給相應的標籤設定對應的data-status,再將修改的函式繫結到bindtap上,一個最簡單的MVVM例子就實現了。

showStatus: function(e){

    let status = e.currentTarget.dataset.status;
    this.setData({
      status:status
    })
  }複製程式碼

關於課程的獲取就是雲函式的用武之地了

onShow:function(){    
    wx.cloud.callFunction({      
        name: 'getMyCourse',    
    }).then(res=>{      
        this.setData({        
            my_courses: res.result.data      
        })    
    })  
}複製程式碼

在你需要使用雲函式的地方呼叫wx.cloud.callFunction(),把要呼叫的雲函式的函式名作為引數傳入,如此處{name: 'getMyCourse'}。接著在wxml內通過一個wx:for把資料輸出即可。此處myCourse-module為封裝好了的自定義元件。

<view wx:for="{{my_courses}}" wx:key="index">    
    <myCourse-module image="{{item.image}}" title="{{item.title}}" cid="{{item.id}}">
    </myCourse-module>
</view>複製程式碼

購物車

小程式 網易雲課堂雲開發初體驗

相關資料項有

totalPrice:0,    
selectedId:[],    
selectAllStatus: false複製程式碼

通過對列表中資料的isSelected屬性判斷,來計算總價

getTotalPrice: function(){    
    let courses = this.data.courses;    
    let total = 0;    
    let selectedId = [];    
    for(let i=0;i<courses.length;i++){      
        if(courses[i].isSelected){        
            total += courses[i].price;        
            selectedId.push(courses[i].id)      
        }          
    }
    this.setData({      
        totalPrice:total,      
        selectedId    
    })    
    console.log('[total]',total);    
    console.log('[selected]',selectedId);    
    return total;  
}複製程式碼

選中課程,還是MVVM資料繫結,且選中後重新計算總價

  selectedList:function(e){    
    const index = e.currentTarget.dataset.index;    
    const courses = this.data.courses;    
    courses[index].isSelected = !courses[index].isSelected;        
    this.setData({      
        courses    
    })      
    this.getTotalPrice();  
}複製程式碼

全選,每次進行selectAll操作,先將selectAllStatus改為!selectAllStatus,(全選=>全不選 || 全不選=>全選 ),之後將所有資料的isSelected屬性統一為selectAllStatus的當前狀態。

selectAll:function(e){    
    let courses = this.data.courses;    
    let selectAllStatus = this.data.selectAllStatus;    
    selectAllStatus = !selectAllStatus;
    courses.forEach((item, index) => {      
        item.isSelected = selectAllStatus    
    })
    this.setData({      
        courses,      
        selectAllStatus    
    })
    this.getTotalPrice();  
}複製程式碼

資料傳遞到下一個介面  將選中的陣列selectedId作為物件通過navigator傳入下一個頁面。

<block wx:if="{{totalPrice > 0}}">   
    <navigator  url="../confirm/confirm?ids={{selectedId}}">           
        <view class="pay-btn active" bindtap="confirm">去結算</view>    
    </navigator>
</block>複製程式碼

訂單

先獲取購物車傳遞的資料

let orderIds = options.ids.split(",");    
console.log(orderIds)    
this.setData({      
    userInfo : app.globalData.userInfo,      
    orderIds: orderIds    
})
複製程式碼

完成訂單後,將購買的課程存入資料庫內

wx.cloud.init() 
const db = wx.cloud.database();
const my_courses = db.collection('my_courses');複製程式碼

addMyCourse(){        
    const orders = this.data.orders;
    for(let order of orders){
      let myCourse = {        
        title:order.title,        
        image:order.image,        
        id:order.id      
    }      
    console.log(myCourse);          
    my_courses.add({data:myCourse})    
    }  
}複製程式碼

提交訂單之後 my_courses更新,相應的,開啟被購買的課程頁面也會更新,查詢當前課程是否在購買的課程中,在則將isPaid改為true。

  wx.cloud.callFunction({    
    name: 'getMyCourse',  
    }).then(res=>{        
                for(const my_course of res.result.data){          
                    if(options.id === my_course.id){            
                        let isPaid = true;            
                        this.setData({              
                            isPaid            
                        })            
                    return ;          
                }        
            }    
         })複製程式碼

<block wx:if="{{isPaid}}">  
    <view class="foot">開啟網易雲課堂APP學習 支援倍速播放<view class="arrow"></view></view>
</block>

<block wx:else>
</block>複製程式碼

小程式 網易雲課堂雲開發初體驗

小程式 網易雲課堂雲開發初體驗

兩個小工具

小程式 網易雲課堂雲開發初體驗

  • indexMock    在進行資料請求時,不要將請求寫在頁面的js裡,將其封裝出去,在需要用的頁面import即可。

let indexMock = function(url){          
    return new Promise((resolve,reject)=>{          
        wx.request({              
            url:url,              
            success(res){                  
                resolve(res.data)              
            },              
            fail(err){                  
                reject(err)              
            }          
          })      
    })
}

module.exports = {    indexMock: indexMock}複製程式碼
import { indexMock } from '../../utils/indexMock.js';
//先引入indexMock
Page({  
  data: {
        mainPage:{},    
        url:"https://www.easy-mock.com/mock/5bda9d1a58caf84108172bab/study163/mainPage",   
    },
  onLoad() {    
    const url = this.data.url;    
    indexMock(url) //indexMock返回的是一個Promise 後面用then()處理就      
        .then(res => {        
            console.log(res)        
            this.setData({          
                mainPage:res        
            })      
        })      
        .then(res=>{        
            console.log("mainpage",this.data.mainPage)      
        })  
    }  //其他函式....  
});
複製程式碼
  • viewContent    在顯示課程介紹時,遇到了一個頭疼的問題,原來介紹是html格式的,有各種<span><p><br>之類的標籤,而小程式內是wxml檔案。最初我想到的是用wxparse來解析。但此處我只需要解析輕量的文章,有點殺雞焉用牛刀的感覺。其實呢,wxparse的實現就是通過生成node陣列來轉換。所以我乾脆直接用正則寫一個轉換的陣列工具。

const viewContent = (data) => {  
    if(data && (typeof data === "string")){    
        return data.replace(/<\/?span>/g, "").replace(/<\/?p>/g, "").replace(/&nbsp;/g, " ").split("<br>");  
    }
    else{    
        return   
    }
}
module.exports = {  viewContent: viewContent}複製程式碼

資料庫中存的是html文件小程式 網易雲課堂雲開發初體驗

小程式 網易雲課堂雲開發初體驗

這裡要注意的一點是要給txt-item設定一個高度,實現空行的效果。

小程式 網易雲課堂雲開發初體驗

結語

寫專案時總是,思考不全總是遇到各種各樣的坑坑坑,算是意識到結構健壯、邏輯一致的重要性。wxml是結構,首先得保證結構的穩定性,至於樣式就是體力活了,至於資料那就是MVVM大顯身手的地方了。路漫漫其修遠兮,吾將上下而求索。

感謝閱讀,文中有錯誤的地方或者有建議歡迎提出!


專案地址:https://github.com/MarchYuanx/study163

歡迎 ☆☆☆star☆☆☆!





相關文章