日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

[MobX State Tree数据组件化开发][3]:选择正确的types.xxx

發(fā)布時(shí)間:2023/12/6 编程问答 39 豆豆
生活随笔 收集整理的這篇文章主要介紹了 [MobX State Tree数据组件化开发][3]:选择正确的types.xxx 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

?系列文章目錄?

定義Model時(shí),需要正確地定義props中各字段的類(lèi)型。本文將對(duì)MST提供的各種類(lèi)型以及類(lèi)型的工廠方法進(jìn)行簡(jiǎn)單的介紹,方便同學(xué)們?cè)诙xprops時(shí)挑選正確的類(lèi)型。

前提

定義props之前,有一個(gè)前提是,你已經(jīng)明確地知道這個(gè)Model中狀態(tài)的數(shù)據(jù)類(lèi)型。

如果Model用于存放由后端API返回的數(shù)據(jù),那么一定要和后端確認(rèn)返回值在所有情況下的類(lèi)型。比如,某個(gè)字段在沒(méi)有值的時(shí)候你以為會(huì)給一個(gè)'',而后端卻給了個(gè)null;或者某個(gè)數(shù)組你以為會(huì)給你一個(gè)空數(shù)組[],他又甩你一個(gè)null。如果你不打算自己編寫(xiě)一個(gè)標(biāo)準(zhǔn)化數(shù)據(jù)的方法,那一定要和數(shù)據(jù)的提供方確定這些細(xì)節(jié)。

基礎(chǔ)類(lèi)型

types.string

定義一個(gè)字符串類(lèi)型字段。

types.number

定義一個(gè)數(shù)值類(lèi)型字段。

types.boolean

定義一個(gè)布爾類(lèi)型字段。

types.integer

定義一個(gè)整數(shù)類(lèi)型字段。

注意,即使是TypeScript中也沒(méi)有“整數(shù)”這個(gè)類(lèi)型,在編碼時(shí),傳入一個(gè)帶小數(shù)的值TypeScript也無(wú)法發(fā)現(xiàn)其中的類(lèi)型錯(cuò)誤。如無(wú)必要,請(qǐng)使用types.number。

types.Date

定義一個(gè)日期類(lèi)型字段。

這個(gè)類(lèi)型存儲(chǔ)的值是標(biāo)準(zhǔn)的Date對(duì)象。在設(shè)置值時(shí),可以選擇傳入數(shù)值類(lèi)型的時(shí)間戳或者Date對(duì)象。

export const Model = types.model({date: types.Date }).actions(self => ({setDate (val: Date | number) {self.date = date;}})); 復(fù)制代碼

types.null

定義一個(gè)值為null的類(lèi)型字段。

types.undefined

定義一個(gè)值為undefined的類(lèi)型字段。

復(fù)合類(lèi)型

types.model

定義一個(gè)對(duì)象類(lèi)型的字段。

types.array

定義一個(gè)數(shù)組類(lèi)型的字段。

types.array(types.string); 復(fù)制代碼

上面的代碼定義了一個(gè)字符串?dāng)?shù)組的類(lèi)型。

types.map

定義一個(gè)map類(lèi)型的字段。該map的key都為字符串類(lèi)型,map的值都為指定類(lèi)型。

types.map(types.number); 復(fù)制代碼

可選類(lèi)型,types.optional

根據(jù)傳入的參數(shù),定義一個(gè)帶有默認(rèn)值的可選類(lèi)型。

types.optional是一個(gè)方法,方法有兩個(gè)參數(shù),第一個(gè)參數(shù)是數(shù)據(jù)的真實(shí)類(lèi)型,第二個(gè)參數(shù)是數(shù)據(jù)的默認(rèn)值。

types.optional(types.number, 1); 復(fù)制代碼

上面的代碼定義了一個(gè)默認(rèn)值為1的數(shù)值類(lèi)型。

注意,types.array或者types.map定義的類(lèi)型自帶默認(rèn)值(array為[],map為{}),也就是說(shuō),下面兩種定義的結(jié)果是一樣的:

// 使用types.optional types.optional(types.array(types.number), []); types.optional(types.map(types.number), {});// 不使用types.optional types.array(types.number); types.map(types.number); 復(fù)制代碼

如果要設(shè)置的默認(rèn)值與types.array或types.map自帶的默認(rèn)值相同,那么就不需要使用types.optional。

自定義類(lèi)型,types.custom

如果想控制類(lèi)型更底層的如序列化和反序列化、類(lèi)型校驗(yàn)等細(xì)節(jié),或者根據(jù)一個(gè)class或interface來(lái)定義類(lèi)型,可以使用types.custom定義自定義類(lèi)型。

class Decimal {... }const DecimalPrimitive = types.custom<string, Decimal>({name: "Decimal",fromSnapshot(value: string) {return new Decimal(value)},toSnapshot(value: Decimal) {return value.toString()},isTargetType(value: string | Decimal): boolean {return value instanceof Decimal},getValidationMessage(value: string): string {if (/^-?\d+\.\d+$/.test(value)) return "" // OKreturn `'${value}' doesn't look like a valid decimal number`} }); 復(fù)制代碼

上面的代碼定義了一個(gè)Decimal類(lèi)型。

聯(lián)合類(lèi)型,types.union

實(shí)際開(kāi)發(fā)中也許會(huì)遇到這樣的情況:一個(gè)值的類(lèi)型可能是字符串,也可能是數(shù)值。那我們就可以使用types.union定義聯(lián)合類(lèi)型:

types.union(types.number, types.string); 復(fù)制代碼

聯(lián)合類(lèi)型可以有任意個(gè)聯(lián)合的類(lèi)型。

字面值類(lèi)型,types.literal

字面值類(lèi)型可以限制存儲(chǔ)的內(nèi)容與給定的值嚴(yán)格相等。

比如使用types.literal('male')定義的狀態(tài)值只能為'male'。

實(shí)際上,上面提到過(guò)的types.null以及types.undefined就是字面值類(lèi)型:

const NullType = types.literal(null); const UndefinedType = types.literal(undefined); 復(fù)制代碼

搭配聯(lián)合類(lèi)型,可以這樣定義一個(gè)性別類(lèi)型:

const GenderType = types.union(types.literal('male'), types.literal('female')); 復(fù)制代碼

枚舉類(lèi)型,types.enumeration

枚舉類(lèi)型可以看作是聯(lián)合類(lèi)型以及字面值類(lèi)型的一層封裝,比如上面的性別可以使用枚舉類(lèi)型來(lái)定義:

const GenderType = types.enumeration('Gender', ['male', 'female']); 復(fù)制代碼

方法的第一個(gè)參數(shù)是可選的,表示枚舉類(lèi)型的名稱(chēng)。第二個(gè)參數(shù)傳入的是字面值數(shù)組。

在TypeScript環(huán)境下,可以這樣搭配TypeScript枚舉使用:

enum Gender {male,female }const GenderType = types.enumeration<Gender>('Gender', Object.values(Gender)); 復(fù)制代碼

可undefined類(lèi)型,types.maybe

定義一個(gè)可能為undefined的字段,并自帶默認(rèn)值undefined。

types.maybe(type) // 等同于 types.optional(types.union(type, types.literal(undefined)), undefined) 復(fù)制代碼

可空類(lèi)型,types.maybeNull

與types.maybe類(lèi)似,將undefined替換成了null。

types.maybeNull(type) // 等同于 types.optional(types.union(type, types.literal(null)), null) 復(fù)制代碼

不可不類(lèi)型,types.frozen

frozen意為“凍結(jié)的”,types.frozen方法用來(lái)定義一個(gè)immutable類(lèi)型,并且存放的值必須是可序列化的。

當(dāng)數(shù)據(jù)的類(lèi)型不確定時(shí),在TypeScript中通常將值的類(lèi)型設(shè)置為any,而在MST中,就需要使用types.frozen定義。

const Model = types.model('Model', {anyData: types.frozen()}).actions(self => ({setAnyData (data: any) {self.anyData = data;}})); 復(fù)制代碼

在MST看來(lái),使用types.frozen定義類(lèi)型的狀態(tài)值是不可變的,所以會(huì)出現(xiàn)這樣的情況:

model.anyData = {a: 1, b: 2}; // ok, reactive model.anyData.b = 3; // not reactive 復(fù)制代碼

也就是只有設(shè)置一個(gè)新的值給這個(gè)字段,相關(guān)的observer才會(huì)響應(yīng)狀態(tài)的更新。而修改這個(gè)字段內(nèi)部的某個(gè)值,是不會(huì)被捕捉到的。

滯后類(lèi)型,types.late

有時(shí)候會(huì)出現(xiàn)這樣的需求,需要一個(gè)Model A,在A中,存在類(lèi)型為A本身的字段。

如果這樣寫(xiě):

const A = types.model('A', {a: types.maybe(A), // 使用mabe避免無(wú)限循環(huán)}); 復(fù)制代碼

會(huì)提示Block-scoped variable 'A' used before its declaration,也就是在A定義完成之前就試圖使用他,這樣是不被允許的。

這個(gè)時(shí)候就需要使用types.late:

const A = types.model('A', {a: types.maybe(types.late(() => A))}); 復(fù)制代碼

types.late需要傳入一個(gè)方法,在方法中返回A,這樣就可以避開(kāi)上面報(bào)錯(cuò)的問(wèn)題。

提純類(lèi)型,types.refinement

types.refinement可以在其他類(lèi)型的基礎(chǔ)上,添加額外的類(lèi)型校驗(yàn)規(guī)則。

比如需要定義一個(gè)email字段,類(lèi)型為字符串但必須滿足email的標(biāo)準(zhǔn)格式,就可以這樣做:

const EmailType = types.refinement('Email',types.string,(snapshot) => /^[a-zA-Z_1-9]+@\.[a-z]+/.test(snapshot), // 校驗(yàn)是否符合email格式 ); 復(fù)制代碼

引用與標(biāo)識(shí)類(lèi)型

拿上一篇文章中的TodoList作為例子,我們?cè)趯?duì)Todo列表中的某一個(gè)Todo進(jìn)行編輯的時(shí)候,需要通過(guò)id跟蹤這個(gè)Todo,在提交編輯結(jié)果時(shí),通過(guò)這個(gè)id找到對(duì)應(yīng)的Todo對(duì)象,然后進(jìn)行更新。

這種需要跟蹤、查找的需求很常見(jiàn),寫(xiě)多了也覺(jué)得麻煩。

好在MST提供了一個(gè)優(yōu)雅的解決方案:引用類(lèi)型和標(biāo)識(shí)類(lèi)型。

這兩者需要搭配使用才能發(fā)揮作用:

定義標(biāo)識(shí),types.identifier

標(biāo)識(shí)就是數(shù)據(jù)對(duì)象的唯一標(biāo)識(shí)字段,這個(gè)字段的值在庫(kù)中保持唯一,也就是primary_key。

比如上一篇文章中的TodoItem,可以改造為:

export const TodoItem = types.model('TodoItem', {id: types.identifier,title: types.string,done: types.boolean,}); 復(fù)制代碼

使用引用類(lèi)型進(jìn)行跟蹤,types.reference

改造TodoList:

export const TodoList = types.model('TodoList', {...list: types.array(TodoItem),editTarget: types.reference(TodoItem),...}); 復(fù)制代碼

然后在創(chuàng)建Model實(shí)例,或者applySnapshot的時(shí)候,可以將editTarget的值設(shè)定為正在編輯的TodoItem的id值,MST就會(huì)自動(dòng)在list中查找id相同的TodoItem:

const todoList = TodoList.create({list: [{id: '1', title: 'Todo 1', done: true},{id: '2', title: 'Todo 2', done: true},...],editTarget: '1' });//此時(shí)的editTarget就是list中id為'1'的TodoItem對(duì)象 console.log(todoList.list[0] === todoList.editTarget); // truetodoList.editTarget = todoItem2; // todoItem2為id為'2'的TodoItem對(duì)象 console.log(getSnapshot(todoList).editTarget === '2'); // truetodoList.editTarget = '2' as any; console.log(getSnapshot(todoList).editTarget === '2'); // true 復(fù)制代碼

上面的代碼說(shuō)明,reference類(lèi)型的字段本質(zhì)上維護(hù)的是目標(biāo)的標(biāo)識(shí)字段值,并且,除了將目標(biāo)對(duì)象賦值給reference字段外,將目標(biāo)標(biāo)識(shí)字段值賦值給reference字段的效果是一樣的。

另外,reference不僅僅能搭配array使用,也能在map中查找:

const TodoList = types.model('TodoList', {todoMap: types.map(TodoItem),editTarget: types.reference(TodoItem) }); 復(fù)制代碼

甚至,MST也允許你自定義查找器(resolver),給types.reference指定第二個(gè)參數(shù),比如官網(wǎng)的這個(gè)例子:

const User = types.model({id: types.identifier,name: types.string })const UserByNameReference = types.maybeNull(types.reference(User, {// given an identifier, find the userget(identifier /* string */, parent: any /*Store*/) {return parent.users.find(u => u.name === identifier) || null},// given a user, produce the identifier that should be storedset(value /* User */) {return value.name}}) )const Store = types.model({users: types.array(User),selection: UserByNameReference })const s = Store.create({users: [{ id: "1", name: "Michel" }, { id: "2", name: "Mattia" }],selection: "Mattia" }) 復(fù)制代碼

types.identifierNumber

若對(duì)象的唯一標(biāo)識(shí)字段的值為數(shù)值類(lèi)型,那么可以使用types.identifierNumber代替types.identifier。

types.safeReference

這是一個(gè)“安全”的引用類(lèi)型:

const Todo = types.model({ id: types.identifier }) const Store = types.model({todos: types.array(Todo),selectedTodo: types.safeReference(Todo) }); 復(fù)制代碼

當(dāng)selectedTodo引用的目標(biāo)從todos這個(gè)節(jié)點(diǎn)被移除后,selectedTodo會(huì)自動(dòng)被設(shè)置為undefined。

小結(jié)

MST提供的類(lèi)型和類(lèi)型方法非常齊全,利用好他們就能為任意數(shù)據(jù)定義恰當(dāng)?shù)念?lèi)型。

喜歡本文的歡迎關(guān)注+收藏,轉(zhuǎn)載請(qǐng)注明出處,謝謝支持。

轉(zhuǎn)載于:https://juejin.im/post/5c526c6451882542ff1297ed

總結(jié)

以上是生活随笔為你收集整理的[MobX State Tree数据组件化开发][3]:选择正确的types.xxx的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。