您当前的位置:首页 > 电脑百科 > 程序开发 > 移动端 > 小程序

小程序websocket开发指南

时间:2020-12-17 11:32:51  来源:  作者:

背景:一般与服务端交互频繁的需求,可以使用轮询机制来实现。然而一些业务场景,比如游戏大厅、直播、即时聊天等,这些需求都可以或者说更适合使用长连接来实现,一方面可以减少轮询带来的流量浪费,另一方面可以减少对服务的请求压力,同时也可以更实时的与服务端进行消息交互。

背景知识

HTTP vs WebSocket

名词解释

  1. HTTP:是一个用于传输超媒体文档(如html)的应用层的无连接、无状态协议。
  2. WebSocket:HTML5开始提供的一种浏览器与服务器进行全双工通讯的网络技术,属于应用层协议,基于TCL传输协议,并复用HTTP的握手通道。
小程序websocket开发指南

 

特点

  1. HTTP
  2. WebSocket建立在TCP协议之上,服务器端的实现比较容易;与HTTP协议有着良好的兼容性。默认端口也是80和443,并且握手阶段采用HTTP协议,因此握手时不容易屏蔽,能通过各种HTTP代理服务器;数据格式比较轻量,性能开销小,通信高效;可以发送文本(text),也可以发送二进制数据(ArrayBuffer);没有同源限制,客户端可以与任意服务器通信;协议标识符是ws(如果加密,则为wss),服务器网址就是URL;

二进制数组

名词解释

  1. ArrayBuffer​对象:代表原始的二进制数据。代表内存中的一段二进制数据,不能直接读写,只能通过“视图”(​TypedArray​和​DataView​)进行操作(以指定格式解读二进制数据)。“视图”部署了数组接口,这意味着,可以用数组的方法操作内存。
  2. ​TypedArray​对象:代表确定类型的二进制数据。用来生成内存的视图,通过9个构造函数,可以生成9种数据格式的视图,数组成员都是同一个数据类型,比如:​Unit8Array​:(无符号8位整数)数组视图​Int16Array​:(16位整数)数组视图​Float32Array​:(32位浮点数)数组视图
  • ...
  1. ​DataView​对象:代表不确定类型的二进制数据。用来生成内存的视图,可以自定义格式和字节序,比如第一个字节是​Uint8​(无符号8位整数)、第二个字节是​Int16​(16位整数)、第三个字节是​Float32​(32位浮点数)等等,数据成员可以是不同的数据类型。

举个栗子

​ArrayBuffer​也是一个构造函数,可以分配一段可以存放数据的连续内存区域

var buf = new ArrayBuffer(32); // 生成一段32字节的内存区域,每个字节的值默认都是0
为了读写buf,需要为它指定视图。
  1. ​DataView​视图,是一个构造函数,需要提供​ArrayBuffer​对象实例作为参数:
var dataView = new DataView(buf); // 不带符号的8位整数格式
dataView.getUnit8(0) // 0
  1. ​TypedArray​视图,是一组构造函数,代表不同的数据格式。
var x1 = new Init32Array(buf); // 32位带符号整数
x1[0] = 1;
var x2 = new Unit8Array(buf); // 8位不带符号整数
x2[0] = 2;
x1[0] // 2 两个视图对应同一段内存,一个视图修改底层内存,会影响另一个视图
TypedArray(buffer, byteOffset=0, length?)
  • buffer:必需,视图对应的底层​ArrayBuffer​对象
  • byteOffset:可选,视图开始的字节序号,默认从0开始,必须与所要建立的数据类型一致,否则会报错
var buffer = new ArrayBuffer(8);
var i16 = new Int16Array(buffer, 1);
// Uncaught RangeError: start offset of Int16Array should be a multiple of 2

因为,带符号的16位整数需要2个字节,所以byteOffset参数必须能够被2整除。

  • length:可选,视图包含的数据个数,默认直到本段内存区域结束

note:如果想从任意字节开始解读​ArrayBuffer​对象,必须使用​DataView​视图,因为​TypedArray​视图只提供9种固定的解读格式。

​TypedArray​视图的构造函数,除了接受​ArrayBuffer​实例作为参数,还可以接受正常数组作为参数,直接分配内存生成底层的​ArrayBuffer​实例,并同时完成对这段内存的赋值。

var typedArray = new Unit8Array([0, 1, 2]);
typedArray.length // 3
typedArray[0] = 5;
typedArray // [5, 1, 2]

总结

​ArrayBuffer​是一(大)块内存,但不能直接访问​ArrayBuffer​里面的字节。​TypedArray​只是一层视图,本身不储存数据,它的数据都储存在底层的​ArrayBuffer​对象之中,要获取底层对象必须使用buffer属性。其实​ArrayBuffer​ 跟 ​TypedArray​ 是一个东西,前者是一(大)块内存,后者用来访问这块内存。

Protocol Buffers

我们编码的目的是将结构化数据写入磁盘或用于网络传输,以便他人来读取,写入方式有多种选择,比如将数据转换为字符串,然后将字符串写入磁盘。也可以将需要处理的结构化数据由 .proto 文件描述,用 Protobuf 编译器将该文件编译成目标语言。

名词解释

Protocol Buffers 是一种轻便高效的结构化数据存储格式,可以用于结构化数据串行化,或者说序列化。它很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

基本原理

一般情况下,采用静态编译模式,先写好 .proto 文件,再用 Protobuf 编译器生成目标语言所需要的源代码文件,将这些生成的代码和应用程序一起编译。

读写数据过程是将对象序列化后生成二进制数据流,写入一个 fstream 流,从一个 fstream 流中读取信息并反序列化。

优缺点

  • 优点

Protocol Buffers 在序列化数据方面,它是灵活的,高效的。相比于 XML 来说,Protocol Buffers 更加小巧,更加快速,更加简单。一旦定义了要处理的数据的数据结构之后,就可以利用 Protocol Buffers 的代码生成工具生成相关的代码。甚至可以在无需重新部署程序的情况下更新数据结构。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言或从各种不同数据流中对你的结构化数据轻松读写。

Protocol Buffers 很适合做数据存储或 RPC 数据交换格式。可用于通讯协议、数据存储等领域的语言无关、平台无关、可扩展的序列化结构数据格式。

  • 缺点

消息结构可读性不高,序列化后的字节序列为二进制序列不能简单的分析有效性;

字节消息通道(Frontier)系统

整体设计

为了维护用户在线状态,需要和服务端保持长连接,决定采用websocket来跟服务端进行通信,同时使用消息通道系统来转发消息。

时序图

小程序websocket开发指南

 

技术要点

交互协议

  • connectSocket:创建一个WebSocket连接实例,并通过返回的​socketTask​操作该连接。
const wsUrl = `${domain}/ws/v2?aid=2493&device_id=${did}&fpid=100&access_key=${access_key}&code=${code}`
let socketTask = tt.connectSocket({
    url: wsUrl,
    protocols: ['p1']
});
  • ​wsUrl​遵循​Frontier​的交互协议:
  • aid:应用id,不是宿主App的appid,由服务端指定
  • fpid:由服务端指定
  • device_id:设备id,服务端通过aid+userid+did来维护长连接
  • access_key:用于防止攻击,一般用md5加密算法生成(​md5.hexMD5(fpid + appkey + did + salt);​)
  • code:调用​tt.login​获取的code,服务端通过 code2Session 可以将其转化为open_id,然后进一步转化为user_id用于标识用户的唯一性。
  • note:由于code具有时效性,每次重新建立​websocket​连接时,需要调用​tt.login​重新获取code。

数据协议

前面介绍了那么多关于​Protobuf​的内容,小程序的​webSocket​接口发送数据的类型支持​ArrayBuffer​,再加上​Frontier​对​Protobuf​支持得比较好,因此和服务端商定采用​Protobuf​作为整个长连接的数据通信协议。

想要在小程序中使用​Protobuf​,首先将.proto文件转换成js能解析的json,这样也比直接使用.proto文件更轻量,可以使用pbjs工具进行解析:

  1. 安装pbjs工具
  • 基于node.js,首先安装protobufjs
$ npm install -g protobufjs
  • 安装 pbjs需要的库 命令行执行下“pbjs”就ok
$ pbjs
  1. 使用pbjs转换.proto文件
  • 和服务端约定好的.proto文件
// awesome.proto
package wenlipackage;
syntax = "proto2";
message Header {
required string key = 1;
required string value = 2;
}
message Frame {
required uint64 SeqID = 1;
required uint64 LogID = 2; 
required int32 service = 3;
required int32 method = 4;
repeated Header headers = 5;
optional string payload_encoding = 6;
optional string payload_type = 7;
optional bytes payload = 8;
}
  • 转换awesome.proto文件
$ pbjs -t json awesome.proto > awesome.json

生成如下的awesom.json文件:

{
"nested": {
"wenlipackage": {
"nested": {
"Header": {
"fields": {
            ...
          }
        },
"Frame": {
"fields": {
            ...
          }
        }
      }
    }
  }
}
  • 此时的json文件还不能直接使用,必须采用​module.exports​的方式将其导出去,可生成如下的awesome.js文件供小程序引用。
module.exports = {
"nested": {
"wenlipackage": {
"nested": {
"Header": {
"fields": {
            ...
          }
        },
"Frame": {
"fields": {
            ...
          }
        }
      }
    }
  }
}
  1. 采用Protobuf库编/解码数据
// 引入protobuf模块
import * as protobuf from './weichatPb/protobuf'; 
// 加载awesome.proto对应的json
import awesomeConfig from './awesome.js'; 
// 加载JSON descriptor
const AwesomeRoot = protobuf.Root.fromJSON(awesomeConfig);
// Message类,.proto文件中定义了Frame是消息主体
const AwesomeMessage = AwesomeRoot.lookupType("Frame");
const payload = {test: "123"};
const message = AwesomeMessage.create(payload);
const array = AwesomeMessage.encode(message).finish();
// unit8Array => ArrayBuffer
const enMessage = array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
console.log("encodeMessage", enMessage);
// buffer 表示通过小程序this.socketTask.onMessage((msg) => {});接收到的数据
const deMessage = AwesomeMessage.decode(new Uint8Array(buffer));
console.log("decodeMessage", deMessage);

消息通信

一个​websocket​实例的生成需要经过以下步骤:

  1. 建立连接
  • 建立连接后会返回一个websoket实例
  1. 连接打开
  • 连接建立->连接打开是一个异步的过程,在这段时间内是监听不到消息,更是无法发送消息的
  1. 监听消息
  • 监听的时机比较关键,只有当连接建立并生成websocket实例后才能监听
  1. 发送消息
  • 发送当时机也很关键,只有当连接真正打开后才能发送消息

将小程序WebSocket的一些功能封装成一个类,里面包括建立连接、监听消息、发送消息、心跳检测、断线重连等等常用的功能。

  1. 封装websocket类
export default class websocket {
constructor({ heartCheck, isReconnection }) {
this.socketTask = null;// websocket实例
this._isLogin = false;// 是否连接
this._netWork = true;// 当前网络状态
this._isClosed = false;// 是否人为退出
this._timeout = 10000;// 心跳检测频率
this._timeoutObj = null;
this._connectNum = 0;// 当前重连次数
this._reConnectTimer = null;
this._heartCheck = heartCheck;// 心跳检测和断线重连开关,true为启用,false为关闭
this._isReconnection = isReconnection;
  }
  _reset() {}// 心跳重置
_start() {} // 心跳开始
  onSocketClosed(options) {}  // 监听websocket连接关闭
  onSocketError(options) {}  // 监听websocket连接关闭
  onNetworkChange(options) {}  // 检测网络变化
  _onSocketOpened() {}  // 监听websocket连接打开
  onReceivedMsg(callBack) {}  // 接收服务器返回的消息
  initWebSocket(options) {}  // 建立websocket连接
  sendWebSocketMsg(options) {}  // 发送websocket消息
  _reConnect(options) {}  // 重连方法,会根据时间频率越来越慢
  closeWebSocket(){}  // 关闭websocket连接
}

 

  1. 多个page使用同一个​websocket​对象

引入​vuex​维护一个全局​websocket​对象​globalWebsocket​,通过​mapMutations​的​changeGlobalWebsocket​方法改变全局​websocket​对象:

methods: {
    ...mapMutations(['changeGlobalWebsocket']),
    linkWebsocket(websocketUrl) {
// 建立连接
this.websocket.initWebSocket({
        url: websocketUrl,
success(res) { console.log('连接建立成功', res) },
fail(err) { console.log('连接建立失败', err) },
complate: (res) => {
this.changeGlobalWebsocket(res);
        }
      })
    }
}
  • 通过WebSocket类建立连接,将tt.connectSocket返回的websocket实例透传出来,全局共享。
computed: {
    ...mapState(['globalWebsocket']),
    newGlobalWebsocket() {
// 只有当连接建立并生成websocket实例后才能监听
if (this.globalWebsocket && this.globalWebsocket.socketTask) {
if (!this.hasListen) {
this.globalWebsocket.onReceivedMsg((res, data) => {
// 处理服务端发来的各类消息
this.handleServiceMsg(res, data);
          });
this.hasListen = true;
        }
if (this.globalWebsocket.socketTask.readyState === 1) {
// 当连接真正打开后才能发送消息
        }
      }
return this.globalWebsocket;
    },
},
watch: {
    newGlobalWebsocket(newVal, oldVal) {
if(oldVal && newVal.socketTask && newVal.socketTask !== oldVal.socketTask) {
// 重新监听
this.globalWebsocket.onReceivedMsg((res, data) => {
this.handleServiceMsg(res, data);
        });
      }
    },
  },

由于需要监听​websocket​的连接与断开,因此需要新生成一个computed属性​newGlobalWebsocket​,直接返回全局的​globalWebsocket​对象,这样才能watch到它的变化,并且在重新监听的时候需要控制好条件,只有​globalWebsocket​对象socketTask真正发生改变的时候才进行重新监听逻辑,否则会收到重复的消息。

问题总结

  1. 直接引入google官方Protobuf库(protobuf.js)将json => pb,在开发者工具能正常使用,真机却报错:
小程序websocket开发指南

 


小程序websocket开发指南

 

原因是protobufjs 代码里面有用到 Function() {} 来执行一段代码,在小程序中Function 和 eval 相关的动态执行代码方式都给屏蔽了,是不允许开发者使用的,导致这个库不能正常使用。

解决办法:搜了一圈github,找到有人专门针对这个问题,修改了dcodeIO 的protobuf.js部分实现方式,写了一个能在小程序中运行的 protobuf.js 。

  1. ​ArrayBuffer​ vs ​Unit8Array​ 到底是个什么关系??!
  • 受小程序框架、protobuf.js工具以及Frontier系统限制,发送消息和接收消息的格式如下
小程序websocket开发指南

 


小程序websocket开发指南

 

可以看到:

  • 发送消息经过protobuf.js编码后的消息是​Unit8Array​格式的
  • 接收到的服务器原始消息是​ArrayBuffer​格式的

上文介绍了​TyedArray​和​ArrayBuffer​的区别,​Unit8Array​是​TypedArray​对象的一种类型,用来表示​ArrayBuffer​的视图,用来读写​ArrayBuffer​,要访问​ArrayBuffer​的底层对象,必须使用​Unit8Array​的buffer属性。

  • 一开始跟服务端调websocket的连通性,发现用​AwesomeMessage.decode​解析服务端消息会解析失败:
小程序websocket开发指南

 

const msg = xxx; // ArrayBuffer类型
const res = AwesomeMessage.decode(msg); // 直接解析ArrayBuffer会报错
const res = AwesomeMessage.decode(new Uint8Array(msg)); // ArrayBuffer => Unit8Array => decode => JSON

原因是原始msg是​ArrayBuffer​类型,protobuf.js在解码的时候限制了类型是​TypedArray​类型,否则解析失败,因此需要将其转换为​TypedArray​对象,选择​Uint8Array​子类型,才能解析成前端能读取的json对象。

  • 在开发者工具调通协议后,转到真机,发现后端解析不了前端发的消息:
小程序websocket开发指南

 


小程序websocket开发指南

 

【开发者工具抓包消息】

小程序websocket开发指南

 

【真机抓包消息】

抓包发现在开发者工具发送的消息是二进制(Binary)类型的,真机却是文本(Text)类型,这就很奇怪了,仔细翻了下小程序文档:

小程序websocket开发指南

 

小程序框架对发送的消息类型进行了限制,只能是string(Text)或arraybuffer(Binary)类型的,真机为啥被转成了text类型呢,首先肯定不是主动发送的string类型,一种可能就是发送的消息不是arraybuffer类型,默认被转成了string。看了下代码:

const encodeMsg = (msg) => {
const message = AwesomeMessage.create(msg);
const array = AwesomeMessage.encode(message).finish();// unit8Array
return array;
};

发现发送的类型直接是​Unit8Array​,开发者工具没有对其进行转换,这个数据是能直接被服务端解析的,然而在真机被转换成了String,导致服务端解析不了,更改代码,将​Unit8Array​转换成​ArrayBuffer​,问题得到解决,在真机和开发者工具都正常:

const encodeMsg = (msg) => {
const message = AwesomeMessage.create(msg);
const array = AwesomeMessage.encode(message).finish();
  console.log('加密后即将发送的消息', array);
// unit8Array => ArrayBuffer,只支持ArrayBuffer
return array.buffer.slice(array.byteOffset, array.byteLength + array.byteOffset)
};

其实还发现一个现象:

小程序websocket开发指南

 

即收到的服务端原始消息最外层是​ArrayBuffer​类型的,解密后的业务数据payload却是​Unit8Array​类型的,结合发送消息时encdoe后的类型也是​Unit8Array​类型,得出如下结论:

  • protobuf.js库和Frontier对数据的处理是以​Unit8Array​类型为准,服务端同时支持​ArrayBuffer​和​Unit8Array​两种类型数据的解析;
  • 小程序框架只支持​ArrayBuffer​和​String​类型数据,其余类型会默认当成​String​类型;

上述两个规则限制导致在数据传输过程中,需要将数据格式转成标准的​ArrayBuffer​即小程序框架支持的数据格式。

ps:至于为啥开发者工具和真机表现不一致,这是因为开发者工具其实是一个web,和小程序的运行时并不太一样,同时由于两者不统一,导致在开发调试过程中踩了许多的坑。 ‍♀️

参考文献

小程序WebSocket接口文档:

https://developer.toutiao.com/docs/api/connectSocket.html#%E8%BE%93%E5%85%A5

protocol buffers介绍:

https://halfrost.com/protobuf_encode/

 

作者:byte

出处:https://segmentfault.com/a/1190000024456875



Tags:websocket   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
最近工作中需要开发前端操作远程虚拟机的功能,简称 WebShell。基于当前的技术栈为 react+django,调研了一会发现大部分的后端实现都是 django+channels 来实现 websocket 服务。...【详细内容】
2021-09-13  Tags: websocket  点击:(52)  评论:(0)  加入收藏
前言:作为一个刚踏入职场的实习生,我很幸运参加了某个政府项目,并且在项目中负责一个核心模块功能的开发,而不是从头到尾对数据库的crud。虽然我一直心里抱怨我的工作范围根本...【详细内容】
2021-05-24  Tags: websocket  点击:(230)  评论:(0)  加入收藏
1)通知功能:保持一个长连接,当服务端游新的消息,能够实时的推送到使用方。像知乎的点赞通知、评论等,都可以使用WebSocket通信。某些使用H5的客户端,为了简化开发,也会使用WebSocke...【详细内容】
2021-03-03  Tags: websocket  点击:(156)  评论:(0)  加入收藏
背景:一般与服务端交互频繁的需求,可以使用轮询机制来实现。然而一些业务场景,比如游戏大厅、直播、即时聊天等,这些需求都可以或者说更适合使用长连接来实现,一方面可以减少轮询...【详细内容】
2020-12-17  Tags: websocket  点击:(156)  评论:(0)  加入收藏
1. 前言Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送...【详细内容】
2020-11-11  Tags: websocket  点击:(100)  评论:(0)  加入收藏
前段时间我有这样一个需求,想和一个异地的人一起看电影,先后在网上找了一些方案,不过那几个案都有一些缺点...【详细内容】
2020-10-21  Tags: websocket  点击:(92)  评论:(0)  加入收藏
1. 前言Websocket是 HTML5 开始提供的一种在单个 TCP 连接上进行全双工通讯的协议。WebSocket 使得客户端和服务器之间的数据交换变得更加简单,允许服务端主动向客户端推送数...【详细内容】
2020-09-18  Tags: websocket  点击:(97)  评论:(0)  加入收藏
前言服务器和客户端保持长连接通信,实现方式比较多。有很多成熟的框架可以完成,底层无非都是对Socket流的封装和使用。一、SOCKET原理Socket大致是指在端到端的一个连接中,这...【详细内容】
2020-08-06  Tags: websocket  点击:(75)  评论:(0)  加入收藏
1. 心跳重连原由 心跳和重连的目的用一句话概括就是客户端和服务端保证彼此还活着,避免丢包发生。websocket连接断开有以下两种情况:前端断开在使用websocket过程中,可能会出现...【详细内容】
2020-07-29  Tags: websocket  点击:(308)  评论:(0)  加入收藏
用这些简化了 WebSockets 的开源支持工具来控制你的流媒体。 来源:https://linux.cn/article-12347-1.html 作者:Kevin Sonney 译者:Xingyu.Wang(本文字数:4340,阅读时长大约:6 分...【详细内容】
2020-06-25  Tags: websocket  点击:(96)  评论:(0)  加入收藏
▌简易百科推荐
一、项目背景随着小程序在用户规模和商业化上取得的极大成功,各大平台都推出了自己的小程序。然而这些平台的小程序开发在语法上又不尽相同,不同平台小程序代码的维护需要投入...【详细内容】
2021-11-05  携程技术    Tags:小程序   点击:(65)  评论:(0)  加入收藏
作者:灰灰来源:JS每日一题 一、背景传统的web开发实现登陆功能,一般的做法是输入账号密码、或者输入手机号及短信验证码进行登录服务端校验用户信息通过之后,下发一个代表登录态...【详细内容】
2021-10-29  Nodejs开发    Tags:微信小程序   点击:(43)  评论:(0)  加入收藏
总结列举微信小程序开放能力清单 硬件能力 蓝牙 NFC读写 连接WIFI设备 开放能力 ...【详细内容】
2021-09-27  软件开发分享    Tags:微信小程序   点击:(60)  评论:(0)  加入收藏
核心商城(CoreShop)介绍核心小程序商城系统(CoreShop) 是基于 Asp.Net 5.0、Uni-App开发、支持可视化布局的小程序商城系统;前后端分离,支持分布式部署,跨平台运行;拥有分销、代理、...【详细内容】
2021-07-20  码农也有梦想    Tags:小程序商城   点击:(115)  评论:(0)  加入收藏
介绍Vue3发布已经有一段时间了,从目前来看,其生态还算可以,也已经有了各种组件库给予了支持,但是不管是Vue3还是Vue2都无法直接用来开发小程序,因此国内一些技术团队针对Vue开发...【详细内容】
2021-07-13  爱分享Coder    Tags:小程序   点击:(203)  评论:(0)  加入收藏
首先明确几个概念1. W3C:指万维网联盟(World Wide Web Consortium),是一个国际的标准的制定机构。2. H5(HTML5,HyperText Markup Language 5的缩写),HTML5 是由W3C制定的新HTML标...【详细内容】
2021-07-06  畅游零和一的海洋    Tags:微信小程序   点击:(152)  评论:(0)  加入收藏
在开发微信公众号时,需要不时请求URL和数据封装。为了不做重复的工作。提取公共部分进行封装。产生了相应的公众类。今天先来写下请求类,代码如下:public class HttpRequestP...【详细内容】
2021-06-16  java浮萍  今日头条  Tags:公共类   点击:(134)  评论:(0)  加入收藏
小程序上线后,改版了很多次,包括一些 Api 接口也有改动。如果你学习一个很久之前的小程序项目是没有意义的,本文推荐的小程序都是最近有更新的。相信在你学习、部署的过程中,不...【详细内容】
2021-06-08    程序猿久一  Tags:微信小程序   点击:(206)  评论:(0)  加入收藏
自从2019年微信公开课Pro在微信之夜演示《跳一跳》以来,微信小游戏已经不知不觉走过的三年,这三年中我们可以明显看到微信对小游戏的扶持,对于微信开发者来说,微信小游戏开发以...【详细内容】
2021-05-25  开课吧科科  今日头条  Tags:微信小游戏   点击:(212)  评论:(0)  加入收藏
学习编程从hello world开始。学习微信小程序开发首先要安装一个微信开发者工具,官网上免费下载童叟无欺,下载完傻瓜式安装即可。 双击微信开发者工具,然后选择小程序,然后点击...【详细内容】
2021-05-12  程序员fearlazy  fearlazy  Tags:微信小程序   点击:(268)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条