现如今mvvm框架如此火热,其核心思想即js逻辑层不直接操作DOM,只改变组件状态;而视图层则通过模板template进行渲染。
1.WePy项目的目录结构
├── dist 小程序运行代码目录 ├── node_modules 依赖 ├── src 代码编写的目录 | ├── components WePY组件目录 | | ├─- com_a.wpy 可复用的WePY组件a | | └── com_b.wpy 可复用的WePY组件b | ├── pages WePY页面目录(属于完整页面) | | ├── index.wpy index页面 | | └── other.wpy other页面 | └── app.wpy 小程序配置项(全局数据、样式、声明钩子等) └── package.json 项目的package配置
二、实例
小程序定义了三大实例类,App、Page、Component;
2.1 App
入口文件app.wpy声明的小程序实例继承至wepy.app类,包含小程序的生命周期函数、config配置、全局属性方法及自定义方法。
export default class extends wepy.app { config = { pages: [ //页面配置 'pages/index/index', 'pages/personals/personal', ], window: { backgroundTextStyle: 'light', navigationBarBackgroundColor: '#a22b00', navigationBarTitleText: '', navigationBarTextStyle: 'white' }, tabBar: { //tab栏 list: [ { pagePath: 'pages/index/index', text: '首页', iconPath: './static/images/icon-index2.png', selectedIconPath: './static/images/icon-index1.png' }, { pagePath: 'pages/personals/personal', text: '我的', iconPath: './static/images/icon-my2.png', selectedIconPath: './static/images/icon-my1.png' } ], color: '#000', borderStyle: 'white', selectedColor: '#ff5000', backgroundColor: '#ffffff' } }; globalData = { //全局数据 }; onLaunch() { //生命周期函数--监听小程序初始化(全局只触发一次) }; onShow() { //生命周期函数--监听小程序显示 }; customFunction() { //自定义方法 }; }
2.2 Page
page页面继承至wepy.page类,该类主要属性如下:
- config 页面配置对象,对应于原生的
page.json
文件; -
components 页面组件列表对象;
- data 页面渲染数据对象,存放可用于页面模板绑定的渲染数据;
-
methods wxml模板中事件处理函数;
- events WePy组件之间通信的函数,响应
$broadcast
、$emit
、$invoke;
- 生命周期函数 onLoad、onShow;
- 自定义方法
2.3 component
components中的组件实例继承wepy.component,本质上page也是component,所以component组件实例除了不需要config
配置以及页面特有的一些生命周期函数之外,其属性与页面属性大致相同。
三、组件间的通信
wepy.component
基类提供$broadcast
、$emit
、$invoke
三个方法用于组件之间的通信和交互。
this.$emit(funName,arg1,arg2);
组件间通信的事件处理函数需要写在组件和页面的events对象中,但是$invoke触发的事件在methods中响应:
import wepy from 'wepy' export default class Com extends wepy.component { components = {}; data = {}; methods = { //响应$invoke funNamet: (p1, p2, $event) => { //处理逻辑 } }; // events对象中所声明的函数为用于监听组件之间的通信与交互事件的事件处理函数 events = { //响应$emit、$broadcast funNamet: (p1, p2, $event) => { //处理逻辑 } }; // Other properties }
$broadcast
事件是由父组件发起,所有子组件都会收到此广播事件,除非事件被手动取消,捕获顺序。
$emit
与$broadcast
正好相反,事件发起组件的所有祖先组件会依次接收到$emit
事件,冒泡顺序。
$invoke
是一个页面或组件对另一个组件中的方法的直接调用,通过传入组件路径找到相应的组件,然后再调用其方法。
四、数据绑定
原生小程序的数据绑定方式
原生小程序通过Page
提供的setData
方法来绑定数据,如:
this.setData({title: 'this is title'});
WePY数据绑定方式
Wepy使用脏数据检查对setData进行封装,在函数执行周期结束时进行脏数据检查。一来不用关心页面多次setData是否有性能上的问题;二来可以更加简洁去修改数据实现绑定。
this.title = 'this is title';
值得注意的是,在异步绑定数据时,需要执行$apply()才会触发脏数据检查,
setTimeout(() => { this.title = 'this is title'; this.$apply(); }, 3000);
WePY脏数据检查流程
在执行脏数据检查时,会通过this.$$phase
标识当前检查状态,并且会保证在并发的流程当中,只会有一个脏数据检查流程在运行,以下是执行脏数据检查的流程图:
五、小程序登录授权获取逻辑
原生的小程序提供许多开放接口供使用者开发,快速建立小程序内的用户体系。
下面将小程序校验、登录、授权、获取用户信息诸多接口串联起来,以便更直观的认识到这些接口是如何在实际应用中使用的。
5.1 检验、登录
wx.checkSession({ success: function() { //session_key 未过期,并且在本生命周期一直有效 }, fail: function() { //session_key 已经失效,需要重新执行登录流程 wx.login({ success: (res) => { if (res.code) { //发起网络请求 wx.request({ //开发者服务器通过code换取用户唯一标识openid 和 会话密钥session_key。 url: 'https://test.com/onLogin', data: { // 临时登录凭证code,并回传到开发者服务器 code: res.code }, success: function(result) { //返回业务数据,前后端交互身份识别 } }) } else { console.log('登录失败!' + res.errMsg) } } }); } })
login说明:
-
小程序调用wx.login() 获取 临时登录凭证code ,并回传到开发者服务器。
-
开发者服务器以code换取 用户唯一标识openid 和 会话密钥session_key。
之后开发者服务器可以根据用户标识来生成自定义登录态,用于后续业务逻辑中前后端交互时识别用户身份。
会话密钥session_key有效性:
开发者如果遇到因为session_key不正确而校验签名失败或解密失败,请关注下面几个与session_key有关的注意事项。
-
wx.login()调用时,用户的session_key会被更新而致使旧session_key失效。开发者应该在明确需要重新登录时才调用wx.login(),及时通过登录凭证校验接口更新服务器存储的session_key。
-
微信不会把session_key的有效期告知开发者。我们会根据用户使用小程序的行为对session_key进行续期。用户越频繁使用小程序,session_key有效期越长。
-
开发者在session_key失效时,可以通过重新执行登录流程获取有效的session_key。使用接口wx.checkSession()可以校验session_key是否有效,从而避免小程序反复执行登录流程。
-
当开发者在实现自定义登录态时,可以考虑以session_key有效期作为自身登录态有效期,也可以实现自定义的时效性策略。
5.2 授权获取用户信息
// 可以通过 wx.getSetting 先查询一下用户是否授权了 "scope.record" 这个 scope wx.getSetting({ success(res) { if (!res.authSetting['scope.record']) { wx.authorize({ scope: 'scope.record', success() { // 用户已经同意小程序使用录音功能,后续调用 wx.startRecord 接口不会弹窗询问 wx.startRecord() } }) } } })
注意:wx.authorize({scope: "scope.userInfo"}),无法弹出授权窗口,请使用 <button open-type="getUserInfo"></button>
如果用户已经授权,要获取用户信息,调下面接口:
wx.getSetting({ success: (res)=>{ if (res.authSetting['scope.userInfo']) { // 已经授权,可以直接调用 getUserInfo 获取头像昵称 wx.getUserInfo({ withCredentials: true, success: (res) => { console.log(res); } }) } } });
注意:
- 当用户未授权过,调用该接口将直接报错
- 当用户授权过,可以使用该接口获取用户信息