实现 this 指向
This commit is contained in:
@@ -1,14 +1,184 @@
|
||||
/*
|
||||
* Copyright (c) 2020. bmy
|
||||
*/
|
||||
import Vue from 'vue';
|
||||
|
||||
export function Autowired(params?: any) {
|
||||
return (target: any, propertyKey: string) => {
|
||||
let typeClass = Reflect.getMetadata('design:type', target, propertyKey);
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
value: params ? new typeClass(new params) : new typeClass(),
|
||||
writable: true,
|
||||
configurable: true
|
||||
});
|
||||
}
|
||||
// 1. 定义最宽松的基础类型(兼容所有场景,避开symbol索引签名限制)
|
||||
type AutowiredTarget = {
|
||||
[key: string]: any;
|
||||
};
|
||||
|
||||
// 2. Vue组件实例专属类型(仅声明string/number索引,运行时兼容symbol)
|
||||
interface VueComponentInstance extends AutowiredTarget {
|
||||
created?: (...args: any[]) => Promise<void> | void;
|
||||
$router?: any;
|
||||
$route?: any;
|
||||
$el?: HTMLElement;
|
||||
$data?: Record<string, any>;
|
||||
}
|
||||
|
||||
// 3. Logic实例类型
|
||||
interface LogicInstance extends AutowiredTarget {
|
||||
mergeVueInstance?: <T extends VueComponentInstance>(vueInstance: T) => void;
|
||||
}
|
||||
|
||||
// 4. 通用构造函数类型
|
||||
type Constructor<T = any> = new (...args: any[]) => T;
|
||||
|
||||
/**
|
||||
* 自动注入实例装饰器
|
||||
* 适配所有场景:
|
||||
* - Vue组件的logic属性:自动创建Logic实例并合并Vue所有属性
|
||||
* - Logic类/普通类的属性:直接创建实例并挂载
|
||||
*/
|
||||
export function Autowired(params?: any) {
|
||||
return function (target: AutowiredTarget, propertyKey: string | symbol): void {
|
||||
// 获取属性对应的类构造函数
|
||||
const typeClass = Reflect.getMetadata('design:type', target, propertyKey) as Constructor;
|
||||
|
||||
// 仅对Vue组件的logic属性做特殊处理(运行时判断)
|
||||
if (propertyKey === 'logic') {
|
||||
enhanceVueLogicProperty(target as VueComponentInstance, propertyKey, typeClass);
|
||||
} else {
|
||||
// 所有其他场景(包括Logic类的属性):直接挂载实例
|
||||
mountNormalInstance(target, propertyKey, typeClass, params);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* 挂载普通实例(适配所有非Vue组件场景)
|
||||
*/
|
||||
function mountNormalInstance(
|
||||
target: AutowiredTarget,
|
||||
propertyKey: string | symbol,
|
||||
typeClass: Constructor,
|
||||
params?: any
|
||||
): void {
|
||||
if (!typeClass) {
|
||||
console.warn(`类构造函数不存在,属性${String(propertyKey)}注入失败`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建实例并挂载(统一用defineProperty兼容symbol)
|
||||
const instance = params ? new typeClass(params) : new typeClass();
|
||||
Object.defineProperty(target, propertyKey, {
|
||||
value: instance,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 增强Vue组件的logic属性:创建实例并合并Vue所有属性
|
||||
*/
|
||||
function enhanceVueLogicProperty(
|
||||
target: VueComponentInstance,
|
||||
propertyKey: string | symbol,
|
||||
typeClass: Constructor<LogicInstance>
|
||||
): void {
|
||||
// 保存原始created钩子
|
||||
const originalCreated: (...args: any[]) => Promise<void> | void = target.created || (() => { });
|
||||
|
||||
// 重写created钩子(显式指定this类型)
|
||||
target.created = async function (this: VueComponentInstance): Promise<void> {
|
||||
const vueInstance = this;
|
||||
|
||||
// 防御性判断
|
||||
if (!typeClass) {
|
||||
console.warn(`Logic类构造函数不存在,属性${String(propertyKey)}注入失败`);
|
||||
await originalCreated.call(this);
|
||||
return;
|
||||
}
|
||||
|
||||
// 创建Logic实例
|
||||
const logicInstance = Vue.observable(new typeClass());
|
||||
|
||||
// 核心修复:用Object.defineProperty兼容symbol,避开TS索引检查
|
||||
Object.defineProperty(this, propertyKey, {
|
||||
value: logicInstance,
|
||||
writable: true,
|
||||
configurable: true,
|
||||
enumerable: true
|
||||
});
|
||||
|
||||
// 合并Vue属性(仅当存在mergeVueInstance方法时)
|
||||
if (typeof logicInstance.mergeVueInstance === 'function') {
|
||||
logicInstance.mergeVueInstance(vueInstance);
|
||||
mergeVueProperties(logicInstance, vueInstance);
|
||||
resetLogicConstructor(logicInstance);
|
||||
}
|
||||
|
||||
// 执行原始created钩子
|
||||
await originalCreated.call(this);
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* 合并Vue实例的所有属性到Logic实例
|
||||
*/
|
||||
function mergeVueProperties(logicInstance: LogicInstance, vueInstance: VueComponentInstance): void {
|
||||
const allProps = getAllVueProperties(vueInstance);
|
||||
const excludeProps = new Set<string | symbol>(['constructor', 'prototype', '__proto__', 'mergeVueInstance']);
|
||||
|
||||
allProps.forEach(prop => {
|
||||
if (excludeProps.has(prop)) return;
|
||||
|
||||
const logicPropDesc = Object.getOwnPropertyDescriptor(logicInstance, prop);
|
||||
if (logicPropDesc && !logicPropDesc.configurable) return;
|
||||
|
||||
try {
|
||||
const vuePropDesc =
|
||||
Object.getOwnPropertyDescriptor(Object.getPrototypeOf(vueInstance), prop) ||
|
||||
Object.getOwnPropertyDescriptor(vueInstance, prop);
|
||||
|
||||
if (vuePropDesc) {
|
||||
Object.defineProperty(logicInstance, prop, {
|
||||
...vuePropDesc,
|
||||
enumerable: true,
|
||||
configurable: true
|
||||
});
|
||||
} else {
|
||||
// 兜底:用defineProperty赋值,兼容symbol
|
||||
Object.defineProperty(logicInstance, prop, {
|
||||
value: vueInstance[prop as keyof typeof vueInstance],
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
if (typeof prop === 'string' && !['__proto__', 'constructor'].includes(prop)) {
|
||||
console.warn(`合并属性 ${String(prop)} 失败:`, e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 收集Vue实例的所有属性(包括原型链、Symbol)
|
||||
*/
|
||||
function getAllVueProperties(obj: any): Set<string | symbol> {
|
||||
const props = new Set<string | symbol>();
|
||||
let currentObj: any = obj;
|
||||
|
||||
while (currentObj && currentObj !== Object.prototype) {
|
||||
Object.getOwnPropertyNames(currentObj).forEach(prop => props.add(prop));
|
||||
Object.getOwnPropertySymbols(currentObj).forEach(prop => props.add(prop));
|
||||
currentObj = Object.getPrototypeOf(currentObj);
|
||||
}
|
||||
|
||||
return props;
|
||||
}
|
||||
|
||||
/**
|
||||
* 重置Logic实例的constructor指向
|
||||
*/
|
||||
function resetLogicConstructor(logicInstance: LogicInstance): void {
|
||||
Object.defineProperty(logicInstance, 'constructor', {
|
||||
value: logicInstance.constructor,
|
||||
configurable: true,
|
||||
enumerable: false,
|
||||
writable: true
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user