Skip to content

装饰器

@Computed 装饰器

When "reactive" uses "class" and "computed", the "computed" attribute will not Reactive

这个 issue 说明了为什么不能直接在 class 的属性上赋值 computed 对象。因为 computed 初始化的时机是非常早的,此时 this 还没有变成响应式对象。 所以 computed 不起作用。

可选的一种方案就是在 this 初始化之后,手动调用 init 方法,在 init 方法中在初始化 computed 相关的属性。 如果是使用@PostConstruct 装饰器,此时也不需要在业务代码中手动调用 init 方法,init 方法会自动执行。

另一种更好的方案就是使用 getter 属性再配合@Computed 装饰器,这样就既能享受 computed 带来的相响应式能力,又能解决 getter 方法没有缓存的问题。

MarkRaw 装饰器

因为本库默认会将整个实例对象转化为响应式对象,但有时候也会期望指定特定的属性不参与响应式追踪。

所以这里的本意是实现一个@MarkRaw 装饰器,作用是使用markRaw函数处理属性值,从而该属性就不再是响应式的。

ts
class DemoService {
  @MarkRaw
  public bigObject = SomeBigObject;
}

最终决定还是放弃了这个装饰器,有两个原因:

  1. 可以直接使用markRaw函数代替
ts
class DemoService {
  public bigObject = markRaw(SomeBigObject);
}
  1. 实现@MarkRaw 的成本有一点点高,而且必须和本库的container.onActivation强制耦合。

在我尝试的实现方案中,我发现比较重要的一点是属性装饰器只能在类的原型上定义属性描述符。

Object.defineProperty(prototype, property, descriptor)

这样有一个问题就是当我们访问/设置实例属性时,并不会触发原型上的属性描述符。

那最终只能想办法在实例对象上定义属性描述符。

Object.defineProperty(instance, property, descriptor)

但是属性装饰器在执行阶段并不能获取到实例对象,因为此时还没有实例化对象。

目前能想到的实现方案就是在@MarkRaw 装饰器中收集有哪些属性需要使用 markRaw 函数进行处理。 然后在container.onActivation中获取到实例对象后,遍历之前收集到的所有属性,针对每个属性创建属性描述符。

在实现可行性方面是没有问题的,关键在于这个装饰器只能在本库的上下文中使用,并不能在任意的类中使用,并不具有通用性。

目前遇到需要使用 markRaw 的一个场景就是引用第三方 sdk 时,这些复杂的 sdk 最好使用 markRaw 函数进行处理后再使用。