太阳rpg编辑器.webp

TypeScript 中的模块系统

1. 模块基础概念

模块是一种组织和复用代码的方式,可以把相关的代码组织在一起,并控制其访问范围。

// 文件: src/systems/HeroSystem.ts
export class HeroSystem {
    createHero() { /* ... */ }
    levelUp() { /* ... */ }
}

// 文件: src/App.ts
import { HeroSystem } from "./systems/HeroSystem";

let heroSystem = new HeroSystem();

2. 导出(Export)

有几种不同的导出方式:

// 1. 默认导出 (src/systems/ItemSystem.ts)
export default class ItemSystem {
    createItem() { /* ... */ }
    removeItem() { /* ... */ }
}

// 2. 命名导出 (src/config/GameConfig.ts)
export const HERO_STATS = {
    baseHealth: 500,
    baseMana: 300
};

export function calculateDamage() {
    // ...
}

// 3. 多个导出 (src/utils/GameUtils.ts)
export class MathUtils {
    static random(min: number, max: number) { /* ... */ }
}

export class StringUtils {
    static format(text: string, ...args: any[]) { /* ... */ }
}

3. 导入(Import)

对应的导入方式:

// 1. 导入默认导出
import ItemSystem from "./systems/ItemSystem";

// 2. 导入命名导出
import { HERO_STATS, calculateDamage } from "./config/GameConfig";

// 3. 导入多个内容
import { MathUtils, StringUtils } from "./utils/GameUtils";

// 4. 重命名导入
import { HERO_STATS as HeroConfig } from "./config/GameConfig";

// 5. 导入所有内容
import * as Utils from "./utils/GameUtils";

4. 实际项目示例

让我们看看这个项目中的一些实际例子:

// src/solar/common/GlobalVars.ts
export default class GlobalVars {
    static isDebug: boolean;
    
    static init(debug: boolean) {
        this.isDebug = debug;
    }
}

// src/systems/AbilitySystem.ts
import GlobalVars from "../solar/common/GlobalVars";
import { DamageUtils } from "../utils/CombatUtils";

export class AbilitySystem {
    castSpell(caster: unit, target: unit) {
        // 使用导入的工具
        if (GlobalVars.isDebug) {
            console.log("施放技能");
        }
        
        let damage = DamageUtils.calculateSpellDamage(caster, target);
        // ...
    }
}

5. 模块组织示例

// src/config/index.ts
// 集中导出配置
export * from "./HeroConfig";
export * from "./ItemConfig";
export * from "./SpellConfig";

// src/systems/index.ts
// 集中导出系统
export * from "./HeroSystem";
export * from "./ItemSystem";
export * from "./CombatSystem";

// src/App.ts
// 统一导入
import { 
    HeroSystem,
    ItemSystem,
    CombatSystem 
} from "./systems";

import {
    HERO_CONFIG,
    ITEM_CONFIG,
    SPELL_CONFIG
} from "./config";

6. 模块的优势

  1. 代码组织
// 相关功能组织在一起
// src/systems/CombatSystem.ts
export class CombatSystem {
    private damageCalculator: DamageCalculator;
    private effectManager: EffectManager;
    
    constructor() {
        this.damageCalculator = new DamageCalculator();
        this.effectManager = new EffectManager();
    }
}
  1. 代码复用
// src/utils/CommonUtils.ts
export function delay(seconds: number): Promise<void> {
    return new Promise(resolve => {
        TimerStart(CreateTimer(), seconds, false, () => {
            DestroyTimer(GetExpiredTimer());
            resolve();
        });
    });
}

// 在其他文件中复用
import { delay } from "../utils/CommonUtils";

async function castDelayedSpell() {
    await delay(1.0);
    // 继续施法逻辑
}
  1. 依赖管理

    // src/systems/SpellSystem.ts
    import { SPELL_CONFIG } from "../config";
    import { EffectUtils } from "../utils/EffectUtils";
    import { CombatUtils } from "../utils/CombatUtils";
    
    export class SpellSystem {
     castFireball(caster: unit, target: unit) {
         let damage = SPELL_CONFIG.FIREBALL.damage;
         CombatUtils.dealDamage(caster, target, damage);
         EffectUtils.playSpellEffect("fireball", target);
     }
    }

7. 最佳实践

  1. 相关功能组织在一起

    // src/systems/HeroSystem/
    // index.ts
    export * from "./HeroCreator";
    export * from "./HeroStats";
    export * from "./HeroAbilities";
    
    // HeroCreator.ts
    export class HeroCreator { /* ... */ }
    
    // HeroStats.ts
    export class HeroStats { /* ... */ }
    
    // HeroAbilities.ts
    export class HeroAbilities { /* ... */ }
  2. 清晰的导入导出

    // 明确导入需要的内容
    import { HeroCreator, HeroStats } from "./systems/HeroSystem";
    import { ItemManager } from "./systems/ItemSystem";
    import { GAME_CONFIG } from "./config";
  3. 避免循环依赖

    // 好的做法:明确的依赖方向
    // HeroSystem 依赖 ItemSystem
    import { ItemSystem } from "./ItemSystem";
    
    // ItemSystem 不依赖 HeroSystem
    export class ItemSystem {
     // 通过参数传递 hero,而不是导入 HeroSystem
     useItem(hero: unit, item: item) { /* ... */ }
    }

默认导出(default export)和命名导出(named export)的区别

1. 默认导出 (Default Export)

  • 每个模块只能有一个默认导出
  • 导入时可以使用任意名称
  • 通常用于导出模块的主要功能

例如在 App.ts 中的默认导出:

export default class App {


    start() {

        //先打印一个文本 让我们知道已运行到了TS入口代码
        // DisplayTimedTextToPlayer(GetLocalPlayer(), 0, 0, 60, 'TS:App.start!');

        //状态初始化
        StateConfigInit();
        StateInit();
        abilitysss()

        //测试区
        new AppTest().start()
        // if(idAndNum&&idAndNum.length>1&&idAndNum[0]&&idAndNum[1]){
        //     material[idAndNum[0]] = parseInt(idAndNum[1])
        // }
    }

}

导入默认导出:

import App from "./App";  // 可以使用任意名称
import MyApp from "./App"; // 同样有效

2. 命名导出 (Named Export)

  • 一个模块可以有多个命名导出
  • 导入时必须使用相同的名称(除非使用 as 重命名)
  • 通常用于导出多个相关但独立的功能

例如:

// 命名导出
export const HERO_STATS = {
    baseHealth: 500,
    baseMana: 300
};

export function calculateDamage() {
    // ...
}

export class HeroSystem {
    // ...
}

导入命名导出:

import { HERO_STATS, calculateDamage, HeroSystem } from "./HeroSystem";
// 使用 as 重命名
import { HERO_STATS as HeroConfig } from "./HeroSystem";

3. 使用场景

使用默认导出的情况:

  • 模块主要提供一个功能
  • 模块代表一个类/组件
  • 希望导入时有更灵活的命名

例如在你的代码中:

export default class GameStart {

  static is: unit
  static shangxian: number
  static i: number = 0;
  static j: number = 0;
  static k: number = 0;
  static l: number = 0;
  static z1: number = 0;
  static z2: number = 0;
  static z3: number = 0;
  static z4: number = 0;
  static addmoster = 0
  static chuangkou: string[] = [];
  static zhuangbei: string[] = [
    "a204",
    "a205",
    "a206",
    "a207",
    "a208",
    "a209",
    "a20a",
    "a20b",
    "a20c",
    "a20d",
    "a20e",
  ]
  static config: {
    start_text: string//初始文本
    start_CreateUnitType: string//初始创建单位类型
    pen_gives_bounty: number[]//获得奖励

  } = {
      start_text: "",//初始文本
      start_CreateUnitType: "jz01",//初始创建单位类型
      pen_gives_bounty: [],
    }
  constructor() {
    this.Passive_trigger();
  };

使用命名导出的情况:

  • 模块提供多个相关但独立的功能
  • 需要导出常量、工具函数等
  • 希望强制使用特定的命名

4. 最佳实践

  1. 一个文件通常只应该有一个默认导出
  2. 相关的功能可以使用命名导出组织在一起
  3. 可以同时使用默认导出和命名导出,但要注意保持代码清晰

例如:

// 命名导出工具函数
export function delay(ms: number) {
    return new Promise(resolve => setTimeout(resolve, ms));
}

// 默认导出主类
export default class GameSystem {
    // ...
}

导入时:

import GameSystem, { delay } from "./GameSystem";

这样的组织方式使代码结构更清晰,更易于维护和使用。


是的,在同一个 TypeScript 文件(模块)中只能有一个默认导出(export default)。让我用例子来说明:

1. 正确的用法

// 正确:一个文件只有一个默认导出
export default class Hero {
    // ...
}

// 可以有多个命名导出
export const HERO_STATS = { /* ... */ };
export function calculateDamage() { /* ... */ };
export class Equipment { /* ... */ };

2. 错误的用法

// ❌ 错误:不能有多个默认导出
export default class Hero {
    // ...
}

export default class Weapon {  // 这会导致编译错误
    // ...
}

3. 实际项目中的例子

从你的项目代码中可以看到正确的使用方式:

// 正确:只有一个默认导出
export default class App {
    start() {
        StateConfigInit();
        StateInit();
        abilitysss()
        new AppTest().start()
    }
}

// 其他代码...

4. 如果需要导出多个内容,可以:

  1. 使用命名导出

    // hero/index.ts
    export class Hero { /* ... */ }
    export class Weapon { /* ... */ }
    export class Armor { /* ... */ }
  2. 将多个相关项组合成一个对象或类后默认导出

    // hero/index.ts
    class Hero { /* ... */ }
    class Weapon { /* ... */ }
    class Armor { /* ... */ }
    
    // 将相关内容组合后默认导出
    export default {
     Hero,
     Weapon,
     Armor
    }
  3. 使用命名导出和默认导出的组合

    // hero/index.ts
    export class Weapon { /* ... */ }
    export class Armor { /* ... */ }
    
    // 主要的类作为默认导出
    export default class Hero { /* ... */ }

5. 导入方式的区别

// 导入默认导出
import Hero from './hero';

// 导入命名导出
import { Weapon, Armor } from './hero';

// 同时导入默认导出和命名导出
import Hero, { Weapon, Armor } from './hero';

// 导入组合的默认导出
import HeroSystem from './hero';
const { Hero, Weapon, Armor } = HeroSystem;

记住:

  1. 一个文件只能有一个 export default
  2. 一个文件可以有任意数量的命名导出(export
  3. 默认导出和命名导出可以共存在同一个文件中
  4. 选择使用默认导出还是命名导出应该基于模块的用途和组织结构