zhaidpjava 2019-07-01
wepy文档:https://tencent.github.io/wepy/
全局安装wepy: npm i -g wepy-cli
初始化项目: wepy init standard myproject
切换到项目目录: cd myproject
安装依赖: npm install
├── dist // 打包后的文件
├── src // 开发文件目录
│ ├── components // 组件目录
│ ├── images // 图片文件目录
│ ├── mixins // 通用函数
│ ├── mocks // 模拟数据目录
│ ├── pages // 小程序单个页面目录
│ ├── store // redux存储数据(页面传值)
│ ├── app.wpy // 入口文件(配置页面的跳转)
│ ├── index.html // 模版文件
├── wepy.config.js // 配置文件
index.template.html: 为模块文件
其中通过wepy.app
创建入口文件,wepy.page
创建页面文件,wepy.component
创建组件文件。app.wpy:入口文件,包含config,globalData,constructor和生命周期。在config中配置路由
// 设置路由,导航栏,底部栏 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等,以及其它自定义的方法与属性 |
页面的跳转需要先在app.wpy
的config
中的pages
中设置页面的路由,在页面中通过navigateTo跳转到相应页面。在wepy.page的脚本中可以通过this.$navigate({url:""})
实现页面的跳转。而在wepy.component的组件中可以通过this.$parent.$navigate({url:""})
或wepy.navigateTo
实现。
wepy.navigateTo({ url: '/pages/info' })
wepy中的生命周期的钩子函数有:onLoad,onReady,onShow,onPrefetch等,其中onReady,onShow,onPrefetch
只有wepy.page中才有用。wepy.component只支持onLoad,其他都不会触发。
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}); })