日韩成人免费在线_国产成人一二_精品国产免费人成电影在线观..._日本一区二区三区久久久久久久久不

當(dāng)前位置:首頁 > 科技  > 軟件

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

來源: 責(zé)編: 時間:2024-06-27 17:19:59 143觀看
導(dǎo)讀理解狀態(tài)的位置和組件邊界仍然是現(xiàn)代前端開發(fā)中主要挑戰(zhàn)之一,也是團隊在應(yīng)用規(guī)模增加時做出的最重要的決定之一,可能會加速開發(fā),也可能成為最大的摩擦源。如果做得好,構(gòu)建、組合、重構(gòu)和測試前端組件會變得輕而易舉;如果做

理解狀態(tài)的位置和組件邊界仍然是現(xiàn)代前端開發(fā)中主要挑戰(zhàn)之一,也是團隊在應(yīng)用規(guī)模增加時做出的最重要的決定之一,可能會加速開發(fā),也可能成為最大的摩擦源。UKK28資訊網(wǎng)——每日最新資訊28at.com

如果做得好,構(gòu)建、組合、重構(gòu)和測試前端組件會變得輕而易舉;如果做得不好,則會成為難以追蹤的幽靈錯誤的無盡源泉,使代碼庫變得脆弱。UKK28資訊網(wǎng)——每日最新資訊28at.com

Vue 3.4 版本中從實驗狀態(tài)發(fā)布的 defineModel 宏,可能是關(guān)于組件間復(fù)雜狀態(tài)交互的實現(xiàn)方式最具變革性的特性之一。UKK28資訊網(wǎng)——每日最新資訊28at.com

描述看似很無害:UKK28資訊網(wǎng)——每日最新資訊28at.com

defineModel 是一個新的 <script setup> 宏,旨在簡化支持 v-model 的組件的實現(xiàn)UKK28資訊網(wǎng)——每日最新資訊28at.com

表面上看,這個宏的實用性似乎微不足道,但它對團隊如何處理狀態(tài)和管理組件邊界有深遠影響。我們看看 defineModel 是做什么的,為什么它的添加在 Vue 3.4 中感覺像是一種范式轉(zhuǎn)變——盡管它只是一個簡單的宏。UKK28資訊網(wǎng)——每日最新資訊28at.com

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

通常,現(xiàn)代前端應(yīng)用程序有三種狀態(tài)范圍(不包括全局窗口級別的狀態(tài))。UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

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

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

第二層狀態(tài)是團隊遇到“屬性傳遞”摩擦的地方——無論是在 React、Vue 還是其他庫或框架中。部分原因是管理狀態(tài)在組件之間的上下移動是繁重的。UKK28資訊網(wǎng)——每日最新資訊28at.com

在這種情況下,團隊的自然決定是將狀態(tài)移動到全局存儲中,或者進入第三個組件狀態(tài)范圍,僅僅是為了避免這種摩擦,而不斷堆積到一個巨大的組件中——這會產(chǎn)生另一種痛苦。UKK28資訊網(wǎng)——每日最新資訊28at.com

如果能在保持 Vue 的雙向綁定的同時,輕松地將狀態(tài)分離開來,而不需要屬性傳遞的摩擦,那該多好。這正是 defineModel 的作用所在,它大大減少了在樹中移動狀態(tài)的摩擦,同時保持 Vue 的雙向綁定。UKK28資訊網(wǎng)——每日最新資訊28at.com

defineModel 是什么?

重要的是首先了解它是什么以及它的功能。對于那些不熟悉 Vue 的人來說,組件間上下移動狀態(tài)的慣用模式一直是使用 props 和 emits。UKK28資訊網(wǎng)——每日最新資訊28at.com

在 defineModel 之前 - props 和 emits

例如,考慮這個父子組件:UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

外部組件定義了 ref 并將其作為 prop 傳遞給子組件。更新通過子組件向父組件的 emit 事件來完成。UKK28資訊網(wǎng)——每日最新資訊28at.com

為了獲得雙向綁定,我們需要內(nèi)部的 NameInput.vue 組件如下:UKK28資訊網(wǎng)——每日最新資訊28at.com

<!-- 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 組件如下:UKK28資訊網(wǎng)——每日最新資訊28at.com

<!-- 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)我們在文本框中輸入值時,它會自動更新 prop 的值:UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

我們的非常簡單的組件具有父子組件之間的雙向綁定。UKK28資訊網(wǎng)——每日最新資訊28at.com

可以很容易地看到,對于如此簡單的事情,這種樣板代碼會變得多么繁瑣!UKK28資訊網(wǎng)——每日最新資訊28at.com

在 defineModel 之后 ?

隨著 Vue 3.4 中 defineModel 的發(fā)布,我們看看它如何簡化 NameInput.vue:UKK28資訊網(wǎng)——每日最新資訊28at.com

<!-- NameInput.vue --><template>  <LabeledContainer label="NameInput.vue">    <input v-model="name"/>  </LabeledContainer></template><script setup lang="ts">const name = defineModel<string>({ required: true })</script>

父組件保持不變,但大量的樣板代碼被刪除了!這個小小的宏完全改變了管理狀態(tài)的體驗。UKK28資訊網(wǎng)——每日最新資訊28at.com

一個實際的例子

表面上看,這似乎是一個微不足道的變化。當(dāng)然,獲得了一些便利,但這對開發(fā)人員管理狀態(tài)有多大影響?僅僅是一個簡單的宏,聲稱會有這么大的影響豈不是荒謬?UKK28資訊網(wǎng)——每日最新資訊28at.com

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

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

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

考慮以下簡單的聯(lián)系人管理應(yīng)用程序:UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

注意這個示例中的層次結(jié)構(gòu)。當(dāng)用戶從 Listing.vue 中選擇聯(lián)系人時,應(yīng)用程序應(yīng)在 Details.vue 中顯示詳細信息。當(dāng)用戶編輯詳細信息并在 Details.vue 中保存更改時,應(yīng)用程序應(yīng)更新 Listing.vue 中的條目。UKK28資訊網(wǎng)——每日最新資訊28at.com

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

在這種情況下,這就是我們的分層狀態(tài)的樣子:UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

狀態(tài)通過 prop 從列表組件傳遞到聯(lián)系人組件。UKK28資訊網(wǎng)——每日最新資訊28at.com

我們從外到內(nèi)檢查代碼。UKK28資訊網(wǎng)——每日最新資訊28at.com

這是我們的父 Example3.vue 組件:UKK28資訊網(wǎng)——每日最新資訊28at.com

<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 組件:UKK28資訊網(wǎng)——每日最新資訊28at.com

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

我們先看看 Details.vue :UKK28資訊網(wǎng)——每日最新資訊28at.com

<!-- 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>

這個組件的目的是擁有一組狀態(tài)副本,當(dāng)選中的聯(lián)系人更改時,組件將值復(fù)制到本地狀態(tài),以便在不影響原始狀態(tài)的情況下(直到用戶保存),更改名稱和 handle。這也允許用戶取消任何編輯。UKK28資訊網(wǎng)——每日最新資訊28at.com

對于更大的屬性集,可以考慮創(chuàng)建對象的完整響應(yīng)式副本并直接綁定到它。UKK28資訊網(wǎng)——每日最新資訊28at.com

在左側(cè),Listing.vue 組件包含聯(lián)系人列表,并有添加新聯(lián)系人的選項。UKK28資訊網(wǎng)——每日最新資訊28at.com

<!-- 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 傳遞顯示值,因為這里不需要變更(也不需要雙向綁定):UKK28資訊網(wǎng)——每日最新資訊28at.com

<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>

我們看看這整個事情是如何結(jié)合在一起的:UKK28資訊網(wǎng)——每日最新資訊28at.com

圖片圖片UKK28資訊網(wǎng)——每日最新資訊28at.com

我們的組件在示例組件樹的層次結(jié)構(gòu)中共享狀態(tài)。UKK28資訊網(wǎng)——每日最新資訊28at.com

如果沒有 defineModel 來幫助簡化這種交互,很容易看到本能是采取捷徑或?qū)顟B(tài)移入全局狀態(tài),因為編寫各種 emits 和 computed 會產(chǎn)生相當(dāng)大的摩擦,即使在這個小示例中也是如此!UKK28資訊網(wǎng)——每日最新資訊28at.com

正如 Billy Mays 可能會說:“但等一下!還有更多!”讓我們看看如何通過使用可組合組件進一步簡化代碼。UKK28資訊網(wǎng)——每日最新資訊28at.com

使用 defineModel 和可組合組件

利用可組合組件可以將其提升到一個新的水平,并通過將狀態(tài)從組件中提取出來進一步簡化我們的代碼。當(dāng)組件變得更大時,這特別有用。UKK28資訊網(wǎng)——每日最新資訊28at.com

在 Vue 中,這很容易實現(xiàn),并使重構(gòu)和重新組織復(fù)雜性變得輕而易舉。UKK28資訊網(wǎng)——每日最新資訊28at.com

我們只需將我們的狀態(tài)和函數(shù)向上提取到另一個函數(shù)中:UKK28資訊網(wǎng)——每日最新資訊28at.com

// useContacts composableexport 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ù)移動到另一個可組合組件中并共享它們是非常低摩擦的:UKK28資訊網(wǎng)——每日最新資訊28at.com

// useDetailsEditor.ts//  
                

本文鏈接:http://www.www897cc.com/showinfo-26-96994-0.htmlVue 3.4 重磅升級:defineModel 宏如何徹底改變前端狀態(tài)管理!

聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時與本網(wǎng)聯(lián)系,我們將在第一時間刪除處理。郵件:2376512515@qq.com

上一篇: 我們一起聊聊 Go 模塊使用 GitLab subgroups 的問題

下一篇: OpenTelemetry 深度定制:跨服務(wù)追蹤的實戰(zhàn)技巧

標簽:
  • 熱門焦點
Top 主站蜘蛛池模板: 浦江县| 金昌市| 霸州市| 宁都县| 辽阳县| 晋宁县| 东方市| 安宁市| 昆山市| 开鲁县| 邻水| 射洪县| 青冈县| 廉江市| 额济纳旗| 清远市| 安溪县| 鹤壁市| 新宾| 昌邑市| 微山县| 思南县| 深水埗区| 涞水县| 界首市| 无为县| 沿河| 桦川县| 客服| 房山区| 岑溪市| 柳州市| 枣强县| 越西县| 珠海市| 平阳县| 北川| 永福县| 周至县| 邵武市| 邮箱|