You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在JavaScript中构建二进制数据结构及高效数据序列化方案问询

在JavaScript中实现高效自定义序列化的方案

我完全懂你怀念Flash那套自定义序列化的便捷性——JSON确实太啰嗦了,尤其是当你有大量重复结构的数据时,键名的重复传输会浪费不少带宽,而且所有类型都转成字符串也会额外增加体积。下面给你几个适配Node.js和浏览器的方案,从原生手动实现到成熟库都有:

一、原生API手动实现(类似Flash ByteArray)

JavaScript的ArrayBuffer + DataView组合完全可以模拟Flash的序列化逻辑,你可以精确控制每个字段的字节长度,完全自定义序列化规则,没有冗余。

核心思路:

  1. 用DataView操作ArrayBuffer,它支持写入/读取各种数值类型(比如16位短整数、32位无符号整数等),和Flash的writeShort/writeUnsignedInt一一对应。
  2. 序列化完成后,把ArrayBuffer转成Base64字符串用于传输;反序列化时则反向操作。

示例代码:

先封装一个类似Flash ByteArray的工具类:

class ByteArray {
  constructor(buffer = new ArrayBuffer(1024)) {
    this.view = new DataView(buffer);
    this.position = 0;
  }

  // 写入16位有符号整数
  writeShort(value) {
    this.view.setInt16(this.position, value, true); // true表示小端字节序,可根据需求调整
    this.position += 2;
  }

  // 写入32位无符号整数
  writeUnsignedInt(value) {
    this.view.setUint32(this.position, value, true);
    this.position += 4;
  }

  // 写入任意JSON可序列化数据(如果需要处理复杂对象)
  writeObject(obj) {
    const jsonStr = JSON.stringify(obj);
    const encoder = new TextEncoder();
    const strBytes = encoder.encode(jsonStr);
    // 先写入字符串长度,方便反序列化时读取
    this.writeUnsignedInt(strBytes.length);
    new Uint8Array(this.view.buffer, this.position, strBytes.length).set(strBytes);
    this.position += strBytes.length;
  }

  // 读取16位有符号整数
  readShort() {
    const value = this.view.getInt16(this.position, true);
    this.position += 2;
    return value;
  }

  // 读取32位无符号整数
  readUnsignedInt() {
    const value = this.view.getUint32(this.position, true);
    this.position += 4;
    return value;
  }

  // 读取对象
  readObject() {
    const length = this.readUnsignedInt();
    const strBytes = new Uint8Array(this.view.buffer, this.position, length);
    this.position += length;
    const decoder = new TextDecoder();
    const jsonStr = decoder.decode(strBytes);
    return JSON.parse(jsonStr);
  }

  // 转成Base64字符串(兼容Node和浏览器)
  toBase64() {
    if (typeof Buffer !== 'undefined') {
      // Node.js环境
      return Buffer.from(this.view.buffer.slice(0, this.position)).toString('base64');
    } else {
      // 浏览器环境
      const uint8Array = new Uint8Array(this.view.buffer.slice(0, this.position));
      let binary = '';
      for (let i = 0; i < uint8Array.length; i++) {
        binary += String.fromCharCode(uint8Array[i]);
      }
      return btoa(binary);
    }
  }

  // 从Base64字符串加载数据
  static fromBase64(base64Str) {
    let buffer;
    if (typeof Buffer !== 'undefined') {
      // Node.js环境
      buffer = Buffer.from(base64Str, 'base64');
    } else {
      // 浏览器环境
      const binary = atob(base64Str);
      buffer = new ArrayBuffer(binary.length);
      const uint8Array = new Uint8Array(buffer);
      for (let i = 0; i < binary.length; i++) {
        uint8Array[i] = binary.charCodeAt(i);
      }
    }
    return new ByteArray(buffer);
  }
}

然后用这个类实现你的数据序列化/反序列化:

// 序列化示例
function serializeUserData(session, itemID, arbitraryData, score) {
  const byteArray = new ByteArray();
  byteArray.writeShort(session);
  byteArray.writeUnsignedInt(itemID);
  byteArray.writeObject(arbitraryData);
  byteArray.writeShort(score);
  return byteArray.toBase64();
}

// 反序列化示例
function deserializeUserData(base64Str) {
  const byteArray = ByteArray.fromBase64(base64Str);
  return {
    session: byteArray.readShort(),
    itemID: byteArray.readUnsignedInt(),
    arbitraryData: byteArray.readObject(),
    score: byteArray.readShort()
  };
}

// 使用示例
const serialized = serializeUserData(123, 456789, {name: "test", enabled: true}, 99);
console.log('序列化后Base64:', serialized);

const deserialized = deserializeUserData(serialized);
console.log('反序列化结果:', deserialized);

二、成熟库推荐(减少手动编码工作量)

如果你不想自己写底层逻辑,下面这些库都支持Node.js和浏览器,且序列化效率远高于JSON:

1. MessagePack(msgpack-lite)

MessagePack是一种二进制序列化格式,比JSON更紧凑,同时支持自定义类型的序列化规则,用法和JSON类似但体积更小。

示例用法:

const msgpack = require('msgpack-lite'); // Node.js环境;浏览器可以通过<script>引入或打包工具

// 序列化
const data = {session: 123, itemID: 456789, arbitraryData: {name: "test"}, score: 99};
const buffer = msgpack.encode(data);
const base64 = Buffer.from(buffer).toString('base64'); // Node.js;浏览器用Uint8Array转Base64

// 反序列化
const decodedBuffer = Buffer.from(base64, 'base64'); // Node.js;浏览器用atob转成ArrayBuffer
const decodedData = msgpack.decode(decodedBuffer);

2. Protocol Buffers(protobufjs)

Protocol Buffers是Google开发的高效序列化协议,需要先定义数据结构的Schema,然后自动生成序列化/反序列化代码,适合结构化数据,体积极小,性能极佳。

示例步骤:

  1. 定义Schema(比如userdata.proto):
syntax = "proto3";

message UserData {
  int32 session = 1;
  uint32 itemID = 2;
  map<string, string> arbitraryData = 3; // 或者自定义嵌套类型
  int32 score = 4;
}
  1. 用protobufjs生成JS代码,或者直接动态加载Schema:
const protobuf = require("protobufjs"); // Node.js;浏览器支持

// 加载Schema并生成类型
protobuf.load("userdata.proto", (err, root) => {
  if (err) throw err;
  const UserData = root.lookupType("UserData");

  // 准备数据
  const data = {session: 123, itemID: 456789, arbitraryData: {name: "test"}, score: 99};

  // 验证数据(可选)
  const errMsg = UserData.verify(data);
  if (errMsg) throw Error(errMsg);

  // 序列化
  const message = UserData.create(data);
  const buffer = UserData.encode(message).finish();
  const base64 = Buffer.from(buffer).toString('base64');

  // 反序列化
  const decodedMessage = UserData.decode(Buffer.from(base64, 'base64'));
  const decodedData = UserData.toObject(decodedMessage);
  console.log(decodedData);
});

3. FlatBuffers

FlatBuffers也是Google的产品,特点是序列化后的数据不需要解析就能直接访问,性能极高,适合对延迟和带宽要求极高的场景,用法和Protocol Buffers类似,需要定义Schema。

三、方案选择建议

  • 如果需要完全自定义序列化逻辑(和Flash那套完全对齐),选原生ArrayBuffer+DataView的方案,灵活性最高。
  • 如果想兼顾开发效率和压缩率,选MessagePack,学习成本低,无需定义Schema。
  • 如果是大型项目、结构化数据,追求极致性能和压缩率,选Protocol Buffers或FlatBuffers。

内容的提问来源于stack exchange,提问作者Jim Sreven

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 07:08:17