【Laya】URL 路径管理
Laya.URL 路径管理摘要 Laya.URL 是 LayaAir 引擎提供的 URL 路径管理工具,主要用于资源路径格式化和版本控制。核心功能包括: 路径管理: 设置基础路径(basePath)实现 CDN/远程资源加载 路径拼接(join)和规范化(normalize) 版本控制: 通过 customFormat 实现资源版本管理 自动添加版本号解决缓存问题(如:image.png → im
·
Laya.URL 路径管理使用指南
简介
Laya.URL 是 LayaAir 引擎提供的 URL 路径格式化和版本管理类。引擎加载资源时会自动调用 formatURL 函数格式化 URL 路径。
适用场景:
- 设置资源基础路径(CDN/远程服务器)
- 资源版本管理(缓存更新)
- URL 路径映射和重定向
- 小游戏平台资源扩展名转换
工作原理:
原始URL → formatURL → basePath拼接 → 版本号插入 → urlMapping映射 → customFormat处理 → 最终URL
核心优势:
| 优势 | 说明 |
|---|---|
| 统一管理 | 集中管理所有资源路径 |
| 版本控制 | 自动添加版本号,解决缓存问题 |
| 环境切换 | 轻松切换开发/生产环境 |
| 路径映射 | 灵活映射资源到不同位置 |
目录
API 参考
静态属性
| 属性 | 类型 | 说明 |
|---|---|---|
basePath |
string |
基础路径,默认为当前网页路径 |
basePaths |
Record<string, string> |
扩展基础路径映射表,如 {"aa/": "http://abc.com/"} |
version |
Record<string, string> |
URL 版本映射表,格式化后生成 文件名-版本号.扩展名 |
urlMapping |
Record<string, string> |
URL 映射表,将 URL 映射到不同路径 |
customFormat |
(url: string) => string |
自定义格式化函数 |
rootPath |
string |
以 ~/ 开头的 URL 路径映射(不推荐,应使用 basePaths) |
实例属性
| 属性 | 类型 | 说明 |
|---|---|---|
url |
string |
格式化后的地址(只读) |
path |
string |
地址的文件夹路径,不包括文件名(只读) |
静态方法
| 方法 | 参数 | 返回值 | 说明 |
|---|---|---|---|
formatURL(url: string, base?: string) |
URL地址, 基础路径(可选) | string |
格式化 URL,合并 basePath 并处理版本 |
normalize(url: string) |
URL地址 | string |
格式化相对路径,处理 . 和 .. |
join(base: string, path: string) |
基础路径, 相对路径 | string |
组合相对路径并格式化 |
getPath(url: string) |
URL地址 | string |
获取文件夹路径(不包括文件名,末尾有 /) |
getFileName(url: string) |
URL地址 | string |
获取文件名 |
getURLVerion(url: string) |
URL地址 | string |
获取 URL 版本字符 |
overrideExtension(originalExts: string[], targetExt: string, miniGameOnly?: boolean) |
原扩展名数组, 目标扩展名, 是否仅小游戏 | void |
覆盖文件扩展名 |
initMiniGameExtensionOverrides() |
无 | void |
初始化小游戏文件扩展名覆盖 |
基础用法
1. 设置基础路径
设置所有资源的公共基础路径:
// 设置 CDN 基础路径
Laya.URL.basePath = "https://cdn.example.com/assets/";
// 之后加载资源时会自动拼接
Laya.loader.load("images/player.png"); // 实际加载: https://cdn.example.com/assets/images/player.png
2. URL 版本管理
为资源添加版本号,解决缓存问题。由于 version 是只读属性,推荐使用 customFormat:
// 使用 customFormat 实现版本管理
const versionMap: Record<string, string> = {
"images/player.png": "v1.0.0",
"data/config.json": "v2.3.1"
};
Laya.URL.customFormat = (url: string): string => {
const version = versionMap[url];
if (version) {
// 插入版本号:images/player.png → images/player-v1.0.0.png
const dotIndex = url.lastIndexOf(".");
if (dotIndex > 0) {
return url.substring(0, dotIndex) + "-" + version + url.substring(dotIndex);
}
}
return url;
};
// 格式化后的 URL
// images/player.png → images/player-v1.0.0.png
// data/config.json → data/config-v2.3.1.json
3. 使用 URL 实例
创建 URL 实例获取格式化后的地址:
const url = new Laya.URL("resources/images/bg.jpg");
console.log(url.url); // "resources/images/bg.jpg" (格式化后的地址)
console.log(url.path); // "resources/images/" (文件夹路径)
4. 路径拼接
组合多个路径段:
// 拼接路径
const fullPath = Laya.URL.join("resources", "images/player.png");
// 结果: "resources/images/player.png"
5. 规范化路径
处理包含 . 和 .. 的路径:
// 规范化路径
const normalized = Laya.URL.normalize("a/b/../c/./d.png");
// 结果: "a/c/d.png"
实用示例
示例1: 环境路径配置
根据不同环境配置资源路径:
@regClass()
export class ResourceConfig {
/**
* 初始化资源路径配置
*/
public static init(): void {
// 根据环境变量判断
const isDev = import.meta.env.DEV;
if (isDev) {
// 开发环境:使用本地资源
Laya.URL.basePath = "h5/";
} else {
// 生产环境:使用 CDN
Laya.URL.basePath = "https://cdn.example.com/game-assets/v1.0/";
}
console.log("资源基础路径:", Laya.URL.basePath);
}
/**
* 切换到 CDN 备份路径
*/
public static switchToBackupCDN(): void {
Laya.URL.basePath = "https://backup-cdn.example.com/game-assets/";
console.log("已切换到备份 CDN");
}
}
// 游戏启动时初始化
ResourceConfig.init();
示例2: 资源版本管理器
自动化资源版本管理,使用 customFormat 实现:
@regClass()
export class VersionManager {
private static readonly VERSION_KEY = "resource_version";
private static versionMap: Record<string, string> = {};
/**
* 初始化版本管理器
*/
public static init(): void {
// 加载保存的版本信息
this.loadVersion();
// 设置自定义格式化
const originalFormat = Laya.URL.customFormat;
Laya.URL.customFormat = (url: string): string => {
// 应用版本号
const version = this.versionMap[url];
if (version) {
const dotIndex = url.lastIndexOf(".");
if (dotIndex > 0) {
url = url.substring(0, dotIndex) + "-" + version + url.substring(dotIndex);
}
}
// 调用原始格式化
return originalFormat ? originalFormat(url) : url;
};
}
/**
* 设置资源版本
*/
public static setVersion(versionMap: Record<string, string>): void {
this.versionMap = {
...this.versionMap,
...versionMap
};
// 保存版本信息到本地
Laya.LocalStorage.setJSON(this.VERSION_KEY, this.versionMap);
}
/**
* 加载保存的版本信息
*/
public static loadVersion(): void {
const saved = Laya.LocalStorage.getJSON(this.VERSION_KEY);
if (saved) {
this.versionMap = saved;
console.log("已加载资源版本:", saved);
}
}
/**
* 清除版本缓存(用于开发调试)
*/
public static clearVersion(): void {
this.versionMap = {};
Laya.LocalStorage.setItem(this.VERSION_KEY, "");
console.log("已清除资源版本缓存");
}
/**
* 更新单个资源版本
*/
public static updateResourceVersion(path: string, version: string): void {
this.versionMap[path] = version;
Laya.LocalStorage.setJSON(this.VERSION_KEY, this.versionMap);
}
}
// 使用示例
VersionManager.init();
VersionManager.setVersion({
"assets/atlas/game.atlas": "20241201",
"assets/sounds/bgm.mp3": "v2.0"
});
示例3: 多路径映射管理
管理不同类型的资源路径:
@regClass()
export class PathManager {
/**
* 初始化多路径映射
*/
public static init(): void {
// 图片资源使用本地路径
Laya.URL.basePaths["images/"] = "assets/images/";
// 音频资源使用 CDN
Laya.URL.basePaths["audio/"] = "https://cdn.example.com/audio/";
// 3D 资源使用单独的服务器
Laya.URL.basePaths["models/"] = "https://models.example.com/";
// 远程配置文件
Laya.URL.basePaths["config/"] = "https://config.example.com/";
}
/**
* 动态添加路径映射
*/
public static addPath(alias: string, realPath: string): void {
Laya.URL.basePaths[alias] = realPath;
}
/**
* 移除路径映射
*/
public static removePath(alias: string): void {
delete Laya.URL.basePaths[alias];
}
}
// 初始化路径管理器
PathManager.init();
// 之后加载资源时会自动映射
// "images/player.png" → "assets/images/player.png"
// "audio/bgm.mp3" → "https://cdn.example.com/audio/bgm.mp3"
示例4: 自定义 URL 格式化
实现自定义的 URL 格式化逻辑:
@regClass()
export class URLFormatter {
/**
* 初始化自定义格式化
*/
public static init(): void {
// 设置自定义格式化函数
Laya.URL.customFormat = (url: string): string => {
// 添加时间戳防止缓存(仅开发环境)
if (import.meta.env.DEV) {
const separator = url.includes("?") ? "&" : "?";
return `${url}${separator}t=${Date.now()}`;
}
// 生产环境:压缩图片路径
if (url.endsWith(".png")) {
return url.replace(".png", ".webp");
}
return url;
};
}
/**
* 设置语言路径前缀
*/
public static setLanguagePrefix(lang: string): void {
const originalFormat = Laya.URL.customFormat;
Laya.URL.customFormat = (url: string): string => {
// 为 i18n 资源添加语言前缀
if (url.startsWith("i18n/")) {
url = url.replace("i18n/", `i18n/${lang}/`);
}
// 调用原始格式化
return originalFormat ? originalFormat(url) : url;
};
}
}
// 初始化
URLFormatter.init();
示例5: 完整的资源管理器
综合管理资源路径、版本和映射:
@regClass()
export class ResourceManager {
private static initialized = false;
private static versionMap: Record<string, string> = {};
/**
* 初始化资源管理器
*/
public static async init(): Promise<void> {
if (this.initialized) return;
// 1. 设置基础路径
this.setupBasePath();
// 2. 设置路径映射
this.setupPathMapping();
// 3. 加载版本信息
this.loadVersions();
// 4. 设置自定义格式化
this.setupCustomFormat();
this.initialized = true;
console.log("资源管理器初始化完成");
}
/**
* 设置基础路径
*/
private static setupBasePath(): void {
const env = this.getEnvironment();
switch (env) {
case "development":
Laya.URL.basePath = "h5/";
break;
case "staging":
Laya.URL.basePath = "https://staging-cdn.example.com/";
break;
case "production":
Laya.URL.basePath = "https://cdn.example.com/";
break;
}
}
/**
* 设置路径映射
*/
private static setupPathMapping(): void {
Laya.URL.basePaths = {
"assets/": "assets/",
"res/": "resources/",
"ui/": "assets/ui/",
"audio/": "assets/audio/",
"scenes/": "assets/scenes/",
"prefabs/": "assets/prefabs/"
};
}
/**
* 加载版本信息
*/
private static loadVersions(): void {
// 从本地加载或从服务器获取
const localVersions = Laya.LocalStorage.getJSON("resource_versions");
if (localVersions) {
this.versionMap = localVersions;
}
}
/**
* 设置自定义格式化
*/
private static setupCustomFormat(): void {
Laya.URL.customFormat = (url: string): string => {
// 应用版本号
const version = this.versionMap[url];
if (version) {
const dotIndex = url.lastIndexOf(".");
if (dotIndex > 0) {
url = url.substring(0, dotIndex) + "-" + version + url.substring(dotIndex);
}
}
// 处理空格
url = url.replace(/\s/g, "%20");
// 处理中文字符
url = encodeURI(url);
return url;
};
}
/**
* 获取当前环境
*/
private static getEnvironment(): string {
// 根据域名或配置判断环境
const hostname = window.location.hostname;
if (hostname === "localhost" || hostname === "127.0.0.1") {
return "development";
} else if (hostname.includes("staging")) {
return "staging";
} else {
return "production";
}
}
/**
* 格式化 URL(供外部使用)
*/
public static formatURL(url: string): string {
return Laya.URL.formatURL(url);
}
/**
* 获取资源路径
*/
public static getPath(url: string): string {
return Laya.URL.getPath(url);
}
}
// 使用示例
await ResourceManager.init();
// 加载资源时会自动应用所有配置
Laya.loader.load("ui/main.json");
Laya.loader.load("audio/bgm.mp3");
高级技巧
1. URL 映射表
使用 urlMapping 进行资源重定向:
// 设置 URL 映射
Laya.URL.urlMapping = {
"old-assets/player.png": "new-assets/sprites/player.png",
"old-ui/main.json": "new-ui/main-v2.json"
};
// 旧路径会自动映射到新路径
Laya.loader.load("old-assets/player.png"); // 实际加载: new-assets/sprites/player.png
2. 小游戏扩展名处理
小游戏平台可能需要特定的文件扩展名:
// 微信小游戏:将 .png 转换为 .jpg
Laya.URL.overrideExtension([".png"], ".jpg", true);
// 小游戏平台初始化
Laya.URL.initMiniGameExtensionOverrides();
3. 路径分析工具
@regClass()
export class PathAnalyzer {
/**
* 解析 URL 各部分
*/
public static analyze(url: string): {
fullPath: string;
path: string;
fileName: string;
extension: string;
version: string;
} {
const path = Laya.URL.getPath(url);
const fileName = Laya.URL.getFileName(url);
const version = Laya.URL.getURLVerion(url);
const extension = fileName.substring(fileName.lastIndexOf("."));
return {
fullPath: Laya.URL.formatURL(url),
path,
fileName,
extension,
version
};
}
/**
* 检查是否为远程资源
*/
public static isRemote(url: string): boolean {
return url.startsWith("http://") || url.startsWith("https://");
}
/**
* 检查是否为绝对路径
*/
public static isAbsolute(url: string): boolean {
return url.startsWith("/") || this.isRemote(url);
}
}
// 使用
const info = PathAnalyzer.analyze("images/player.png");
console.log(info);
// {
// fullPath: "https://cdn.example.com/images/player.png",
// path: "images/",
// fileName: "player.png",
// extension: ".png",
// version: ""
// }
4. 动态 CDN 切换
根据网络状况动态切换 CDN:
@regClass()
export class CDNSwitcher {
private static cdnList = [
"https://cdn1.example.com/",
"https://cdn2.example.com/",
"https://backup.example.com/"
];
private static currentIndex = 0;
/**
* 测试 CDN 可用性
*/
public static async testCDN(cdnUrl: string): Promise<boolean> {
try {
const response = await fetch(`${cdnUrl}health.json`, {
method: "HEAD",
cache: "no-cache"
});
return response.ok;
} catch {
return false;
}
}
/**
* 选择最快的 CDN
*/
public static async selectBestCDN(): Promise<void> {
for (let i = 0; i < this.cdnList.length; i++) {
const isAvailable = await this.testCDN(this.cdnList[i]);
if (isAvailable) {
this.currentIndex = i;
Laya.URL.basePath = this.cdnList[i];
console.log("已切换到 CDN:", this.cdnList[i]);
return;
}
}
// 所有 CDN 都不可用,使用本地资源
Laya.URL.basePath = "h5/";
console.warn("所有 CDN 不可用,使用本地资源");
}
/**
* 切换到下一个 CDN
*/
public static switchCDN(): void {
this.currentIndex = (this.currentIndex + 1) % this.cdnList.length;
Laya.URL.basePath = this.cdnList[this.currentIndex];
console.log("已切换到 CDN:", this.cdnList[this.currentIndex]);
}
}
5. 资源预加载路径
@regClass()
export class PreloadPathConfig {
/**
* 生成预加载资源列表
*/
public static getPreloadList(): string[] {
const basePath = Laya.URL.basePath;
return [
// 核心 UI
Laya.URL.formatURL("ui/loading.json"),
// 必要图集
Laya.URL.formatURL("assets/atlas/common.atlas"),
// 启动音频
Laya.URL.formatURL("audio/bgm.mp3"),
// 配置文件
Laya.URL.formatURL("config/game.json")
];
}
/**
* 预加载所有资源
*/
public static preload(): Promise<void> {
const resources = this.getPreloadList();
return new Promise((resolve, reject) => {
Laya.loader.load(resources, {
// 完成回调
complete: () => {
console.log("预加载完成");
resolve();
},
// 进度回调
progress: (progress: number) => {
console.log(`预加载进度: ${Math.floor(progress * 100)}%`);
},
// 错误回调
error: (err: any) => {
console.error("预加载失败:", err);
reject(err);
}
} as any);
});
}
}
最佳实践
1. 环境分离
// ✅ 正确:根据环境配置
const config = {
dev: { basePath: "h5/", cdn: "" },
prod: { basePath: "", cdn: "https://cdn.example.com/" }
};
const current = config[import.meta.env.MODE];
Laya.URL.basePath = current.basePath;
// ❌ 错误:硬编码路径
Laya.URL.basePath = "https://cdn.example.com/";
2. 版本号命名规范
| 规范 | 示例 | 说明 |
|---|---|---|
| 语义化版本 | v1.0.0 |
适合正式发布版本 |
| 日期版本 | 20241201 |
适合每日构建 |
| 哈希版本 | a3f2c1 |
适合 CI/CD 自动构建 |
// ✅ 正确:使用 customFormat 实现有意义的版本号
const versionMap: Record<string, string> = {
"game.atlas": "v1.2.0",
"config.json": "20241201"
};
Laya.URL.customFormat = (url: string): string => {
const version = versionMap[url];
if (version) {
const dotIndex = url.lastIndexOf(".");
if (dotIndex > 0) {
return url.substring(0, dotIndex) + "-" + version + url.substring(dotIndex);
}
}
return url;
};
// ❌ 错误:尝试赋值给只读属性
// Laya.URL.version = { // Error: Cannot assign to 'version' because it is read-only
// "game.atlas": "v1.2.0"
// };
3. 路径常量管理
// ✅ 正确:使用常量管理路径
export class ResourcePaths {
public static readonly UI = "ui/";
public static readonly IMAGES = "assets/images/";
public static readonly AUDIO = "assets/audio/";
public static readonly SCENES = "assets/scenes/";
}
// 使用
Laya.loader.load(ResourcePaths.UI + "main.json");
4. 避免频繁修改 basePath
// ✅ 正确:使用 basePaths 处理多路径
Laya.URL.basePaths = {
"images/": "local/images/",
"audio/": "https://cdn.example.com/audio/"
};
// ❌ 错误:频繁修改 basePath
Laya.URL.basePath = "local/images/";
// ... 加载图片
Laya.URL.basePath = "https://cdn.example.com/audio/";
// ... 加载音频
5. 调试 URL 格式化
// ✅ 正确:调试时打印格式化结果
const originalFormatURL = Laya.URL.formatURL;
Laya.URL.formatURL = function(url: string, base?: string): string {
const result = originalFormatURL.call(this, url, base);
console.log(`[URL] ${url} → ${result}`);
return result;
};
注意事项
- basePath 自动拼接:引擎加载资源时自动调用
formatURL,无需手动拼接 - 版本号格式:版本号会插入到文件名和扩展名之间,如
file-v1.0.0.png - 路径映射优先级:
basePaths>basePath,具体路径映射优先于通用基础路径 - 自定义格式化:
customFormat会覆盖默认格式化逻辑,谨慎使用 - 小游戏限制:小游戏平台对资源路径有特殊要求,使用
initMiniGameExtensionOverrides适配 - CDN 缓存:版本号更新后,需要确保 CDN 已部署对应版本资源
- 相对路径:使用
./或../时,推荐用normalize规范化
相关文档
更多推荐
所有评论(0)