微信小程序-wepy學習筆記

wepy文檔:https://tencent.github.io/wepy/html

全局安裝wepy: npm i -g wepy-clivue

初始化項目: wepy init standard myprojectgit

切換到項目目錄: cd myprojectgithub

安裝依賴: npm installajax

wepy項目目錄

├── dist // 打包後的文件
├── src // 開發文件目錄
│ ├── components // 組件目錄
│ ├── images // 圖片文件目錄
│ ├── mixins // 通用函數
│ ├── mocks // 模擬數據目錄
│ ├── pages // 小程序單個頁面目錄
│ ├── store // redux存儲數據(頁面傳值)
│ ├── app.wpy // 入口文件(配置頁面的跳轉)
│ ├── index.html // 模版文件
├── wepy.config.js // 配置文件redis

重要提醒

  1. 使用微信開發者工具-->添加項目,項目目錄請選擇dist目錄。
  2. 微信開發者工具-->項目-->關閉ES6轉ES5。 重要:漏掉此項會運行報錯。
  3. 微信開發者工具-->項目-->關閉上傳代碼時樣式自動補全。 重要:某些狀況下漏掉此項也會運行報錯。
  4. 微信開發者工具-->項目-->關閉代碼壓縮上傳。 重要:開啓後,會致使真機computed, props.sync 等等屬性失效。(注:壓縮功能可以使用WePY提供的build指令代替,詳見後文相關介紹以及Demo項目根目錄中的wepy.config.js和package.json文件。)
  5. 本地項目根目錄運行npm run dev(wepy build --watch),開啓實時編譯。(注:若是同時在微信開發者工具-->設置-->編輯器中勾選了文件保存時自動編譯小程序,將能夠實時預覽,很是方便。)

開發介紹

index.template.html: 爲模塊文件算法

其中經過 wepy.app建立入口文件, wepy.page建立頁面文件, wepy.component建立組件文件。

app.wpy:入口文件,包含config,globalData,constructor和生命週期。在config中配置路由express

  1. config:配置pages,window,tabBar。分別爲頁面路由配置,頁面導航欄配置,頁面底部欄配置。詳細配置在小程序框架配置
  2. globalData: 全局參數配置,用於多個頁面的公用參數。
  3. constructor: 攔截器的配置。
  4. 生命週期使用
其中若是在頁面開發中須要用到async/await的話,須要在app.wpy中使用import 'wepy-async-function'加載模塊,否則在編譯後頁面會報錯,致使async/await沒法使用。
// 設置路由,導航欄,底部欄
config = {
    pages: [
        'pages/main',
        'pages/admin-center'
    ],
    window: {
        backgroundTextStyle: 'light',
        navigationBarBackgroundColor: '#fff',
        navigationBarTitleText: 'WeChat',
        navigationBarTextStyle: 'black'
    },
    tabBar = {
        color: '#AEADAD',
        selectedColor: '#049BFF',
        backgroundColor: '#fff',
        borderStyle: 'black',
        list: [{
            pagePath: 'pages/index',
            text: '首頁',
            "iconPath": "images/ico-home.png",
            "selectedIconPath": "images/ico-home-d.png"
        }, {
            pagePath: 'pages/admin-center',
            text: '借閱',
            "iconPath": "images/ico-setting.png",
            "selectedIconPath": "images/ico-setting-d.png"
        }]
    }
}
// 全局參數(方便後期各頁面中的使用)
globalData = {
    prizeList: [],                  // 領取的獎品列表
}
// 設置攔截器,intercept爲攔截器函數
constructor () {
    super()
    intercept(this)
}
// 頁面加載
onLaunch(res) {
    console.log(res)
}

wpy模塊中的屬性

export default class Index @extends wepy.page{
    customData = {}  // 自定義數據

    customFunction () {}  //自定義方法

    onLoad () {}  // 在Page和Component共用的生命週期函數

    onShow () {}  // 只在Page中存在的頁面生命週期函數

    config = {};  // 只在Page實例中存在的配置數據,對應於原生的page.json文件

    data = {};  // 頁面所需數據均需在這裏聲明,可用於模板數據綁定

    components = {};  // 聲明頁面中所引用的組件,或聲明組件中所引用的子組件

    mixins = [];  // 聲明頁面所引用的Mixin實例

    computed = {};  // 聲明計算屬性(詳見後文介紹)

    watch = {};  // 聲明數據watcher(詳見後文介紹)

    methods = {};  // 聲明頁面wxml中標籤的事件處理函數。注意,此處只用於聲明頁面wxml中標籤的bind、catch事件,自定義方法需以自定義方法的方式聲明

    events = {};  // 聲明組件之間的事件處理函數
}
屬性 說明
config 頁面配置對象,對應於原生的page.json文件,相似於app.wpy中的config
components 頁面組件列表對象,聲明頁面所引入的組件列表
data 頁面渲染數據對象,存放可用於頁面模板綁定的渲染數據
methods wxml事件處理函數對象,存放響應wxml中所捕獲到的事件的函數,如bindtap、bindchange,不能用於聲明自定義方法。
events WePY組件事件處理函數對象,存放響應組件之間經過$broadcast、$emit、$invoke所傳遞的事件的函數
其它 小程序頁面生命週期函數,如onLoad、onReady等,以及其它自定義的方法與屬性
wpy中自定義的函數應當寫在與methods平級的位置,不用寫在methods中。

頁面的跳轉

頁面的跳轉須要先在app.wpyconfig中的pages中設置頁面的路由,在頁面中經過navigateTo跳轉到相應頁面。在wepy.page的腳本中能夠經過this.$navigate({url:""})實現頁面的跳轉。而在wepy.component的組件中能夠經過this.$parent.$navigate({url:""})wepy.navigateTo實現。npm

wepy.navigateTo({
  url: '/pages/info'
})

wepy中的生命週期

wepy中的生命週期的鉤子函數有:onLoad,onReady,onShow,onPrefetch等,其中onReady,onShow,onPrefetch只有wepy.page中才有用。wepy.component只支持onLoad,其餘都不會觸發。json

  1. onLoad: 頁面加載完成時調用,一個頁面只會調用一次。(在路由跳轉的時候經過navigateTo跳轉的話onload會從新執行,經過navigateBack跳轉的話onLoad不會從新執行)
  2. onShow:頁面顯示的時候調用。
  3. onReady: 頁面中的全部資源加載完成時調用。
  4. onPrefetch:在頁面跳轉時觸發,用於預加載和預查詢數據。
  5. onUnload: 在頁面卸載時觸發(經過redirectTo,switchTab,navigateBack,reLaunch會觸發當前頁面中的onUnload,但navigateTo不會觸發)。

生命週期順序:onPrefetch > onLoad > onShow > onReady。

onPrefetch這個生命週期是wepy中擴展的,它的觸發須要經過 this.$navigate及其餘wepy封裝的跳轉方式才能實現。當設置 onPrefetch後,能夠在 onLoad中設置變量來獲取 onPrefetch中返回的值。

案例:

onLoad (params, data) {
  data.prefetch.then((list) => {
    this.adminMath = list.chasucccnt.data.succcnt
    this.recordInfo = list.adminCenter.data.challengeRecList
    this.heightScore = list.adminCenter.data.hs
    this.hadMath = list.adminCenter.data.cc
    this.$apply()
  })
}
// chasucccnt,getAdminCenter爲請求後臺的函數,返回的數據結構是{data:{succcnt:0}。
async onPrefetch () {
  let chasucccnt = await this.chasucccnt()
  let adminCenter = await this.getAdminCenter()
  return new Promise((resolve, reject) => {
    resolve({
      chasucccnt: chasucccnt,
      adminCenter: adminCenter
    })
  })
}

props實現父子組件之間的傳值

官方案例:

// parent.wpy
<child :title = 'parentTitle' :syncTitle.sync = 'parentTitle' :twoWayTitle = 'parentTitle'></child>
在script中的設置
data = {
    parentTitle: 'p-title'
}

// child.wpy
props = {
    // 靜態傳值
    title: String,

    // 父向子單向動態傳值
    syncTitle: {
        type: String,
        default: 'null'
    },

    twoWayTitle: {
        type: Number,
        default: 'nothing',
        twoWay: true
    }
};

onLoad () {
    console.log(this.title); // p-title
    console.log(this.syncTitle); // p-title
    console.log(this.twoWayTitle); // p-title

    this.title = 'c-title';
    console.log(this.$parent.parentTitle); // p-title.
    this.twoWayTitle = 'two-way-title';
    this.$apply();
    console.log(this.$parent.parentTitle); // two-way-title.  --- twoWay爲true時,子組件props中的屬性值改變時,會同時改變父組件對應的值
    this.$parent.parentTitle = 'p-title-changed';
    this.$parent.$apply();
    console.log(this.title); // 'c-title';
    console.log(this.syncTitle); // 'p-title-changed' --- 有.sync修飾符的props屬性值,當在父組件中改變時,會同時改變子組件對應的值。
}
有上案例能夠知道 :title爲靜態傳值,即只有第一次有效,後面改變值後子組件中的title不會發生改變,當在屬性後添加.sync後,即該屬性發生改變會致使子組件中相應的值發生改變,當在子組件中的 props中設置 twoWay: true後,能夠實現父子組件的雙向綁定。

組件之間的數據通訊

wepy中的通訊主要採用三種方法:$broadcast, $emit, $invoke;

  1. $broadcast:父組件觸發全部子組件(包含子孫級組件)事件。
  2. $emit:子組件觸發全部父組件(包含祖先級組件)事件。當在父組件屬性中使用.user設置自定義事件後,$emit能夠用於觸發自定義事件而events中聲明的函數將不會再執行。(與vue中的用法不一樣,vue中須要在父組件中設置子組件的屬性,用於在子組件中觸發。而wepy中則不須要,只要在events中設置方法就能夠在子組件中觸發。)
  3. $invoke:頁面或子組件觸發另外一個子組件事件。
parent.wpy
<template>
    <view>
        <children @childFun.user = 'someEvent'></children>
    </view>
</template>
<script>
export default class Parent extends wepy.page{
    data = {
        name: 'parent'
    }
    events = {
        'some-event': (p1, p2, p3, $event) => {
            //  輸出爲'parent receive some-event children',$event.source指向子組件。
            console.log(`${this.name} receive ${$event.name} from ${$event.source.name}`)
        }
    }
    onLoad () {
            this.$broadcast('getIndex', 1, 4)
    }
    methods = {
        someEvent (...p) {
            // 輸出[1, 2, 3, _class]。
            console.log(p)
        }
    }
}
</script>

children.wpy
<script>
export default class Parent extends wepy.page{
    data = {
        name: 'children'
    }
    onLoad () {
        // this.$emit('some-event', 1, 2, 3)
        // 觸發組件中的自定義事件
        this.$emit('childFun', 1, 2, 3)
    }
    events = {
        'getIndex': (...p) => {
            console.log(p)        // 輸出[1, 4]
        }
    }
}
在父組件中給子組件添加屬性@childFun.user = 'someEvent'後,在子組件中修改觸發條件this.$emit('childFun', 1, 2, 3)


//$invoke
父組件向子組件發送事件:
使用import導入子組件後,在使用時能夠直接經過
this.$invoke('子組件,必需要單引號括起來', '子組件方法名稱',  param1,param2,param3.......);
子組件間發送事件:
this.$invoke('子組件的相對路徑', '子組件方法名稱',  param1,param2,param3.......);
子組件的相對路徑的理解: 當設置'./'即當前組件,'../'爲父組件,以此類推。它能夠指定向哪個組件分發內容,但只適用於簡單的組件樹結構,複雜的結構考慮使用redux。
在子組件中使用$emit會觸發父組件及祖先組件中的相同事件。在父組件中使用$broadcast會觸發子組件及子孫組件中的相同事件。其中$emit和$broadcast觸發的事件設置在組件中的events中,而$invoke觸發的函數與events是平級的,當在組件中已經經過components導入組件的話,$invoke中的第一個參數能夠直接設置爲components中的值,當設置相對路徑時,即根據當前組件在整個組件樹中的位置來肯定路徑,它能夠指定向特定的組件傳值,但當結構複雜時,須要嵌套的路徑會比較複雜,很差維護,考慮用redux實現。

Mixins混合

  1. 默認式混合(模塊中的data數據,components,events,自定義事件)

當在wepy.mixin中設置了和頁面中相同的函數或變量時,以當前頁面的函數和變量爲主,mixin中的函數和變量會失效。官方案例:

// mixin.js
export default class TestMixin extends wepy.mixin {
    data = {
        foo: 'foo defined by page',
        bar: 'bar defined by testMix'
    };
    methods: {
    tap () {
      console.log('mix tap');
    }
  }
}
....
import wepy from 'wepy';
import TestMixin from './mixins/test';
export default class Index extends wepy.page {
    data = {
        foo: 'foo defined by index'
    };
    mixins = [TestMixin ];
    onShow() {
        console.log(this.foo); // foo defined by index
        console.log(this.bar); // bar defined by testMix
    }
}
在mixin中申明的函數能夠調用引入mixin後的頁面的data數據和函數。mixin中的this指向當前頁面組件。
  1. 兼容式混合(生命週期)

當在mixin中設置生命週期一類的鉤子函數時,會優先執行mixin中的生命週期,再執行頁面中的函數。

// mixin.js
export default class TestMixin extends wepy.mixin {
    onLoad () {
        console.log(2222)
    }
}
....
import wepy from 'wepy';
import TestMixin from './mixins/test';
export default class Index extends wepy.page {
    data = {
        foo: 'foo defined by index'
    };
    mixins = [TestMixin ];
    onLoad() {
        console.log(11111); 
    }
}
結果打印爲: 
2222
11111

wxs的使用,實現過濾器

在項目目錄下新建wxs文件夾,在該文件夾中新增wxs文件。新增內容以下:

// 設置一個過濾器對超過10000的數字進行轉化
module.exports = {
  filter: function (num) {
    if (num < 10000) {
      return num
    } else {
      var reNum = (num / 10000).toFixed(1) 
      return reNum + '萬'
    }
  }
}

在頁面中經過import導入該過濾器:

// template中使用過濾器,mywxs對應下方wxs中設置的key值
<view>{{mywxs.filter(mItem.playerCount)}}人</view>
.....
import mywxs from '@/wxs/fixed.wxs'
export default class Index extends wepy.page{
......
wxs = {
    mywxs: mywxs
}
.....
}
導入的wxs文件只能在template中使用,不能在js中使用

Promise和async/await的使用(髒數據的檢測)

// 錄音
async endVideo (event) {
  let endTime = event.timeStamp
  let startTime = this.startTime
  this.videoInfo = '2131'
  let video = await new Promise((resolve, reject) => {
    recorderManager.onStop((res) => {
      console.log('recorder stop', res)
      const { tempFilePath } = res
      resolve(tempFilePath)
    })
    recorderManager.stop()
  })
  if ((endTime - startTime) > 1000) {
    this.videoInfo = video
    this.$apply()
  }
}
當使用異步函數的時候,咱們若是須要從新渲染頁面的時候,須要手動調用$apply()方法,才能觸發髒數據檢查流程的運行。

羣轉發

用於實現微信小程序的分享功能,獲取每一個羣的獨立ID。在上彈窗中默認有轉發按鈕,能夠經過使用hideShareMenu來隱藏彈窗中的轉發按鈕.

用法
在wpy類型文件的onload中設置
// 用於顯示分享的羣列表
wepy.showShareMenu({
    withShareTicket: true
})

在onShareAppMessage中設置分享的界面,在分享成功後的回調函數中經過wepy.getShareInfo獲取分享羣的信息。羣的信息是通過加密的,須要經過後臺來進行解密操做。
onShareAppMessage (res) {
    if (res.from === 'button') {
        console.log(res.target)
    }
    return {
        title: '自定義轉發標題',
        path: '/pages/main',
        success: function(res) {
            let shareId = res.shareTickets[0]
            // 轉發成功
            wepy.getShareInfo({
                shareTicket: shareId,
                success: (data) => {
                  var appId = '小程序的appID'
                  var encryptedData = data.encryptedData
                  var iv = data.iv
                  wepy.request({
                    url: 'http://localhost:3000/api/decode',
                    method: 'post',
                    data: {
                      appId: appId,
                      encryptedData: encryptedData,
                      iv: iv
                    },
                    success: (info) => {
                      console.log('info:' + info)
                    },
                    fail: (info) => {
                      console.log(info)
                    }
                  })
                  console.log(data)
                },
                fail: (data) => {
                  console.log(data)
                }
            })
            console.log(res)
        },
        fail: function(res) {
            // 轉發失敗
            console.log(res)
        }
    }
}
其中onShareAppMessage須要在wepy.page中設置纔有效果,在wepy.component中設置無效果。在onShareAppMessage中的path設置的參數能夠跟隨 ?ie=值,而後在對應的頁面中設置 onLoad來獲取跟隨的值

微信小程序和本地接口對接,模擬請求數據

後臺採用express,代碼以下:

var express = require('express');
var app = express();
app.listen(3000, function () {
    console.log('listen:3000');
})

設置先後臺的對接接口:

var router = express.Router();
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
router.get('/info', function (req, res) {
    console.log(req.query);
    console.log(req.body);
}
app.use('/api', router)     // 前臺經過ajax向'/api/info'發送請求

加載請求模塊(由於前臺沒法直接向https://api.weixin.qq.com/sns...,該域名沒法添加到小程序的服務器域名中):

var request = require('request');
// 在'/info'配置的接口中使用,獲取session_key。
request.get({
    uri: 'https://api.weixin.qq.com/sns/jscode2session',
    json: true,
    qs: {
        grant_type: 'authorization_code',
        appid: '小程序的appID',
        secret: '小程序的密鑰',
        js_code: code
    }
}, (err, response, data) => {
    if (response.statusCode === 200) {
        console.log("[openid]", data.openid)
        console.log("[session_key]", data.session_key)
        session_key = data.session_key
        //TODO: 生成一個惟一字符串sessionid做爲鍵,將openid和session_key做爲值,存入redis,超時時間設置爲2小時
        //僞代碼: redisStore.set(sessionid, openid + session_key, 7200)
        res.json({
            openid: data.openid,
            session_key: data.session_key
        });
    } else {
        console.log("[error]", err)
        res.json(err)
    }
})

解密

在官方案例中下載解密須要的文件WXBizDataCrypt.js。
從案例中知道須要的參數有4個:

1. AppID(微信小程序的id,在微信小程序的後臺設置中有),

2. session_key(用戶登陸的時候能夠獲取到),

3. encryptedData(須要解密的字符串,在獲取的羣信息中加密的字段),

4. iv(算法初始向量,在獲取的羣信息中有返回)。

在app.js中引入WXBizDataCrypt.js:

// 解密模塊
var WXBizDataCrypt = require('./WXBizDataCrypt')

// 解密數據
router.post('/decode', function (req, res) {
    var appId = req.body.appId;
    var sessionKey = session_key;
    var encryptedData = req.body.encryptedData;
    var iv = req.body.iv;
    var pc = new WXBizDataCrypt(appId, sessionKey);
    var data = pc.decryptData(encryptedData , iv);
    res.json({data: data});
})
相關文章
相關標籤/搜索