wepy文檔:https://tencent.github.io/wepy/html
全局安裝wepy: npm i -g wepy-cli
vue
初始化項目: wepy init standard myproject
git
切換到項目目錄: cd myproject
github
安裝依賴: npm install
ajax
├── dist // 打包後的文件
├── src // 開發文件目錄
│ ├── components // 組件目錄
│ ├── images // 圖片文件目錄
│ ├── mixins // 通用函數
│ ├── mocks // 模擬數據目錄
│ ├── pages // 小程序單個頁面目錄
│ ├── store // redux存儲數據(頁面傳值)
│ ├── app.wpy // 入口文件(配置頁面的跳轉)
│ ├── index.html // 模版文件
├── wepy.config.js // 配置文件redis
- 使用微信開發者工具-->添加項目,項目目錄請選擇dist目錄。
- 微信開發者工具-->項目-->關閉ES6轉ES5。 重要:漏掉此項會運行報錯。
- 微信開發者工具-->項目-->關閉上傳代碼時樣式自動補全。 重要:某些狀況下漏掉此項也會運行報錯。
- 微信開發者工具-->項目-->關閉代碼壓縮上傳。 重要:開啓後,會致使真機computed, props.sync 等等屬性失效。(注:壓縮功能可以使用WePY提供的build指令代替,詳見後文相關介紹以及Demo項目根目錄中的wepy.config.js和package.json文件。)
- 本地項目根目錄運行npm run dev(wepy build --watch),開啓實時編譯。(注:若是同時在微信開發者工具-->設置-->編輯器中勾選了文件保存時自動編譯小程序,將能夠實時預覽,很是方便。)
index.template.html: 爲模塊文件算法
其中經過wepy.app
建立入口文件,wepy.page
建立頁面文件,wepy.component
建立組件文件。
app.wpy:入口文件,包含config,globalData,constructor和生命週期。在config中配置路由express
其中若是在頁面開發中須要用到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) }
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.wpy
的config
中的pages
中設置頁面的路由,在頁面中經過navigateTo跳轉到相應頁面。在wepy.page的腳本中能夠經過this.$navigate({url:""})
實現頁面的跳轉。而在wepy.component的組件中能夠經過this.$parent.$navigate({url:""})
或wepy.navigateTo
實現。npm
wepy.navigateTo({ url: '/pages/info' })
wepy中的生命週期的鉤子函數有:onLoad,onReady,onShow,onPrefetch等,其中onReady,onShow,onPrefetch
只有wepy.page中才有用。wepy.component只支持onLoad,其餘都不會觸發。json
navigateTo
跳轉的話onload
會從新執行,經過navigateBack
跳轉的話onLoad
不會從新執行)生命週期順序: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 }) }) }
官方案例:
// 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;
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實現。
當在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指向當前頁面組件。
當在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文件。新增內容以下:
// 設置一個過濾器對超過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中使用
// 錄音 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}); })