有什么办法可以同时使用 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
我有一个 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