云计算百科
云计算领域专业知识百科平台

【Laya】LocalStorage 本地存储

Laya.LocalStorage 本地存储使用指南

简介

Laya.LocalStorage 是 LayaAir 引擎提供的本地存储管理类,用于实现数据的持久化存储。基于浏览器 LocalStorage API 封装,支持字符串和 JSON 对象的存储。

适用场景:

  • 游戏存档、进度保存
  • 玩家设置、配置项存储
  • 离线数据缓存
  • 用户偏好记录

工作原理:

数据 → 序列化 → 浏览器 LocalStorage → 持久保存 → 下次启动读取

核心优势:

优势说明
数据持久化 浏览器关闭后数据仍保留
容量充足 通常约 5MB 存储空间
API 简洁 字符串和 JSON 对象分别处理
跨会话 同一域名下数据共享

目录

  • API 参考
  • 基础用法
  • 实用示例
  • 高级技巧
  • 最佳实践

API 参考

属性

属性类型说明
count number 获取本地存储中存储的项目数量(只读)

字符串存储

方法参数返回值说明
setItem(key: string, value: string) 键名, 键值 void 存储字符串类型的键值对
getItem(key: string) 键名 string | null 获取指定键名的字符串值,不存在返回 null

JSON 存储

方法参数返回值说明
setJSON(key: string, value: any) 键名, 任意值 void 存储 Object 类型(自动序列化为 JSON)
getJSON(key: string) 键名 any 获取指定键名的 Object 值(自动反序列化)

数据管理

方法参数返回值说明
removeItem(key: string) 键名 void 删除指定键名的数据
clear() void 清除所有本地存储的数据

基础用法

1. 存储和读取字符串

最基础的存储方式:

// 存储数据
Laya.LocalStorage.setItem("username", "player1");
Laya.LocalStorage.setItem("level", "10");
Laya.LocalStorage.setItem("music", "0.8");

// 读取数据
let name = Laya.LocalStorage.getItem("username"); // "player1"
let level = Laya.LocalStorage.getItem("level"); // "10"
let music = Laya.LocalStorage.getItem("music"); // "0.8"

// 读取不存在的键返回 null
let unknown = Laya.LocalStorage.getItem("unknown"); // null

2. 存储和读取 JSON 对象

存储复杂对象时使用 setJSON/getJSON:

// 存储 JSON 对象
Laya.LocalStorage.setJSON("playerData", {
level: 25,
score: 15000,
inventory: ["sword", "shield", "potion"],
stats: {
hp: 100,
mp: 50,
attack: 35
}
});

// 读取 JSON 对象
let playerData = Laya.LocalStorage.getJSON("playerData");
console.log(playerData.level); // 25
console.log(playerData.inventory); // ["sword", "shield", "potion"]
console.log(playerData.stats.hp); // 100

3. 删除单个数据

// 删除指定键的数据
Laya.LocalStorage.removeItem("username");

// 验证删除
console.log(Laya.LocalStorage.getItem("username")); // null

4. 清空所有数据

// 清空所有存储
Laya.LocalStorage.clear();

// 检查数量
console.log(Laya.LocalStorage.count); // 0

5. 检查存储数量

// 存储一些数据
Laya.LocalStorage.setItem("key1", "value1");
Laya.LocalStorage.setItem("key2", "value2");
Laya.LocalStorage.setItem("key3", "value3");

// 获取存储数量
console.log(Laya.LocalStorage.count); // 3


实用示例

示例1: 游戏存档系统

完整的存档管理器,支持保存/读取/删除存档:

@regClass()
export class SaveManager {
private static readonly SAVE_KEY = "game_save_data";
private static readonly MAX_SAVES = 3;

/**
* 保存当前游戏进度
*/

public static save(slot: number = 1): boolean {
if (slot < 1 || slot > this.MAX_SAVES) {
console.error("存档槽位无效:", slot);
return false;
}

const saveData = {
slot: slot,
timestamp: Date.now(),
version: "1.0.0",
player: {
level: GameManager.playerLevel,
exp: GameManager.playerExp,
gold: GameManager.playerGold,
hp: GameManager.playerHp,
maxHp: GameManager.playerMaxHp
},
scene: GameManager.currentScene,
quests: GameManager.quests.map(q => ({
id: q.id,
status: q.status,
progress: q.progress
})),
settings: {
musicVolume: AudioManager.musicVolume,
soundVolume: AudioManager.soundVolume
}
};

const key = `${this.SAVE_KEY}_${slot}`;
Laya.LocalStorage.setJSON(key, saveData);
console.log(`存档已保存到槽位 ${slot}`);
return true;
}

/**
* 加载指定槽位的存档
*/

public static load(slot: number = 1): boolean {
if (slot < 1 || slot > this.MAX_SAVES) {
console.error("存档槽位无效:", slot);
return false;
}

const key = `${this.SAVE_KEY}_${slot}`;
const saveData = Laya.LocalStorage.getJSON(key);

if (!saveData) {
console.log(`槽位 ${slot} 没有存档`);
return false;
}

// 恢复游戏数据
GameManager.playerLevel = saveData.player.level;
GameManager.playerExp = saveData.player.exp;
GameManager.playerGold = saveData.player.gold;
GameManager.playerHp = saveData.player.hp;
GameManager.playerMaxHp = saveData.player.maxHp;
GameManager.currentScene = saveData.scene;

// 恢复任务
GameManager.loadQuests(saveData.quests);

// 恢复设置
AudioManager.musicVolume = saveData.settings.musicVolume;
AudioManager.soundVolume = saveData.settings.soundVolume;

console.log(`存档已加载,保存时间:${new Date(saveData.timestamp).toLocaleString()}`);
return true;
}

/**
* 检查指定槽位是否有存档
*/

public static hasSave(slot: number = 1): boolean {
const key = `${this.SAVE_KEY}_${slot}`;
return Laya.LocalStorage.getItem(key) !== null;
}

/**
* 获取存档信息(用于显示存档列表)
*/

public static getSaveInfo(slot: number = 1): any | null {
const key = `${this.SAVE_KEY}_${slot}`;
const saveData = Laya.LocalStorage.getJSON(key);

if (!saveData) return null;

return {
slot: slot,
timestamp: saveData.timestamp,
level: saveData.player.level,
scene: saveData.scene
};
}

/**
* 删除指定槽位的存档
*/

public static deleteSave(slot: number = 1): void {
const key = `${this.SAVE_KEY}_${slot}`;
Laya.LocalStorage.removeItem(key);
console.log(`槽位 ${slot} 的存档已删除`);
}

/**
* 获取所有存档信息
*/

public static getAllSaves(): any[] {
const saves = [];
for (let i = 1; i <= this.MAX_SAVES; i++) {
const info = this.getSaveInfo(i);
if (info) {
saves.push(info);
}
}
return saves;
}
}

// 使用示例
// 保存游戏
SaveManager.save(1);

// 检查是否有存档
if (SaveManager.hasSave(1)) {
// 加载存档
SaveManager.load(1);
}

// 获取所有存档信息
const saves = SaveManager.getAllSaves();
console.log(saves);

示例2: 游戏设置管理

设置数据的持久化存储:

@regClass()
export class GameSettings {
private static readonly SETTINGS_KEY = "game_settings";

// 默认设置
public static readonly DEFAULTS = {
musicVolume: 0.8,
soundVolume: 1.0,
enableVibration: true,
enableParticles: true,
language: "zh-CN",
quality: "high",
autoSave: true,
showFps: false
};

// 当前设置
static settings = { this.DEFAULTS };

/**
* 加载设置
*/

public static load(): void {
const saved = Laya.LocalStorage.getJSON(this.SETTINGS_KEY);
if (saved) {
// 合并保存的设置和默认设置(处理新增设置项)
this.settings = { this.DEFAULTS, saved };
}
console.log("设置已加载:", this.settings);
}

/**
* 保存设置
*/

public static save(): void {
Laya.LocalStorage.setJSON(this.SETTINGS_KEY, this.settings);
console.log("设置已保存");
}

/**
* 更新单个设置并保存
*/

public static update<K extends keyof typeof GameSettings.settings>(
key: K,
value: typeof GameSettings.settings[K]
): void {
this.settings[key] = value;
this.save();
}

/**
* 获取设置值
*/

public static get<K extends keyof typeof GameSettings.settings>(
key: K
): typeof GameSettings.settings[K] {
return this.settings[key];
}

/**
* 重置为默认设置
*/

public static reset(): void {
this.settings = { this.DEFAULTS };
this.save();
console.log("设置已重置");
}

/**
* 应用设置到游戏
*/

public static apply(): void {
AudioManager.musicVolume = this.settings.musicVolume;
AudioManager.soundVolume = this.settings.soundVolume;
Laya.Stat.show = this.settings.showFps;
}
}

// 游戏启动时加载设置
GameSettings.load();
GameSettings.apply();

// 修改设置
GameSettings.update("musicVolume", 0.5);
GameSettings.update("showFps", true);

示例3: 玩家数据统计

记录玩家统计数据:

@regClass()
export class PlayerStats {
private static readonly STATS_KEY = "player_stats";

static stats = {
totalPlayTime: 0, // 总游戏时长(秒)
totalGamesPlayed: 0, // 游戏次数
totalScore: 0, // 总得分
highestScore: 0, // 最高分
totalKills: 0, // 总击杀数
totalDeaths: 0, // 总死亡数
achievements: [] as string[], // 成就列表
firstPlayTime: 0, // 首次游戏时间
lastPlayTime: 0 // 最后游戏时间
};

/**
* 加载统计数据
*/

public static load(): void {
const saved = Laya.LocalStorage.getJSON(this.STATS_KEY);
if (saved) {
this.stats = { this.stats, saved };
} else {
// 首次游戏,记录时间
this.stats.firstPlayTime = Date.now();
}
this.stats.lastPlayTime = Date.now();
}

/**
* 保存统计数据
*/

public static save(): void {
Laya.LocalStorage.setJSON(this.STATS_KEY, this.stats);
}

/**
* 记录游戏结束
*/

public static recordGameEnd(score: number, kills: number, deaths: number): void {
this.stats.totalGamesPlayed++;
this.stats.totalScore += score;
this.stats.totalKills += kills;
this.stats.totalDeaths += deaths;

if (score > this.stats.highestScore) {
this.stats.highestScore = score;
}

this.save();
}

/**
* 解锁成就
*/

public static unlockAchievement(achievementId: string): void {
if (!this.stats.achievements.includes(achievementId)) {
this.stats.achievements.push(achievementId);
this.save();
console.log("成就解锁:", achievementId);
}
}

/**
* 获取统计信息
*/

public static getStats(): typeof PlayerStats.stats {
return { this.stats };
}

/**
* 计算平均得分
*/

public static getAverageScore(): number {
if (this.stats.totalGamesPlayed === 0) return 0;
return Math.floor(this.stats.totalScore / this.stats.totalGamesPlayed);
}

/**
* 计算 KDA
*/

public static getKDA(): number {
if (this.stats.totalDeaths === 0) return this.stats.totalKills;
return parseFloat((this.stats.totalKills / this.stats.totalDeaths).toFixed(2));
}
}

示例4: 离线缓存系统

缓存游戏资源数据:

@regClass()
export class CacheManager {
private static readonly CACHE_PREFIX = "cache_";
private static readonly CACHE_META = "cache_meta";
private static readonly DEFAULT_EXPIRE = 24 * 60 * 60 * 1000; // 24小时

/**
* 存储缓存数据(带过期时间)
*/

public static set(key: string, data: any, expireMs: number = this.DEFAULT_EXPIRE): void {
const cacheKey = this.CACHE_PREFIX + key;
const expireTime = Date.now() + expireMs;

const cacheData = {
data: data,
expire: expireTime
};

Laya.LocalStorage.setJSON(cacheKey, cacheData);
this.updateMeta(key, expireTime);
}

/**
* 获取缓存数据
*/

public static get(key: string): any | null {
const cacheKey = this.CACHE_PREFIX + key;
const cacheData = Laya.LocalStorage.getJSON(cacheKey);

if (!cacheData) {
return null;
}

// 检查是否过期
if (Date.now() > cacheData.expire) {
this.remove(key);
return null;
}

return cacheData.data;
}

/**
* 删除缓存
*/

public static remove(key: string): void {
const cacheKey = this.CACHE_PREFIX + key;
Laya.LocalStorage.removeItem(cacheKey);
}

/**
* 清理所有过期缓存
*/

public static cleanExpired(): void {
const meta = Laya.LocalStorage.getJSON(this.CACHE_META) || {};
const now = Date.now();
let cleaned = 0;

for (const key in meta) {
if (meta[key] < now) {
this.remove(key);
delete meta[key];
cleaned++;
}
}

if (cleaned > 0) {
Laya.LocalStorage.setJSON(this.CACHE_META, meta);
console.log(`清理了 ${cleaned} 个过期缓存`);
}
}

/**
* 清空所有缓存
*/

public static clear(): void {
const meta = Laya.LocalStorage.getJSON(this.CACHE_META) || {};
for (const key in meta) {
Laya.LocalStorage.removeItem(this.CACHE_PREFIX + key);
}
Laya.LocalStorage.removeItem(this.CACHE_META);
}

/**
* 更新缓存元数据
*/

private static updateMeta(key: string, expireTime: number): void {
const meta = Laya.LocalStorage.getJSON(this.CACHE_META) || {};
meta[key] = expireTime;
Laya.LocalStorage.setJSON(this.CACHE_META, meta);
}
}

// 使用示例
// 缓存服务器数据,1小时过期
CacheManager.set("player_info", serverData, 60 * 60 * 1000);

// 获取缓存
const data = CacheManager.get("player_info");

// 定期清理过期缓存
setInterval(() => {
CacheManager.cleanExpired();
}, 60 * 60 * 1000); // 每小时清理一次

示例5: 数据版本迁移

处理数据结构升级:

@regClass()
export class MigrationManager {
private static readonly VERSION_KEY = "data_version";
private static readonly CURRENT_VERSION = 3;

/**
* 执行数据迁移
*/

public static migrate(): void {
const savedVersion = parseInt(Laya.LocalStorage.getItem(this.VERSION_KEY) || "0");

if (savedVersion >= this.CURRENT_VERSION) {
console.log("数据已是最新版本");
return;
}

console.log(`开始数据迁移:${savedVersion}${this.CURRENT_VERSION}`);

// 依次执行迁移
for (let v = savedVersion + 1; v <= this.CURRENT_VERSION; v++) {
this.runMigration(v);
}

// 更新版本号
Laya.LocalStorage.setItem(this.VERSION_KEY, this.CURRENT_VERSION.toString());
console.log("数据迁移完成");
}

/**
* 执行指定版本的迁移
*/

private static runMigration(version: number): void {
console.log(`执行迁移 v${version}`);

switch (version) {
case 1:
this.migrateToV1();
break;
case 2:
this.migrateToV2();
break;
case 3:
this.migrateToV3();
break;
}
}

/**
* 迁移到版本1:重命名键名
*/

private static migrateToV1(): void {
const oldData = Laya.LocalStorage.getJSON("player");
if (oldData) {
Laya.LocalStorage.setJSON("player_data", oldData);
Laya.LocalStorage.removeItem("player");
}
}

/**
* 迁移到版本2:添加新字段
*/

private static migrateToV2(): void {
const settings = Laya.LocalStorage.getJSON("game_settings");
if (settings && !settings.quality) {
settings.quality = "high";
Laya.LocalStorage.setJSON("game_settings", settings);
}
}

/**
* 迁移到版本3:重构数据结构
*/

private static migrateToV3(): void {
const oldSaves = Laya.LocalStorage.getJSON("game_save_data");
if (oldSaves) {
const newSaves = {
slot: 1,
timestamp: oldSaves.timestamp || Date.now(),
player: {
level: oldSaves.level || 1,
exp: oldSaves.exp || 0
}
};
Laya.LocalStorage.setJSON("game_save_data_1", newSaves);
Laya.LocalStorage.removeItem("game_save_data");
}
}
}

// 游戏启动时执行迁移
MigrationManager.migrate();


高级技巧

1. 数据加密存储

敏感数据加密后存储:

@regClass()
export class SecureStorage {
// 简单的 XOR 加密(生产环境应使用更安全的加密方式)
private static encrypt(data: string, key: string = "layaair"): string {
let result = "";
for (let i = 0; i < data.length; i++) {
result += String.fromCharCode(
data.charCodeAt(i) ^ key.charCodeAt(i % key.length)
);
}
return btoa(result); // Base64 编码
}

private static decrypt(data: string, key: string = "layaair"): string {
const decoded = atob(data); // Base64 解码
let result = "";
for (let i = 0; i < decoded.length; i++) {
result += String.fromCharCode(
decoded.charCodeAt(i) ^ key.charCodeAt(i % key.length)
);
}
return result;
}

public static setItem(key: string, value: string): void {
const encrypted = this.encrypt(value);
Laya.LocalStorage.setItem("sec_" + key, encrypted);
}

public static getItem(key: string): string | null {
const encrypted = Laya.LocalStorage.getItem("sec_" + key);
if (!encrypted) return null;
return this.decrypt(encrypted);
}
}

// 使用
SecureStorage.setItem("token", "player_secret_token");
const token = SecureStorage.getItem("token");

2. 数据压缩存储

使用 JSON 简化减少存储空间:

@regClass()
export class CompressedStorage {
/**
* 存储时只保留必要字段
*/

public static setPlayerData(data: any): void {
const compressed = {
l: data.level, // level -> l
e: data.exp, // exp -> e
g: data.gold, // gold -> g
i: data.inventory // inventory -> i
};
Laya.LocalStorage.setJSON("p", compressed);
}

/**
* 读取时恢复完整字段名
*/

public static getPlayerData(): any | null {
const compressed = Laya.LocalStorage.getJSON("p");
if (!compressed) return null;

return {
level: compressed.l,
exp: compressed.e,
gold: compressed.g,
inventory: compressed.i
};
}
}

3. 使用常量定义键名

避免字符串拼写错误:

export class StorageKeys {
// 存档相关
public static readonly SAVE_DATA = "game_save_data";
public static readonly AUTO_SAVE = "auto_save";

// 设置相关
public static readonly SETTINGS = "game_settings";
public static readonly AUDIO_SETTINGS = "audio_settings";

// 玩家相关
public static readonly PLAYER_DATA = "player_data";
public static readonly PLAYER_STATS = "player_stats";

// 缓存相关
public static readonly CACHE_PREFIX = "cache_";
}

// 使用
Laya.LocalStorage.setJSON(StorageKeys.SAVE_DATA, saveData);
Laya.LocalStorage.setItem(StorageKeys.SETTINGS, JSON.stringify(settings));

4. 存储空间检测

检测存储空间是否充足:

@regClass()
export class StorageChecker {
/**
* 检查存储空间
*/

public static checkSpace(): { used: number; remaining: number; percent: number } {
let used = 0;
for (let i = 0; i < Laya.LocalStorage.count; i++) {
const key = localStorage.key(i);
if (key) {
used += localStorage.getItem(key)?.length || 0;
}
}

const total = 5 * 1024 * 1024; // 约 5MB
const remaining = total used;
const percent = Math.floor((used / total) * 100);

return { used, remaining, percent };
}

/**
* 检查是否能存储数据
*/

public static canStore(data: any): boolean {
const json = JSON.stringify(data);
const space = this.checkSpace();
return json.length < space.remaining;
}

/**
* 显示存储信息
*/

public static logInfo(): void {
const space = this.checkSpace();
console.log(`存储使用情况: ${space.percent}%`);
console.log(`已使用: ${(space.used / 1024).toFixed(2)} KB`);
console.log(`剩余: ${(space.remaining / 1024).toFixed(2)} KB`);
}
}

5. 批量操作

@regClass()
export class BatchStorage {
/**
* 批量存储
*/

public static setItems(items: Record<string, string>): void {
for (const key in items) {
Laya.LocalStorage.setItem(key, items[key]);
}
}

/**
* 批量获取
*/

public static getItems(keys: string[]): Record<string, string | null> {
const result: Record<string, string | null> = {};
for (const key of keys) {
result[key] = Laya.LocalStorage.getItem(key);
}
return result;
}

/**
* 批量删除
*/

public static removeItems(keys: string[]): void {
for (const key of keys) {
Laya.LocalStorage.removeItem(key);
}
}

/**
* 按前缀删除
*/

public static removeByPrefix(prefix: string): void {
const keys: string[] = [];
for (let i = 0; i < Laya.LocalStorage.count; i++) {
const key = localStorage.key(i);
if (key && key.startsWith(prefix)) {
keys.push(key);
}
}
this.removeItems(keys);
}
}

// 使用
BatchStorage.setItems({
"key1": "value1",
"key2": "value2",
"key3": "value3"
});

BatchStorage.removeByPrefix("cache_");


最佳实践

1. 合理规划数据结构

建议说明
使用 JSON 存储复杂对象 用 setJSON/getJSON 处理对象
键名使用前缀分类 如 save_, settings_, cache_
避免存储大文件 图片、音频等不应存入 LocalStorage
定期清理过期数据 实现缓存过期机制

2. 错误处理

// ✅ 正确:添加错误处理
public static safeSetJSON(key: string, value: any): boolean {
try {
Laya.LocalStorage.setJSON(key, value);
return true;
} catch (e) {
console.error("存储失败:", e);
// 可能是空间不足或隐私模式
return false;
}
}

public static safeGetJSON<T>(key: string): T | null {
try {
return Laya.LocalStorage.getJSON(key) as T;
} catch (e) {
console.error("读取失败:", e);
return null;
}
}

3. 版本控制

// ✅ 正确:为数据添加版本信息
interface SaveData {
version: string;
timestamp: number;
player: PlayerData;
}

const saveData: SaveData = {
version: "1.0.0",
timestamp: Date.now(),
player: { }
};

4. 数据验证

// ✅ 正确:验证读取的数据
public static loadPlayerData(): PlayerData | null {
const data = Laya.LocalStorage.getJSON("player_data");

// 验证数据完整性
if (!data || typeof data !== "object") return null;
if (!data.level || typeof data.level !== "number") return null;
if (!data.inventory || !Array.isArray(data.inventory)) return null;

return data;
}

5. 默认值处理

// ✅ 正确:提供默认值
export class GameSettings {
private static readonly DEFAULTS = {
musicVolume: 0.8,
soundVolume: 1.0
};

public static getMusicVolume(): number {
const saved = Laya.LocalStorage.getItem("musicVolume");
return saved ? parseFloat(saved) : this.DEFAULTS.musicVolume;
}
}


注意事项

  • 容量限制:浏览器 LocalStorage 通常限制约 5MB,超出会抛出异常
  • 同步操作:存储/读取是同步的,大量数据可能阻塞主线程
  • 隐私模式:浏览器隐私/无痕模式下可能无法使用
  • 字符串存储:setItem/getItem 只能存储字符串,数值需自行转换
  • 跨域隔离:不同域名/端口/协议的存储相互独立
  • 数据安全:存储的数据明文可见,敏感信息需加密
  • 兼容性:部分浏览器在禁用 Cookie 时也会禁用 LocalStorage

  • 相关文档

    • Laya.Pool 使用指南
    • Laya.Handler 使用指南
    • Laya.Loader 使用指南
    赞(0)
    未经允许不得转载:网硕互联帮助中心 » 【Laya】LocalStorage 本地存储
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!