AutoBind.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100
  1. import { Component } from "cc";
  2. import { Logger } from "./Logger";
  3. interface AutoBindOptions {
  4. type?: any; // 组件类型(如 Label)
  5. tooltip?: string;
  6. [key: string]: any;
  7. }
  8. const AutoBindMap = new WeakMap<any, { [key: string]: AutoBindOptions }>();
  9. /**
  10. * 自动绑定与变量同名的子节点。
  11. * 装饰器不传参数(组件)则绑定节点,传了组件则绑定节点上的组件。
  12. * 如果传了组件,则传入的组件一定要和变量类型一致,否则绑定会失败。
  13. * 记得在组件的onLoad函数调用initAutoBindings(this)
  14. 事例:
  15. "@autoBind不传" 绑定是节点
  16. ”@autoBind(Label)“ 绑定的是Label
  17. “@autoBind({type: Label,tooltip: "文本"})” 绑定的是Label
  18. */
  19. export function autoBind(target: any, propertyKey: string): void;
  20. export function autoBind<T extends Component>(componentType: { new(): T }): (target: any, propertyKey: string) => void;
  21. export function autoBind<T extends Component>(options: AutoBindOptions): (target: any, propertyKey: string) => void;
  22. export function autoBind<T extends Component>(
  23. componentTypeOrTarget?: { new(): T } | AutoBindOptions | any,
  24. propertyKey?: string
  25. ) {
  26. if (propertyKey !== undefined) {
  27. //情况1:@autoBind 不传参
  28. const target = componentTypeOrTarget;
  29. if (!AutoBindMap.has(target)) {
  30. AutoBindMap.set(target, {});
  31. }
  32. AutoBindMap.get(target)![propertyKey] = {};
  33. return;
  34. }
  35. //情况2:@autoBind(Label) 或 @autoBind({type: Label})
  36. return function (target: any, propertyKey: string) {
  37. if (!AutoBindMap.has(target)) {
  38. AutoBindMap.set(target, {});
  39. }
  40. const options: AutoBindOptions = typeof componentTypeOrTarget === "function"
  41. ? { type: componentTypeOrTarget }
  42. : componentTypeOrTarget;
  43. AutoBindMap.get(target)![propertyKey] = options;
  44. };
  45. }
  46. /**
  47. * 获取变量的类型
  48. */
  49. function getComponentType(instance: any, propertyKey: string, binding: AutoBindOptions): any {
  50. //1. 优先用 @autoBind 指定的类型
  51. if(binding.type) {
  52. return binding.type;
  53. }
  54. //2. 尝试从变量默认值推断(如 `= null! as Label`)
  55. const defaultValue = instance[propertyKey];
  56. if (defaultValue !== null && defaultValue !== undefined) {
  57. return defaultValue.constructor;
  58. }
  59. //3. 尝试从类型断言推断(如 `= null! as Label`)
  60. //注意:这种方式在运行时无法获取类型信息,需要用户确保正确使用
  61. // 这里我们无法直接获取类型断言信息,只能依赖用户正确设置默认值
  62. //4. 如果仍然没有,返回 null(绑定节点而非组件)
  63. return null;
  64. }
  65. /**
  66. * 在onLoad中调用这个函数然后才会自动绑定。initAutoBindings(this)。
  67. */
  68. export function initAutoBindings(instance: any) {
  69. const bindings = AutoBindMap.get(Object.getPrototypeOf(instance));
  70. if (!bindings) return;
  71. const nameMap = new Map<string, any>();
  72. buildNodeNameMap(instance.node, nameMap);
  73. for (const propertyKey in bindings) {
  74. const binding = bindings[propertyKey];
  75. const node = nameMap.get(propertyKey);
  76. if (!node) {
  77. //Logger.warn(`[AutoBind] 节点未找到: ${propertyKey}`);
  78. continue;
  79. }
  80. const componentType = getComponentType(instance, propertyKey, binding);
  81. if (componentType && !(node instanceof componentType)) { // 关键修改:排除 Node 类型
  82. instance[propertyKey] = node.getComponent(componentType);
  83. //Logger.log(`[AutoBind] 绑定组件 ${propertyKey}:`, componentType.name);
  84. } else {
  85. instance[propertyKey] = node; // 直接绑定节点
  86. // Logger.log(`[AutoBind] 绑定节点 ${propertyKey}`);
  87. }
  88. }
  89. }
  90. function buildNodeNameMap(node: any, nameMap: Map<string, any>) {
  91. nameMap.set(node.name, node);
  92. for (const child of node.children) {
  93. buildNodeNameMap(child, nameMap);
  94. }
  95. }