修飾器
啟用修飾器
經過多年的變更,ES 修飾器終於在 TC39 流程中達到 Stage 3,這意味著它們相當穩定,並且不會像之前的修飾器提案那樣再次經歷重大變更。MobX 已經實現了對這個新的「2022.3/Stage 3」修飾器語法的支援。使用現代修飾器,不再需要呼叫 makeObservable
/ makeAutoObservable
。
2022.3 修飾器在以下環境中受支援
- TypeScript(5.0 及更高版本,請確保**未**啟用
experimentalDecorators
旗標)。範例提交。 - 對於 Babel,請確保插件
proposal-decorators
已啟用,並使用最高版本(目前為2023-05
)。範例提交。
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": false /* or just remove the flag */
}
}
// babel.config.json (or equivalent)
{
"plugins": [
[
"@babel/plugin-proposal-decorators",
{
"version": "2023-05"
}
]
]
}
使用修飾器
import { observable, computed, action } from "mobx"
class Todo {
id = Math.random()
@observable accessor title = ""
@observable accessor finished = false
@action
toggle() {
this.finished = !this.finished
}
}
class TodoList {
@observable accessor todos = []
@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length
}
}
請注意,使用 @observable
時,需使用新的 accessor
關鍵字。它是 2022.3 規範的一部分,如果您想使用現代修飾器,則必須使用它。
使用舊版修飾器
我們不建議程式碼庫使用 TypeScript / Babel 舊版修飾器,因為它們永遠不會成為語言的正式部分,但您仍然可以使用它們。它確實需要特定的轉譯設定。
版本 6 之前的 MobX 鼓勵使用舊版修飾器,並將事物標記為 observable
、computed
和 action
。雖然 MobX 6 建議不要使用這些修飾器(而是使用現代修飾器或 makeObservable
/ makeAutoObservable
),但在目前的 major 版本中仍然可以使用。MobX 7 將移除對舊版修飾器的支援。
import { makeObservable, observable, computed, action } from "mobx"
class Todo {
id = Math.random()
@observable title = ""
@observable finished = false
constructor() {
makeObservable(this)
}
@action
toggle() {
this.finished = !this.finished
}
}
class TodoList {
@observable todos = []
@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length
}
constructor() {
makeObservable(this)
}
}
從舊版修飾器遷移
要從舊版修飾器遷移到現代修飾器,請執行以下步驟
- 從 TypeScript 設定(或 Babel 等效設定)中停用/移除
experimentalDecorators
旗標。 - 從使用修飾器的類別建構函式中移除所有
makeObservable(this)
呼叫。 - 將所有
@observable
(及其變體)實例替換為@observable accessor
修飾器變更/注意事項
MobX 的 2022.3 修飾器與 MobX 5 修飾器非常相似,因此用法基本相同,但有一些注意事項
@observable accessor
修飾器**不可**列舉。accessor
在過去沒有直接的等效物——它們是語言中的一個新概念。我們選擇將它們設為不可列舉的、非自有屬性,以便更好地遵循 ES 語言的精神和accessor
的含義。可列舉性的主要情況似乎都圍繞著序列化和其餘解構。- 關於序列化,隱式序列化所有屬性在 OOP 世界中可能並不理想,因此這似乎不是一個實質性問題(考慮實作
toJSON
或使用serializr
作為可能的替代方案)。 - 關於其餘解構,這在 MobX 中是一種反模式——這樣做會(可能非預期地)觸及所有可觀察物件,並使觀察者過度反應。
- 關於序列化,隱式序列化所有屬性在 OOP 世界中可能並不理想,因此這似乎不是一個實質性問題(考慮實作
@action some_field = () => {}
過去和現在都是有效的用法。但是,舊版修飾器和現代修飾器之間的繼承有所不同。- 在舊版修飾器中,如果父類別有一個由
@action
修飾的欄位,而子類別嘗試覆蓋同一個欄位,則會拋出TypeError: Cannot redefine property
。 - 在現代修飾器中,如果父類別有一個由
@action
修飾的欄位,而子類別嘗試覆蓋同一個欄位,則允許覆蓋該欄位。但是,子類別上的欄位不是動作,除非它也在子類別宣告中使用@action
修飾。
- 在舊版修飾器中,如果父類別有一個由
observer
用作修飾器
將 mobx-react
中的 observer
函式既是函式又是修飾器,可以用於類別組件
@observer
class Timer extends React.Component {
/* ... */
}