有没有办法将 ES6 私有字段和 Vue 3 的 `reactive` 一起使用?

Con*_*ley 10 javascript vue.js es6-class vuejs3 vue-composition-api

我有一个使用 ES6 私有字段和公共 getter 的类,我需要使用 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 }
Run Code Online (Sandbox Code Playgroud)

然后通过提供/注入 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  }
})

Run Code Online (Sandbox Code Playgroud)

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

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

谢谢!

编辑:经过更多研究,我找到了问题“为什么”部分的答案。Vue 3 使用 Proxy 来跟踪反应性不幸的是它不适用于私有字段。如果有任何解决办法,我仍然很想知道。

Con*_*ley 7

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

这是我如何组建一家商店的方法

  • 为其成员编译时隐私
  • 对于保证返回值不是未定义的成员的反应式、只读(也在编译时)getter
  • 也可能产生副作用的特定设置器
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
Run Code Online (Sandbox Code Playgroud)