有什么办法可以同时使用 ES6 私有字段和 Vue 3 的 `reactive` 吗?

Is there any way to use ES6 private fields and Vue 3's `reactive` together?

我有一个 class 使用 ES6 私有字段和 public 吸气剂,我需要使用 Vue 3 的组合 API 来响应。目前,我的设置看起来像这样:

//store.ts
class Store {
  #userName?: string
  get userName() {
    if(this.#userName !== undefined) return this.#userName
    throw new Error('Cannot get userName before it is defined')
  }

  setUserName(newUserName: string) {
    this.#userName = newUserName
  }
}

const store = reactive(new Store())

export { store }

然后通过 provide/inject API 将此商店实例提供给组件,并像这样使用

<template>
  <span> {{ formattedUserName }} </span>
</template>

<script lang="ts">
import { defineComponent, toRefs, inject } from 'vue'

export default defineComponent({
  setup(){
    const store = inject('storeKey')
    const formattedUserName = doSomeThings(store.userName)
    return { formattedUserName  }
})

但是当我尝试这样做时,出现了这个错误: Cannot read private member #userName from an object whose class did not declare it

有人可以解释这是为什么吗?是否有解决办法?我知道 TypeScript 有 private 关键字,但如果可能的话我想使用私有字段,因为它们实际上在运行时强制执行隐私。

谢谢!

编辑:经过更多研究,我找到了问题“为什么”部分的答案。 Vue 3 uses Proxy to track reactivity, which unfortunately does not work with private fields。如果有任何解决办法,我仍然很想知道。

如此简短的回答:没有简单的方法可以同时使用私有字段和代理。现在(2021 年 8 月 10 日)实施的私有字段和代理从根本上是不兼容的。我原来的 post 中的第二个 link 就为什么会这样以及是否应该更改进行了大量讨论,但如果没有新提案,看起来事情就是这样。

以下是我如何组建一家商店

  • 为其成员编译时间隐私
  • 那些保证返回值不是未定义的成员的反应式、只读(也在编译时)getter
  • 也可能有副作用的特定 setter
import {ref, reactive, computed} from 'vue'

class Store {
  private internal_userName: Ref<string | undefined> = ref(undefined)
  readonly userName = computed((): string => {
    if(this.internal_userName.value !== undefined) return this.internal_userName.value
    throw new Error('Cannot access userName before it is defined')
  })

  setUserName(newUserName: string) {
    // do side effects here, such as setting localStorage keys
    this.internal_userName.value = newUserName
  }
}

const store = reactive(new Store())

//do things to export or provide your store here