国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁(yè) > 編程 > JavaScript > 正文

Vue 中的受控與非受控組件的實(shí)現(xiàn)

2019-11-19 12:20:19
字體:
供稿:網(wǎng)友

受控組件

什么是受控組件?

其值由React控制的輸入表單元素稱為“受控組件”。

受控組件有兩個(gè)特點(diǎn):1. 設(shè)置value值,value由state控制,2. value值一般在onChange事件中通過setState進(jìn)行修改

什么時(shí)候使用受控組件?

需要對(duì)組件的value值進(jìn)行修改時(shí),使用受控組件。比如:頁(yè)面中有一個(gè)按鈕,每點(diǎn)擊一次按鈕受控組件的值加1.

非受控組件

什么是非受控組件?

表單數(shù)據(jù)由 DOM 處理的組件非受控組件。

非受控組件有兩個(gè)特點(diǎn):1. 不設(shè)置value值,2. 通過ref獲取dom節(jié)點(diǎn)然后再取value值

<input type="text" placeholder="請(qǐng)輸入姓名" name='username' ref={(input) => this.usernameElem = input}/>

取值方法:this.usernameElem.value

什么時(shí)候使用非受控組件?

任何時(shí)候都不需要改變組件的value值,這時(shí)候可以使用非受控組件。

Vue 中的受控與非受控組件

熟悉 React 的開發(fā)者應(yīng)該對(duì)“受控組件”的概念并不陌生,實(shí)際上對(duì)于任何組件化開發(fā)框架而言,都可以實(shí)現(xiàn)所謂的受控與非受控,Vue 當(dāng)然也不例外。并且理解受控與非受控對(duì)應(yīng)的需求場(chǎng)景,可以讓我們?cè)谠O(shè)計(jì)一些基礎(chǔ)組件時(shí)思路更加清晰,暴露出來的組件 API 也更加合理、統(tǒng)一。

需求

許多 UI 組件都是有狀態(tài)(stateful)的,而這個(gè)狀態(tài)是由組件外部控制還是組件內(nèi)部維護(hù),也就對(duì)應(yīng)了受控與非受控兩種模式。

例如 Tabs 組件是很常見的一種 UI 組件,它的核心狀態(tài)就是記錄當(dāng)前 active 的 Tab,并且允許用戶切換。

很多時(shí)候我們只希望 Tabs 可以正確的展示 active 的內(nèi)容、并在用戶操作時(shí)正常切換,不需要進(jìn)行任何干預(yù),那么就希望 只需要傳入所有的 Tab 內(nèi)容,不需要再做額外的配置。

但有的時(shí)候我們又希望對(duì) Tabs 的狀態(tài)有很強(qiáng)的控制能力,例如多個(gè)關(guān)聯(lián)的 Tabs,子級(jí) Tabs 的內(nèi)容需要根據(jù)父級(jí) Tabs 的 active Tab 動(dòng)態(tài)切換,這時(shí)候就會(huì)希望 Tabs 組件可以暴露足夠充分的 API,來實(shí)現(xiàn)業(yè)務(wù)的需求。

因此我們可以用一種通用的模式,來讓任意組件的任意狀態(tài)同時(shí)兼容受控與非受控兩種模式,讓不同需求場(chǎng)景下都可以使用最合理的 API。

簡(jiǎn)化示例

我們用一個(gè)簡(jiǎn)單的 Tabs 實(shí)現(xiàn)來演示這種通用的組件 API 設(shè)計(jì)模式,簡(jiǎn)化的部分包括:

  • 用 index 來作為 Tab 的唯一標(biāo)識(shí)
  • Tab content 只支持字符串

可以打開 online DEMO 配合閱讀

API 設(shè)計(jì)

對(duì)于 Vue 組件而言,API 設(shè)計(jì)主要指的是內(nèi)部的 data, computed, methods 以及對(duì)外的 props, events。在這個(gè)示例中,我們會(huì)用 activeIdx 作為核心狀態(tài),所有的 API 也都會(huì)圍繞這個(gè)狀態(tài)命名。

非受控模式

如上文所說,非受控模式指的是使用者不需要關(guān)心控制組件的狀體,完全交由組件內(nèi)部維護(hù)。

因此我們的 API 會(huì)包括:

{ props: {  defaultActiveIdx: {   type: Number,   default: 0  } }, data() {  return {   localActiveIdx: this.defaultActiveIdx  } }, methods: {  handleActiveIdxChange(idx) {   this.localActiveIdx = idx;   this.$emit("active-idx-change", idx);  } }}

localActiveIdx 是我們用來存放 active index 的組件內(nèi) data,對(duì)于非受控模式而言,雖然不希望在外部維護(hù)狀態(tài),但是仍有可能希望在外部決定初始狀態(tài),所以我們用 defaultActiveIdx 這個(gè) props 決定 localActiveIdx 的初始值。

之后當(dāng)我們用 v-for="(tab, idx) in tabs" 指令生成所有的 Tab 時(shí),就可以通過 idx === localActiveIdx 的方式判斷當(dāng)前 Tab 是否 active,再通過 @click="handleActiveIdxChange(idx)" 就可以實(shí)現(xiàn)對(duì) localActiveIdx 的更新。

同樣的,我們也可以通過 {{ tabs[localActiveIdx].content }} 展示 active Tab 的內(nèi)容。

需要注意的是在 handleActiveIdxChange 的事件處理中,我們也 emit 了 active-idx-change 這一事件,這樣可以方便外部在不需要管理組件狀態(tài)的同時(shí)也可以與組件狀態(tài)保持同步。例如我們希望將 active Tab 反映在 URL 中,就可以在外部監(jiān)聽 active-idx-change 這一事件,并將當(dāng)前 index 同步到路由中,在將路由中獲取到的 index 作為 defaultActiveIdx 傳入,就可以實(shí)現(xiàn) URL 和 Tabs 的同步。

受控模式

對(duì)于受控模式來說,我們可以理解為 active index 是外部傳入的 props,由外部自行維護(hù)其狀態(tài)。

因此我們只需要添加如下 props:

props: { activeIdx: Number}

由于我們已經(jīng)有對(duì)外 emit 的事件 active-idx-change,所以外部用以下方式就可以用一個(gè) data 屬性 externalActiveIdx 維護(hù)對(duì)應(yīng)狀態(tài):

<tabs :tabs="tabs" :activeIdx="externalActiveIdx" @active-idx-change="this.externalActiveIdx = $event"/>

當(dāng)然由于在這種模式下外部對(duì)狀態(tài)有完全的控制權(quán),所以在 active-idx-change 的事件處理中也可以做更為復(fù)雜的判斷,例如是否允許激活目標(biāo) Tab 之類的校驗(yàn)。

而在 Tabs 組件內(nèi)部,我們還需要做一些小的修改。在受控模式中,我們所有狀態(tài)相關(guān)的處理都是直接使用 localActiveIdx,而現(xiàn)在我們的邏輯應(yīng)該變?yōu)椤叭绻嬖?activeIdx props,則使用,否則使用 localActiveIdx”。

為了保證以上邏輯不會(huì)讓我們的組件內(nèi)部實(shí)現(xiàn)變得復(fù)雜、易錯(cuò),我們引入一個(gè) computed 屬性:

computed: { _activeIdx() {  return this.activeIdx || this.localActiveIdx; }}

這樣我們就可以把狀態(tài)相關(guān)的判斷改為通過 idx === _activeIdx 判斷一個(gè) Tab 是否為激活狀態(tài),也通過 {{ tabs[_activeIdx].content }} 展示 active Tab 的內(nèi)容。

同樣,我們?cè)?handleActiveIdxChange 的方法內(nèi)部也可以增加一個(gè)判斷,如果存在 props aciveIdx 則不更新 localActiveIdx:

handleActiveIdxChange(idx) { if (this.activeIdx === undefined) {  this.localActiveIdx = idx; } this.$emit("active-idx-change", idx);}

在一些更復(fù)雜的組件中,可能會(huì)頻繁判斷是否為受控模式并做不同的處理,這時(shí)候通過 this.activeIdx 這樣的核心狀態(tài) props 是否傳入來判斷是否為受控模式是一個(gè)不錯(cuò)的實(shí)踐。

總結(jié)

最終我們?yōu)?active index 設(shè)計(jì)的完整 API 如下:

{ props: {  activeIdx: Number,  defaultActiveIdx: {   type: Number,   default: 0  } }, data() {  return {   localActiveIdx: this.defaultActiveIdx  }; }, computed: {  _activeIdx() {   return this.activeIdx || this.localActiveIdx;  } }, methods: {  handleActiveIdxChange(idx) {   if (this.activeIdx === undefined) {    this.localActiveIdx = idx;   }   this.$emit("active-idx-change", idx);  } }}

通過這種 API 設(shè)計(jì)方式,可以讓我們?cè)O(shè)計(jì)的基礎(chǔ)組件使用方式更一致,拓展性更強(qiáng),不論是開發(fā)還是使用時(shí)思路也會(huì)更加簡(jiǎn)潔清晰。

以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持武林網(wǎng)。

發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 晋宁县| 志丹县| 汝城县| 阜新市| 祥云县| 台南市| 内乡县| 简阳市| 泸州市| 永安市| 乌兰察布市| 西乌珠穆沁旗| 浦北县| 扶余县| 永川市| 上虞市| 山丹县| 昌吉市| 永济市| 阳春市| 苗栗县| 淳安县| 长寿区| 桃江县| 根河市| 林甸县| 乳山市| 谷城县| 九龙坡区| 桓台县| 游戏| 田东县| 丰宁| 新建县| 运城市| 常熟市| 汝南县| 沁阳市| 招远市| 来安县| 卢湾区|