写在前面
- 上一篇讲了小程序一个页面文件构成
- 本篇主要是模块化以及网络访问(http&websocket)
- 还有一点点自己的异步解决
网络访问
小程序API提供了一套封装好了的网络框架
对!就是不用封装了
每次android换一次网络框架真的是 一言难尽
HTTP
这里挑一个使用频率较高的 wx.request(OBJECT) 来说
参数OBJECT实际上是一个JSON格式的数据里面除了url以外其他都是非必填参数
method -> GET
dataType -> json
responseType -> text
值得注意的一点是也是我在网上查的时候发现很多人问的问题
啰嗦一句 小程序的 header 其实也是有默认值的
{
'content-type': 'application/json' // 默认值
}
所以当传输为Form表单时记得修改header
PS.因为wx这个API还是留了些参数让开发者自定义,建议进行简单的二次封装并模块化方便复用
WebSocket
小程序这次是第一次使用websocket所以对于我还是挺新鲜的会详细总结一下 WS其实在okhttp上也有封装 只是安卓碰到的大多是标准的 客户端 -> 服务器 场景 遇到 客户端 <=> 服务器 轮询似乎成了唯一方法 但是小程序内部封装了WS协议的访问方法 而且使用起来 ** 十分简洁 **
简单介绍
WS的从服务器到小程序机制类似于安卓广播
只不过这个广播没有特定的目标
只要接入了服务器WS API 的所有用户都可以在同时收到来自服务器的广播
所以这个对于服务器是有一定压力的 (但是相比轮询来说已经好很多了)
然而客户端一般只需要对广播进行判断就可以进行相应操作
比如 访问之前需要轮询的HTTP接口
TIPS
WS看上去解决了很多问题 但是WS的保活就显得十分重要
加上小程序看起来 乱七八糟 的声明周期有时候WS被莫名奇妙关闭就会显得很麻烦
下面是一些注意事项
第一点
onHide 和 onUnload 并不会自动关闭WS
但是WS对于服务器还是有一定的压力的建议在onUnload时关掉WS
第二点
右上角的关闭键会销毁 WS 但不会直接马上销毁
dei dei dei 会销毁但不会马上销毁
最重要的是 重新进来小程序时不会从onLoad执行 !
而且在小程序的API中并没有提供方法进行WS状态的判定方法
建议在APP.js
中的globalData
里面自己写一个WS的状态值
并且在wx.onSocketClose
和 wx.onSocketOpen
里面处理一下
异步
异步对于小程序网络访问是绝对让人头大的一个问题 而且微信的官方代码里给了一个并不太漂亮的解决方法 让我这个从Android开发者一开始上手遇到了不少很麻烦的问题
WHY 异步
JS其实确实是单线程应用所以它无法像JAVA一样去直接操控线程来达到async同步
这里再啰嗦一句 JS单线程为什么存在异步
JS运行下的运行环境如
浏览器或者node
会在一些耗时任务给予其开辟另外的线程去执行耗时任务
然后通过类似于Android中MessageQueue的机制把额外线程执行完后的事件
循环按照顺序取出并执行 以此来实现异步
而在小程序开发时一定会遇到的问题就是
用户数据读取和自己服务器更新这些数据的先后关系
在小程序默认index.js
和 app.js
其实官方使用接口回调举了一个栗子
首先我们先来分析一下接口回调情况下怎么解决这个问题
这个栗子所解决当的问题在于 APP 和 Page 的加载时出现的异步
下面的Main.js指正常情况下第一个被加载出来的页面
也就是说App.js
中网络访问和 Main.js
中 onLoad
是不确定谁先完成
那么就需要一个方法来解决异步
即当 wx.getSetting
执行完之后通知Main.js
去刷新代码
先看一下官方的代码
App.js
// 获取用户信息
wx.getSetting({
success: res => {
if (res.authSetting['scope.userInfo']) {
// 已经授权,可以直接调用 getUserInfo 获取头像昵称,不会弹框
wx.getUserInfo({
success: res => {
// 可以将 res 发送给后台解码出 unionId
this.globalData.userInfo = res.userInfo
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
if (this.userInfoReadyCallback) {
this.userInfoReadyCallback(res)
}
}
})
}
}
})
复制代码
Main.js 即官方demo里的index.js
if (app.globalData.userInfo) {
this.setData({
userInfo: app.globalData.userInfo,
hasUserInfo: true
})
} else if (this.data.canIUse){
// 由于 getUserInfo 是网络请求,可能会在 Page.onLoad 之后才返回
// 所以此处加入 callback 以防止这种情况
app.userInfoReadyCallback = res => {
this.setData({
userInfo: res.userInfo,
hasUserInfo: true
})
}
}
复制代码
首先看App.js
获取授权之后执行真正获取信息的 wx.getUserInfo
成功以后把值赋给 .globalData.userInfo
然后会去判断一个Callback是否存在存在即执行
把这个Callback先放一边看下一段
在Main.js
中如果.globalData.userInfo
存在那么会直接 setData
这种即 Main.onLoad()
之前就已经获得了data那么不需要处理异步
直接加载即可
继续往下看 先忽略这个if条件不是我们要讨论的对象
然后这里对 app.userInfoReadyCallback
进行了一个定义 然后内容是 setData
那么就明白了 App.js
本来就有一个空值
如果onLoad
在wx.getUserInfo
之前完成
即.globalData.userInfo
不存在为空那么会去定义回调函数
等wx.getUserInfo
里的success执行过程中判定到app.userInfoReadyCallback
不为空
也就是网络访问慢于页面加载是会对回调函数进行执行 解决异步问题
OK 我们搞清楚了这种回调 但是当有很多异步需要管理的时候
嵌套就会变得很麻烦 但是JS有一个类似于RxJAVA的函数 Promise()
而且是小程序本来就支持的
其实JS还有ES6语法async/waite
但是IDE并不能直接转码还需要加入第三方工具进行转码 并不划算 *
话不多说 举个栗子 Main.js
app.promisLogin()//token
.then(that.promiseUpdateInfo)//刷新页面
.catch(function (res) {
wx.hideLoading()
wx.showToast({
title: '网络错误',
icon: 'none'
})
console.log('初始化错误 '+res);
});
复制代码
这几个函数体本身
promisLogin : function(){
return new Promise(function(resolve,reject){
wx.login({
success: res => {
// 发送 res.code 到后台换取 openId, sessionKey, unionId
if (res.code) {
//发起网络请求
wxCode=res.code
console.log(res.code)
// this.apiLogin(res)
resolve()
} else {
console.log('获取用户登录态失败!' + res.errMsg)
}
}
})
})
},
复制代码
重点在于resolve()
和 reject()
当方法执行到这两个函数时开始执行下一个步骤
这样的链式调用明显使得异步逻辑更清晰了
模块化
模块化几乎是所有开发过程中都需要用到
特别是碰到网络访问之后一个api的复用所以这里放在一起总结
JS的模块化和JAVA类似 通过 * require * 来进行声明
var api = require('../../utils/api.js')
复制代码
但是由于JS没有 * private protected public * 这些字段来约束可见性
所以在模块的最后需要通过 * module.exports * 字段来声明那些方法可见
多个方法
module.exports = {
getUserInfo: getUserInfo,
getGroupInfo: getGroupInfo,
}
复制代码
单个方法
module.exports.getUserInfo = getUserInfo
复制代码