微信小程序知识总结
微信小程序的优势和劣势
优势:
- 容易推广,入口众多
- 使用便捷,
- 体验良好,有接近原生app的体验,H5页面经常出现延迟,卡顿,加载慢,权限不足等,而这些在小程序中不会出现
- 成本低,
劣势:
- 单个包大小限制为2M,无法开发大型应用,分包之后最大值是20M
- 上架需要审核,比较麻烦
- 会受到微信限制,比如不能直接分享到朋友圈,涉及到积分和虚拟交易时也是不允许的
在小程序中,规定所有手机的屏幕宽度都是750rpx。
改变数据:
this.setData({
msg:’’
})
h5 自定义属性 用于在事件中传递参数
通过 data- 的形式定义变量 多个单词用 - 连接 他会自动转化为驼峰命名法 data-article-id
可以通过e.currentTarget.dataset.变量名的方式拿到数据
小程序页面跳转
navigateTo 跳转到非tabBar页面
页面传参 通过模板字符串在路径中通过?拼接变量
控制显示与隐藏
<view wx:if="{{length > 5}}"> 1 </view>
<view wx:elif="{{length > 2}}"> 2 </view>
<view wx:else> 3 </view>
<block wx:if="{{true}}">
<view> view1 </view>
<view> view2 </view>
</block>
通过hidden控制显示与隐藏
<view hidden="{{condition}}">
隐藏
</view>
小程序中的js和浏览器中和node中的区别
浏览器中JS:
- ES
- DOM
- BOM
Node中的JS:
- ES
- NPM
- Native
小程序中的JS:
- ES
- 小程序框架
- 小程序API
小程序中的数据渲染
1.渲染层和数据相关。
2.逻辑层负责产生、处理数据。
3.逻辑层通过 Page 实例的 setData 方法传递数据到渲染层。
小程序中通讯模型
小程序的渲染层和逻辑层分别由2个线程管理:
渲染层的界面使用了WebView 进行渲染;
逻辑层采用JsCore线程运行JS脚本。
一个小程序存在多个界面,所以渲染层存在多个WebView线程,这两个线程的通信会经由微信客户端(下文中也会采用Native来代指微信客户端)做中转,逻辑层发送网络请求也经由Native转发。
// app 中的生命周期
let app = getApp() 来获取app实例
- onLaunch: 当小程序初始化完成时,会触发 onLaunch(全局只触发一次)
- onShow:当小程序启动,或从后台进入前台显示,会触发 onShow
- onHide:当小程序从前台进入后台,会触发 onHide
小程序的界面跳转
-
navigateTo 通过问号拼接参数 只能跳转到非tabBar页面
-
wx.redirectTo ,关闭卸载页面,只能跳转非tabBar页面
-
wx.switchTab 关闭所有非tabBar页面,只能跳转到tabBar页面
-
wx.reLaunch 关闭卸载所有页面,可以跳转到任意页面,没有返回键
-
wx.navigateBack
wx.navigateBack({
delta: 2 //返回的页面数,如果 delta 大于现有页面数,则返回到首页。
})
路由跳转传参
路由跳转传参可以通过?的方式拼接参数。跳转到指定界面之后,可以在该页面的onLoad方法中的options参数(本身是个对象)拿到路由跳转的参数(wx.switchTab()方法不支持传参)。
微信中发送请求
wx.request({
url: 'https://showme2.myhope365.com/api/cms/article/open/list',
method: "POST",
data: {
pageNum: 1,
pageSize: 10
},
header: {
"content-type": "application/x-www-form-urlencoded"
},
success: res => {
console.log(res.data.rows)
}
})
微信中封装请求
// 封装http请求
function request(options) {
// 请求拦截器
// ...
// 1. 加一些统一的参数,或者配置
if (!options.url.startsWith("https://") && !options.url.startsWith("http://")) {
options.url = "https://showme2.myhope365.com" + options.url
}
// 默认的请求头
let header = {
"content-type": "application/x-www-form-urlencoded",
// 加上统一的cookie 如果没有则为空
"cookie": wx.getStorageSync("cookie") || ""
};
if (options.header) {
header = {
...header,
...options.header
}
}
return new Promise((reslove, reject) => {
// 调用接口
wx.request({
// 默认的配置
// 加载传入的配置
...options,
header,
success(res) {
// 响应拦截器,所有接口获取数据之前,都会先执行这里
// 1. 统一的错误处理
if (res.statusCode != 200) {
wx.showToast({
title: '服务器异常,请联系管理员',
})
}
reslove(res)
},
fail(err) {
reject(err)
}
})
})
}
export function get(url, options = {}) {
return request({
url,
...options
})
}
export function post(url, data, options = {}) {
return request({
url,
data,
method: "POST",
...options
})
}
微信中登录功能,以及保持登录状态
后端采用的登录鉴权方式是通过cookie的方式进行的鉴权,即登录成功之后,后端会给我们cookie上增加一个JSESSIONID,这个JESSIONID就标识了当前登录用户的身份。
在浏览器中,我们每次发送请求都会携带cookie,所以说在浏览器中我们登录成功之后就可以直接调用登录之后才能访问的接口。但是在小程序端,小程序默认不会帮我们在发送请求的时候带上cookie,这个时候就需要我们手动添加请求cookie了。
小程序的开放能力
获取网络状态
wx.getNetworkType({
success(res){
// wifi/2g/3g/4g/unknown(Android下不常见的网络类型)/none(无网络)
}
})
从网络上下载pdf文档
wx.downloadFile({
url:'',
success(res){
wx.openDocument({
filePath:filePath: res.tempFilePath
})
}
})
下载成功之后预览文档
wx.openDocument({
filePath:filePath: res.tempFilePath
})
调用微信扫一扫
wx.scanCode({
success(res){
}
})
获取当前用户信息
wx.getUserProfile
获取图片的宽高
wx.getImageInfo({
src:'',
success(res){
}
})
小程序分享
onShareAppMessage这个函数 ,在页面中就可以点击右上角分享
如果想要在页面的分享按钮中分享就可以设置button 的open-type为share,这样我们就可以页面中添加分享按钮
分享朋友圈
onShareTimeline
父子组件通信
父传子
在标签中定义属性
子组件在properties中接收
设置类型type ,默认值使用的是value而不是default
子传父
在父组件中注册事件 子组件通过
this.triggerEvent("事件名",数据)
behaviors 类似vue中的mixin
module.exports = Behavior({
behaviors: [],
properties: {
myBehaviorProperty: {
type: String
}
},
data: {
myBehaviorData: {}
},
attached: function(){},
methods: {
myBehaviorMethod: function(){}
}})
组件样式隔离
Component({
options: {
styleIsolation: 'isolated' // 开启样式隔离 页面不会影响组件
styleIsolation: 'apply-shared' // 页面会影响组件,组件不会影响页面
styleIsolation: 'shared' // 页面和组件互相影响
addGlobalClass: true // 和apply-shared等价,但设置了 styleIsolation 选项后这个选项会失效。
styleIsolation: 'isolated' // 开启样式隔离
styleIsolation: 'isolated' // 开启样式隔离
}
})
webSocket
为什么要用WebSocket
为什么我们有了http请求之后,还要用WebSocket呢?
http只能是单向发起的,只能由客户端主动发起,服务端被动响应。服务端无法主动向客户端发送消息,如果想实现类似于聊天室这种即时通讯的功能,就需要使用消息轮询。轮询的效率低,比较消耗资源。(每次建立http连接都进行三次握手,并且每次请求都需要携带请求头),开发人员为了解决这个问题,发明了webSocket
特点
它的最大特点就是,服务器可以主动向客户端推送信息,客户端也可以主动向服务器发送信息,是真正的双向平等对话,属于服务器推送技术的一种。
websocket建立连接时,数据是通过http传输的,建立连接后就不需要http协议了。
websocket建立连接后就是全双工模式,也是基于tcp协议。
建立连接之后,不必在浏览器(客户端)发送request之后服务器才能发送信息到浏览器,这时候服务器有主动权,可以随时发消息给浏览器(客户端)。
发送的信息中不必带有head部分信息了,相对于http来说,降低了服务器的压力,极大的减少了不必要的网络流量与延迟。
没有同源限制,客户端可以与任意服务器通信
协议标识符是ws(如果加密,则为wss),服务器网址就是 URL。
回执消息
command == 11
接收到聊天信息
command === 18
获取用户信息响应处理;
command === 12 && code === 10000
发送消息成功
command === 9
加入群组的消息通知处理
command === 10
退出群组的消息通知处理;
command === 20 && code === 10015
获取消息失败,未开启持久化处理 聊天室…
处理离线消息; (离线消息提示 )
command === 20 && code === 10016
处理离线消息; (暂时不用处理)
command === 20 && code === 10018
处理历史消息
connectSocket 连接socket
onSocketOpen 监听连接建立成功
sendSocketMessage 发送消息和获取历史消息和心跳检测
onSocketMessage 接收服务端的消息
uploadFile 图片上传
// pages/chatroomDetail/chatroomDetail.js
import {
uploadFile
} from "../../utils/api"
const app = getApp()
// pages/chat/index.js
Page({
/**
* 页面的初始数据
*/
data: {
loadingSocket: true,
value: "",
list: [],
userName: ""
},
/**
* 生命周期函数--监听页面加载
*/
onLoad: function (options) {
app.globalData.loginPromise.then(()=>{
console.log(app.globalData);
console.log(app.globalData.isLogin);
if (app.globalData.isLogin) {
const userInfo = app.globalData.userinfo
console.log(app.globalData.userinfo);
// 准备数据
this.userName = userInfo.loginName;
this.setData({
userName: this.userName
})
// 随便输入
// courseId 分组id 通过这个courseId来标识不同的聊天室
this.groupId = "web07" + options.id
// nickName 昵称
this.nickName = userInfo.userName
// 头像
this.avatar = userInfo.avatar
// 登录
this.connectSocket();
// 监听链接打开
this.onSocketOpen()
// 接受服务器消息
this.onSocketMessage()
} else {
// 未登录
wx.reLaunch({
url: '../mine/mine',
})
}
})
},
connectSocket() {
const url = `wss://showme2.myhope365.com/websocketChat?username=${this.userName}&password=123&courseId=${this.groupId}&nickName=${this.nickName}&avatar=${this.avatar}`
// 建立链接
wx.connectSocket({
// 要链接的socket服务器的地址
url,
})
},
onSocketOpen() {
// 监听链接建立成功
wx.onSocketOpen((result) => {
// 当我们socket链接打开之后执行
// 需要保证的时候,我们在发送消息之前一定要先链接成功
// console.debug("socket链接已经打开了");
this.setData({
loadingSocket: false
})
// 链接打开之后加载历史消息
this.getHistory()
// 添加心跳检测
this.intervalId = setInterval(() => {
wx.sendSocketMessage({
data: JSON.stringify({
"cmd":13, // 固定参数
"hbbyte":"-127" // 固定参数
}),
})
}, 5000);
})
},
onSocketMessage() {
// 接受服务端的消息
wx.onSocketMessage((result) => {
const data = JSON.parse(result.data)
console.debug(data);
// 针对不同类型的消息进行一些处理
if (data.command === 11) {
// 有新消息
this.data.list.push(data.data)
this.setData({
list: this.data.list
})
} else if (data.command === 20 && data.code === 10018) {
// 服务端返回了历史消息
this.setData({
list: data.data.groups[this.groupId]
})
}
})
},
getHistory() {
const historyBody = {
cmd: 19, // 命令
type: 1, // 类型 固定值
groupId: this.groupId, // 分组的id
userId: this.userName // 用户id(这里可以用loginName)
}
// 接受字符串类型
wx.sendSocketMessage({
data: JSON.stringify(historyBody),
})
},
// 发送消息
sendSocketMsg(content, type) {
const msgBody = {
from: this.userName, // 发送人,当前用户的用户名
createTime: new Date().getTime(), // 发送时间
cmd: 11, // 命令固定内容
group_id: this.groupId, // 分组id。 想要发送到哪个组里
chatType: 1, // 聊天类型 固定内容
msgType: 0, // 消息类型 固定内容
content, // 消息内容,自己设计结构,比如你想发送图片(图片上传的接口)
nickName: this.nickName, // 用户昵称
avatar: this.avatar, // 用户头像
type // 消息类型。 你可以自己设计,发送过去是什么,返回的就是什么(1: 普通文本 2: 图片 3:点赞 4, 送花)
}
wx.sendSocketMessage({
data: JSON.stringify(msgBody),
})
},
sendMsg() {
if (!this.data.value) {
wx.showToast({
title: '请输入消息内容',
icon: "none"
})
return
}
this.sendSocketMsg(this.data.value, "1")
this.setData({
value: ""
})
},
sendImg() {
// 图片上传发送
uploadFile('https://showme2.myhope365.com/api/nos/upload/image', "file", {
'fileUseForEnum': 'DEFAULT'
}).then(res => {
this.sendSocketMsg(res.url, "2")
})
},
onUnload(){
// 进行卸载操作
wx.closeSocket({
code: 1000,
})
// 清除计时器
clearInterval(this.intervalId)
}
})