成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

Vue 3.4 重磅升級:defineModel 宏如何徹底改變前端狀態(tài)管理!

開發(fā) 前端
隨著 3.4 的發(fā)布,由于它使?fàn)顟B(tài)管理變得更加簡潔和直觀,這種愿景現(xiàn)在感覺更加完整。在某種程度上,它有助于澄清狀態(tài)放置位置的復(fù)雜決策過程,使簡單而明顯的選擇成為正確的選擇。

理解狀態(tài)的位置和組件邊界仍然是現(xiàn)代前端開發(fā)中主要挑戰(zhàn)之一,也是團(tuán)隊(duì)在應(yīng)用規(guī)模增加時(shí)做出的最重要的決定之一,可能會加速開發(fā),也可能成為最大的摩擦源。

如果做得好,構(gòu)建、組合、重構(gòu)和測試前端組件會變得輕而易舉;如果做得不好,則會成為難以追蹤的幽靈錯(cuò)誤的無盡源泉,使代碼庫變得脆弱。

Vue 3.4 版本中從實(shí)驗(yàn)狀態(tài)發(fā)布的 defineModel 宏,可能是關(guān)于組件間復(fù)雜狀態(tài)交互的實(shí)現(xiàn)方式最具變革性的特性之一。

描述看似很無害:

defineModel 是一個(gè)新的 <script setup> 宏,旨在簡化支持 v-model 的組件的實(shí)現(xiàn)

表面上看,這個(gè)宏的實(shí)用性似乎微不足道,但它對團(tuán)隊(duì)如何處理狀態(tài)和管理組件邊界有深遠(yuǎn)影響。我們看看 defineModel 是做什么的,為什么它的添加在 Vue 3.4 中感覺像是一種范式轉(zhuǎn)變——盡管它只是一個(gè)簡單的宏。

狀態(tài)的概念模型

通常,現(xiàn)代前端應(yīng)用程序有三種狀態(tài)范圍(不包括全局窗口級別的狀態(tài))。

圖片圖片

  1. 全局共享狀態(tài)。這是整個(gè)層次結(jié)構(gòu)中不同組件可訪問的狀態(tài),例如登錄用戶的賬戶信息和路由間共享的信息。
  2. 分層組件狀態(tài)。這是在層次結(jié)構(gòu)的子樹中不同組件可訪問的狀態(tài),例如列表-詳情編輯視圖。
  3. 單個(gè)組件級別的狀態(tài)。這是僅在層次結(jié)構(gòu)中的單個(gè)組件中可訪問的狀態(tài),狀態(tài)交互不需要在樹中上下傳遞。

在全局級別,有許多庫和解決方案可以解決這個(gè)問題。例如,React 的 Zustand、Jotai、Recoil、Redux 等,Vue 的 Pinia 可以將狀態(tài)從組件樹中提取出來放入全局范圍,以跨越樹。它旨在保存真正的全局狀態(tài),如淺色/深色模式或租戶 ID。

第二層狀態(tài)是團(tuán)隊(duì)遇到“屬性傳遞”摩擦的地方——無論是在 React、Vue 還是其他庫或框架中。部分原因是管理狀態(tài)在組件之間的上下移動是繁重的。

在這種情況下,團(tuán)隊(duì)的自然決定是將狀態(tài)移動到全局存儲中,或者進(jìn)入第三個(gè)組件狀態(tài)范圍,僅僅是為了避免這種摩擦,而不斷堆積到一個(gè)巨大的組件中——這會產(chǎn)生另一種痛苦。

如果能在保持 Vue 的雙向綁定的同時(shí),輕松地將狀態(tài)分離開來,而不需要屬性傳遞的摩擦,那該多好。這正是 defineModel 的作用所在,它大大減少了在樹中移動狀態(tài)的摩擦,同時(shí)保持 Vue 的雙向綁定。

defineModel 是什么?

重要的是首先了解它是什么以及它的功能。對于那些不熟悉 Vue 的人來說,組件間上下移動狀態(tài)的慣用模式一直是使用 props 和 emits。

在 defineModel 之前 - props 和 emits

例如,考慮這個(gè)父子組件:

圖片圖片

外部組件定義了 ref 并將其作為 prop 傳遞給子組件。更新通過子組件向父組件的 emit 事件來完成。

為了獲得雙向綁定,我們需要內(nèi)部的 NameInput.vue 組件如下:

<!-- NameInput.vue -->
<template>
  <LabeledContainer label="NameInput.vue">
    <input v-model="name"/>
  </LabeledContainer>
</template>

<script setup lang="ts">
const props = defineProps<{
  modelValue: string
}>()

const emits = defineEmits<{
  'update:modelValue': [string]
}>()

const name = computed({
  get() {
    return props.modelValue
  },
  set(val) {
    emits('update:modelValue', val)
  }
})
</script>

外部的 Example1.vue 組件如下:

<!-- Example1.vue -->
<template>
  <LabeledContainer label="Example1.vue">
    <h1>Example 1</h1>
    <p>Hello, {{ name.length === 0 ? "(enter your name below)" : name }}</p>
    <NameInput v-model="name"/>
  </LabeledContainer>
</template>

<script setup lang="ts">
const name = ref('')
</script>

現(xiàn)在,當(dāng)我們在文本框中輸入值時(shí),它會自動更新 prop 的值:

圖片圖片

我們的非常簡單的組件具有父子組件之間的雙向綁定。

可以很容易地看到,對于如此簡單的事情,這種樣板代碼會變得多么繁瑣!

在 defineModel 之后 ?

隨著 Vue 3.4 中 defineModel 的發(fā)布,我們看看它如何簡化 NameInput.vue:

<!-- NameInput.vue -->
<template>
  <LabeledContainer label="NameInput.vue">
    <input v-model="name"/>
  </LabeledContainer>
</template>

<script setup lang="ts">
const name = defineModel<string>({ required: true })
</script>

父組件保持不變,但大量的樣板代碼被刪除了!這個(gè)小小的宏完全改變了管理狀態(tài)的體驗(yàn)。

一個(gè)實(shí)際的例子

表面上看,這似乎是一個(gè)微不足道的變化。當(dāng)然,獲得了一些便利,但這對開發(fā)人員管理狀態(tài)有多大影響?僅僅是一個(gè)簡單的宏,聲稱會有這么大的影響豈不是荒謬?

事實(shí)上,開發(fā)人員往往會選擇阻力最小的路徑,如果阻力最小的路徑是壞習(xí)慣,那么,開發(fā)人員將創(chuàng)建一個(gè)充滿許多壞習(xí)慣的代碼庫——即“技術(shù)債務(wù)”。如果你見過 1000 多行的 React 或 Vue 組件(我們中誰沒見過?),那么很可能的原因是將狀態(tài)以可管理的方式分散出來的摩擦太大;隨著組件的有機(jī)增長,將狀態(tài)保持在一個(gè)巨大的組件中比分解出新組件更容易。

defineModel 的實(shí)現(xiàn)是,它創(chuàng)建了一條最小阻力路徑,同時(shí)有助于改善團(tuán)隊(duì)對狀態(tài)的思考方式。突然之間,管理分層組件狀態(tài)變得微不足道,并消除了將狀態(tài)移入全局范圍或在大型組件中進(jìn)行松散操作的誘惑(通常是 1000 多行組件的來源)。

使用 defineModel 簡化分層狀態(tài)

考慮以下簡單的聯(lián)系人管理應(yīng)用程序:

圖片圖片

注意這個(gè)示例中的層次結(jié)構(gòu)。當(dāng)用戶從 Listing.vue 中選擇聯(lián)系人時(shí),應(yīng)用程序應(yīng)在 Details.vue 中顯示詳細(xì)信息。當(dāng)用戶編輯詳細(xì)信息并在 Details.vue 中保存更改時(shí),應(yīng)用程序應(yīng)更新 Listing.vue 中的條目。

如果我們想在 Listing.vue 和 Details.vue 之間共享狀態(tài),它必須是全局狀態(tài)或從公共父級 Example3.vue 開始的分層狀態(tài)——否則,很容易看到將所有內(nèi)容放入一個(gè)巨大的組件中的誘惑!

在這種情況下,這就是我們的分層狀態(tài)的樣子:

圖片圖片

狀態(tài)通過 prop 從列表組件傳遞到聯(lián)系人組件。

我們從外到內(nèi)檢查代碼。

這是我們的父 Example3.vue 組件:

<template>
  <LabeledContainer label="Example3.vue">
    <h1>Example 3</h1>

    <p v-if="!!selectedContact">
      Selected: {{ selectedContact.name }} ({{ selectedContact.handle  }})
    </p>

    <div class="parent">
      <Listing
        v-model="contacts"
        v-model:selected="selectedContact"/>

      <Details v-model="selectedContact"/>
    </div>
  </LabeledContainer>
</template>

<script setup lang="ts">
const selectedContact = ref<Contact>()

const contacts = ref<Contact[]>([{
  name: 'Charles',
  handle: '@chrlschn'
}])
</script>

這是我們的狀態(tài)所在根,并通過綁定將其傳遞給 Listing 和 Details 組件:

<!-- Snippet from Example3.vue-->
<Listing
  v-model="contacts"
  v-model:selected="selectedContact"/>

<Details v-model="selectedContact"/>

我們先看看 Details.vue :

<!-- Details.vue, the right side form inputs -->
<template>
  <LabeledContainer label="Details.vue">
    <div v-if="!!selected">
      <label>
        Name
        <input v-model="name"/>
      </label>

      <label>
        Handle
        <input v-model="handle"/>
      </label>

      <div>
        <button @click="handleCancel">Done</button>
        <button @click="handleDone">Save</button>
      </div>
    </div>
    <p v-else>
      Select a contact
    </p>
  </LabeledContainer>
</template>

<script setup lang="ts">
const selected = defineModel<Contact|undefined>({
  required: true
})

const name = ref('')

const handle = ref('')

watch (selected, (contact) => {
  if (!contact) {
    return
  }

  name.value = contact.name,
  handle.value = contact.handle
})

function handleCancel() {
  selected.value = undefined
}

function handleDone() {
  if (!selected.value) {
    return
  }

  selected.value.name = name.value;
  selected.value.handle = handle.value;
}
</script>

這個(gè)組件的目的是擁有一組狀態(tài)副本,當(dāng)選中的聯(lián)系人更改時(shí),組件將值復(fù)制到本地狀態(tài),以便在不影響原始狀態(tài)的情況下(直到用戶保存),更改名稱和 handle。這也允許用戶取消任何編輯。

對于更大的屬性集,可以考慮創(chuàng)建對象的完整響應(yīng)式副本并直接綁定到它。

在左側(cè),Listing.vue 組件包含聯(lián)系人列表,并有添加新聯(lián)系人的選項(xiàng)。

<!-- Listing.vue -->
<template>
  <LabeledContainer label="Listing.vue">
    <div class="container">
      <ContactItem
        v-for="contact in contacts"
        :cnotallow="contact"
        :selected="selected == contact"
        @click="selected = contact">
      </ContactItem>
    </div>

    <div>
      <button @click="handleAddContact"> Add contact </button>
    </div>
  </LabeledContainer>
</template>

<script setup lang="ts">
const contacts = defineModel<Contact[]>({
  required: true
})

const selected = defineModel<Contact|undefined>('selected', {
  required: true
})

function handleAddContact() {
  contacts.value.push({
    name: 'Name',
    handle: 'Handle'
  })
}
</script>

然后在 ContactItem.vue 中,Listing.vue 通過普通的 props 傳遞顯示值,因?yàn)檫@里不需要變更(也不需要雙向綁定):

<template>
  <LabeledContainer
    label="Contact.vue"
    class="contact"
    :class="{
      'selected': !!selected
    }">
    <p class="name">{{ contact.name }}</p>
    <p class="handle">{{ contact.handle }}</p>
  </LabeledContainer>
</template>

<script setup lang="ts">
defineProps<{
  contact: Contact,
  selected?: boolean
}>()
</script>

我們看看這整個(gè)事情是如何結(jié)合在一起的:

圖片圖片

我們的組件在示例組件樹的層次結(jié)構(gòu)中共享狀態(tài)。

如果沒有 defineModel 來幫助簡化這種交互,很容易看到本能是采取捷徑或?qū)顟B(tài)移入全局狀態(tài),因?yàn)榫帉懜鞣N emits 和 computed 會產(chǎn)生相當(dāng)大的摩擦,即使在這個(gè)小示例中也是如此!

正如 Billy Mays 可能會說:“但等一下!還有更多!”讓我們看看如何通過使用可組合組件進(jìn)一步簡化代碼。

使用 defineModel 和可組合組件

利用可組合組件可以將其提升到一個(gè)新的水平,并通過將狀態(tài)從組件中提取出來進(jìn)一步簡化我們的代碼。當(dāng)組件變得更大時(shí),這特別有用。

在 Vue 中,這很容易實(shí)現(xiàn),并使重構(gòu)和重新組織復(fù)雜性變得輕而易舉。

我們只需將我們的狀態(tài)和函數(shù)向上提取到另一個(gè)函數(shù)中:

// useContacts composable
export function useContacts() {
  const selectedContact = ref<Contact>()

  const contacts = ref<Contact[]>([{
    name: 'Charles',
    handle: '@chrlschn'
  }])

  function addContact() {
    contacts.value.push({
      name: 'Name',
      handle: 'Handle'
    })
  }

  return {
    selectedContact,
    contacts,
    addContact
  }
}

很容易看出,如果我們想從 Details.vue 中移動更多的邏輯和狀態(tài),將 name 和 handle refs 以及 handleCancel() 和 handleDone() 函數(shù)移動到另一個(gè)可組合組件中并共享它們是非常低摩擦的:

// useDetailsEditor.ts

// ?? 注意我們在這里接收響應(yīng)式 selectedContact 
export function useDetailsEditor(
  selectedContact: Ref<Contact|undefined>
) {
  const name = ref('') 

  const handle = ref('') 

  // ?? 我們在 selectedContact 上添加一個(gè)監(jiān)聽器
  // 以便更新封裝的狀態(tài)。
  watch (selectedContact, (contact) => {
    if (!contact) {
      return
    }

    name.value = contact.name,
    handle.value = contact.handle
  })

  function cancel() {
    selectedContact.value = undefined
  }

  function done() {
    if (!selectedContact.value) {
      return
    }

    selectedContact.value.name = name.value;
    selectedContact.value.handle = handle.value;
  }

  return {
    name,
    handle,
    cancel,
    done
  }
}

然后我們更新 Details.vue:

<template>
  <LabeledContainer label="Details.vue">
    <div v-if="!!selected">
      <div>
        <label>
          Name
          <input v-model="name"/>
        </label>
      </div>

      <div>
        <label>
          Handle
          <input v-model="handle"/>
        </label>
      </div>

      <div>
        <button @click="cancel">Done</button>
        <button @click="done">Save</button>
      </div>
    </div>
    <p v-else>
      Select a contact
    </p>
  </LabeledContainer>
</template>

<script setup lang="ts">
const selected = defineModel<Contact|undefined>({
  required: true
})

const {
  name,
  handle,
  cancel,
  done
} = useDetailsEditor(selected)
// ?? 我們將選中的聯(lián)系人傳遞給可組合組件以便監(jiān)聽
</script>

使用 Vue 3 可組合組件和 Vue 3.4 的 defineModel 宏,這種方式可以很好地將相關(guān)狀態(tài)和邏輯干凈地分離和封裝。我希望清楚的是,通過簡單地復(fù)制狀態(tài)、函數(shù)、監(jiān)聽器和計(jì)算值并將它們粘貼到可組合組件中,可以輕松地重構(gòu)代碼。

這種模式可以使即使是大型子組件樹也變得易于管理、重構(gòu)和測試。

結(jié)論

Vue 3.4 中引入的 defineModel 實(shí)際上是一個(gè)深遠(yuǎn)的變化,它將幫助團(tuán)隊(duì)遵循最佳實(shí)踐并構(gòu)建更好、更易管理的組件。通過消除構(gòu)建分層狀態(tài)時(shí)的許多摩擦,它使得團(tuán)隊(duì)不太可能立即求助于全局狀態(tài)或倒退到松散的做法。

通過將 defineModel 與 Vue 可組合組件結(jié)合使用,團(tuán)隊(duì)可以通過組織和封裝相關(guān)狀態(tài)和邏輯來創(chuàng)建更干凈的組件,這些組件易于閱讀和理解。

當(dāng)尤雨溪首次提出 Vue 3 的 Composition API 時(shí),社區(qū)中有很多反對聲音,認(rèn)為它應(yīng)該保留 Options API 的簡單性和易用性。事后看來,很明顯,尤雨溪幫助 Vue 更好地?cái)U(kuò)展以應(yīng)對構(gòu)建大型項(xiàng)目的團(tuán)隊(duì)的路徑是正確的選擇。

隨著 3.4 的發(fā)布,由于它使?fàn)顟B(tài)管理變得更加簡潔和直觀,這種愿景現(xiàn)在感覺更加完整。在某種程度上,它有助于澄清狀態(tài)放置位置的復(fù)雜決策過程,使簡單而明顯的選擇成為正確的選擇。defineModel 就像是一顆投向海洋的小石子,其漣漪可能有一天會成為洶涌的波浪!

責(zé)任編輯:武曉燕 來源: 大遷世界
相關(guān)推薦

2023-10-17 13:50:04

RPAAI

2025-03-07 07:00:00

AI人工智能

2023-04-17 16:13:16

2021-06-24 14:20:31

物聯(lián)網(wǎng)智能建筑IoT

2023-04-17 16:02:00

2022-10-26 11:06:16

機(jī)器學(xué)習(xí)農(nóng)業(yè)

2020-05-25 20:48:06

物聯(lián)網(wǎng)供應(yīng)鏈技術(shù)

2024-03-05 14:43:02

2023-05-04 09:48:54

機(jī)器學(xué)習(xí)人工智能

2023-07-20 10:44:46

2023-10-10 13:16:16

云優(yōu)先數(shù)字孿生

2023-03-14 14:43:40

邊緣計(jì)算物聯(lián)網(wǎng)

2022-07-11 18:49:19

虛擬Web3元宇宙

2013-04-28 10:31:18

物聯(lián)網(wǎng)改變搜索

2022-04-01 14:43:35

物聯(lián)網(wǎng)數(shù)字營銷物聯(lián)網(wǎng)網(wǎng)絡(luò)

2022-08-02 17:14:52

區(qū)塊鏈網(wǎng)絡(luò)安全去中心化

2023-04-21 08:00:00

2023-06-08 11:57:48

2024-10-21 12:53:23

2024-01-24 11:24:39

VR虛擬現(xiàn)實(shí)電影制作
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 日韩欧美在线观看视频 | 色婷婷国产精品 | 亚洲精品乱码久久久久久按摩观 | 色综合久久天天综合网 | 国产精品久久久久久久久久东京 | 久久精品一区二区三区四区 | 日韩一区二区三区在线观看 | 欧美精品一区二区三区视频 | 青草福利| 久久久久久国产精品 | 日韩中文字幕免费在线观看 | 日本黄色片免费在线观看 | 亚洲精品在线观看网站 | 日本天天色 | 91精品国产综合久久婷婷香蕉 | 97精品国产97久久久久久免费 | 一区二区三区成人 | h视频亚洲| av毛片免费 | 91av视频在线观看 | 国产欧美久久一区二区三区 | 成人精品福利 | 国产伦精品一区二区三区高清 | 免费精品久久久久久中文字幕 | 亚洲精品乱码 | 久久亚洲精品国产精品紫薇 | 午夜不卡一区二区 | 久久久www成人免费无遮挡大片 | 日韩另类 | 中文字幕一区二区三区在线观看 | 91xxx在线观看| 成人高清网站 | 国产高清视频一区二区 | 国产999精品久久久久久绿帽 | 无码日韩精品一区二区免费 | 成人在线视频一区 | 国产视频久久久久 | 久久亚洲一区二区三区四区 | 网站国产 | 成人免费片| 精品美女|