onmounted vue3_基于项目时间阐述vue3.0新型状态管理和逻辑复用方式
作者:Mingle
轉(zhuǎn)發(fā)鏈接:https://mp.weixin.qq.com/s/iOq-eeyToDXJ6lvwnC12DQ
前言
背景:2019年2月6號(hào),React 發(fā)布 「16.8.0」 版本,vue緊隨其后,發(fā)布了「vue3.0 RFC」
Vue3.0受React16.0 推出的hook抄襲啟發(fā)(咳咳...),提供了一個(gè)全新的邏輯復(fù)用方案。使用基于函數(shù)的 API,我們可以將相關(guān)聯(lián)的代碼抽取到一個(gè) "composition function"(組合函數(shù))中 —— 該函數(shù)封裝了相關(guān)聯(lián)的邏輯,并將需要暴露給組件的狀態(tài)以相應(yīng)式的數(shù)據(jù)源的方式返回出來。
本文目的
本文會(huì)介紹Vue3.0「組合api的用法和注意點(diǎn)」。最后會(huì)用一個(gè) Todolist 的項(xiàng)目實(shí)戰(zhàn),向大家介紹「Vue3.0的邏輯復(fù)用寫法以及借用provide和inject的新型狀態(tài)管理方式」
本文提綱:
- 如何新建一個(gè)使用vue3.0的項(xiàng)目
- conposition api
- 邏輯復(fù)用(hook)和狀態(tài)管理(provide+inject)
- 結(jié)合項(xiàng)目實(shí)戰(zhàn),做一個(gè)todo list
正文
如何新建一個(gè)使用vue3.0的項(xiàng)目
接下來向大家簡(jiǎn)單介紹下如何嘗鮮 -- 自己創(chuàng)建一個(gè)vue3.0的項(xiàng)目。
我這邊使用的是最新版本的vue-cli 4.4.0
npm?install?-g?@vue/cli#?ORyarn?global?add?@vue/cliok了。就這么簡(jiǎn)單!
conposition api
#### 目錄
- 基本例子
- setup()
- reactive
- ref
- computed
- watchEffect
- watch
- 生命周期
- 依賴注入
基本例子
??????count?is?{{?count.count?}}????plusOne?is?{{?plusOne?}}????count++??setup
?
該setup功能是新的組件選項(xiàng)。它是組件內(nèi)部暴露出所有的屬性和方法的統(tǒng)一API。
?
調(diào)用時(shí)機(jī)
創(chuàng)建組件實(shí)例,然后初始化 props ,緊接著就調(diào)用setup 函數(shù)。從生命周期鉤子的視角來看,它會(huì)在 beforeCreate 鉤子之前被調(diào)用
模板中使用
如果 setup 返回一個(gè)對(duì)象,則對(duì)象的屬性將會(huì)被合并到組件模板的渲染上下文
??{{?count?}}?{{?object.foo?}}setup 參數(shù)
- 不要在子組件中修改props;如果你嘗試修改,將會(huì)給你警告甚至報(bào)錯(cuò)。
- 不要結(jié)構(gòu)props。結(jié)構(gòu)的props會(huì)失去響應(yīng)性。
2.「上下文對(duì)象」第二個(gè)參數(shù)提供了一個(gè)上下文對(duì)象,從原來 2.x 中 this 選擇性地暴露了一些 property。
const?MyComponent?=?{??setup(props,?context)?{????context.attrs????context.slots????context.emit??},}Tip:
由于vue3.x向下兼容vue2.x,所以我在嘗試之后發(fā)現(xiàn),一個(gè)vue文件中你可以同時(shí)寫兩個(gè)版本的東西。
import?{?reactive,?computed,?watch,?onMounted?}?from?'vue'export?default?{??name:?'HelloWorld',??props:?{????count:?Number,??},??data?()?{????return?{??????msg:?"我是vue2.x中的this"????}??},??methods:?{????test?()?{??????console.log(this.msg)????}??},??mounted?()?{????console.log('vue2.x?mounted')??},??//?eslint-disable-next-line?no-unused-vars??setup?(props,?val)?{????console.log(this,?'this')?//?undefined????onMounted(()?=>?{??????console.log('vue3.x?mounted')????})????return?{??????...props????}??}}當(dāng)然這邊不推薦你在項(xiàng)目中這么用,但是抱著嘗鮮和探究的態(tài)度,我們勢(shì)必要弄清如果這么寫要注意哪些?
setup中的先執(zhí)行。因?yàn)閟etup() 在解析 2.x 選項(xiàng)前被調(diào)用;
首先在setup中的this將不再指向vue,而是undefined;所以在setup函數(shù)內(nèi)部自然無法訪問到vue實(shí)例上的this。
setup內(nèi)部定義的變量和外表的變量并無沖突;
但是如果你要將其return 暴露給template,那么就會(huì)產(chǎn)生沖突。
reactive
?
接收一個(gè)普通對(duì)象然后返回該普通對(duì)象的響應(yīng)式代理。等同于 2.x 的 Vue.observable()
?
const?obj?=?reactive({?count:?0?})ref
?
接受一個(gè)參數(shù)值并返回一個(gè)響應(yīng)式且可改變的 ref 對(duì)象。ref 對(duì)象擁有一個(gè)指向內(nèi)部值的單一屬性 value。
?
const?count?=?ref(0)console.log(count.value)?//?0count.value++console.log(count.value)?//?1tip:
computed
computed和vue2.x版本保持一致,支持getter和setter
- 傳入一個(gè) getter 函數(shù),返回一個(gè)默認(rèn)不可手動(dòng)修改的 ref 對(duì)象。
- 或者傳入一個(gè)擁有 get 和 set 函數(shù)的對(duì)象,創(chuàng)建一個(gè)可手動(dòng)修改的計(jì)算狀態(tài)。
watchEffect
?
傳入的一個(gè)函數(shù),并且立即執(zhí)行,響應(yīng)式追蹤其依賴,并在其依賴變更時(shí)重新運(yùn)行該函數(shù)。
?
注冊(cè)監(jiān)聽
import?{watchEffect}from?'vue'?//?導(dǎo)入apiconst?count?=?ref(0)?//?定義響應(yīng)數(shù)據(jù)watchEffect(()?=>?console.log(count.value))?//?注冊(cè)監(jiān)聽函數(shù)//?->?打印出?0setTimeout(()?=>?{??count.value++??//?->?打印出?1},?100)注銷監(jiān)聽
- 默認(rèn)情況下是在**組件卸載**的時(shí)候停止監(jiān)聽;- 也可以顯示**調(diào)用返回值**以停止偵聽;
const?stop?=?watchEffect(()?=>?{??/*?...?*/})//?之后stop()清除副作用
> 有時(shí)副作用函數(shù)會(huì)執(zhí)行一些異步的副作用, 這些響應(yīng)需要在其失效時(shí)清除(即完成之前狀態(tài)已改變了)。所以偵聽副作用傳入的函數(shù)可以接收一個(gè) onInvalidate 函數(shù)作入?yún)? 用來注冊(cè)清理失效時(shí)的回調(diào)。
當(dāng)以下情況發(fā)生時(shí),這個(gè)失效回調(diào)會(huì)被觸發(fā):
- 副作用即將重新執(zhí)行時(shí)
- 偵聽器被停止
副作用刷新時(shí)機(jī)
> Vue 的響應(yīng)式系統(tǒng)會(huì)緩存副作用函數(shù),并異步地刷新它們,這樣可以避免同一個(gè) tick 中多個(gè)狀態(tài)改變導(dǎo)致的不必要的重復(fù)調(diào)用。在核心的具體實(shí)現(xiàn)中, 組件的更新函數(shù)也是一個(gè)被偵聽的副作用。當(dāng)一個(gè)用戶定義的副作用函數(shù)進(jìn)入隊(duì)列時(shí), 會(huì)在所有的組件更新后執(zhí)行:
??{{?count?}}在這個(gè)例子中:
- count 會(huì)在初始運(yùn)行時(shí)同步打印出來
- 更改 count 時(shí),將在組件更新后執(zhí)行副作用。
如果副作用需要同步或在組件更新之前重新運(yùn)行,我們可以傳遞一個(gè)擁有 flush 屬性的對(duì)象作為選項(xiàng)(默認(rèn)為 'post'):
//?同步運(yùn)行watchEffect(??()?=>?{????/*?...?*/??},??{????flush:?'sync',??})//?組件更新前執(zhí)行watchEffect(??()?=>?{????/*?...?*/??},??{????flush:?'pre',??})watch
> watch API 完全等效于 2.x this.$watch (以及 watch 中相應(yīng)的選項(xiàng))。watch 需要偵聽特定的數(shù)據(jù)源,并在回調(diào)函數(shù)中執(zhí)行副作用。默認(rèn)情況是懶執(zhí)行的,也就是說僅在偵聽的源變更時(shí)才執(zhí)行回調(diào)。
- 對(duì)比 watchEffect,watch 允許我們:
- 懶執(zhí)行副作用;
- 更明確哪些狀態(tài)的改變會(huì)觸發(fā)偵聽器重新運(yùn)行副作用;
- 訪問偵聽狀態(tài)變化前后的值。
- 偵聽單個(gè)數(shù)據(jù)源
偵聽器的數(shù)據(jù)源可以是一個(gè)擁有返回值的 getter 函數(shù),也可以是 ref:
//?偵聽一個(gè)?getterconst?state?=?reactive({?count:?0?})watch(??()?=>?state.count,??(count,?prevCount)?=>?{????/*?...?*/??})//?直接偵聽一個(gè)?refconst?count?=?ref(0)watch(count,?(count,?prevCount)?=>?{??/*?...?*/})- 偵聽多個(gè)數(shù)據(jù)源
- 與 watchEffect 共享的行為
watch 和 watchEffect 在停止偵聽, 清除副作用 (相應(yīng)地 onInvalidate 會(huì)作為回調(diào)的第三個(gè)參數(shù)傳入),副作用刷新時(shí)機(jī) 和 助聽器調(diào)試 等方面行為一致.
生命周期鉤子函數(shù)
?
可以直接導(dǎo)入 onXXX 一組的函數(shù)來注冊(cè)生命周期鉤子,這些生命周期鉤子注冊(cè)函數(shù)只能在 setup() 期間同步使用,在卸載組件時(shí),在生命周期鉤子內(nèi)部同步創(chuàng)建的偵聽器和計(jì)算狀態(tài)也將自動(dòng)刪除。
?
- 「與 2.x 版本生命周期相對(duì)應(yīng)的組合式 API」
- beforeCreate -> 使用 setup()
- created -> 使用 setup()
- beforeMount -> onBeforeMount
- mounted -> onMounted
- beforeUpdate -> onBeforeUpdate
- updated -> onUpdated
- beforeDestroy -> onBeforeUnmount
- destroyed -> onUnmounted
- errorCaptured -> onErrorCaptured
- 新增的鉤子函數(shù)
- onRenderTracked
- onRenderTriggered
兩個(gè)鉤子函數(shù)都接收一個(gè)DebuggerEvent,與 watchEffect 參數(shù)選項(xiàng)中的 onTrack 和 onTrigger 類似:
export?default?{??onRenderTriggered(e)?{????debugger????//?檢查哪個(gè)依賴性導(dǎo)致組件重新渲染??},}依賴注入
?
provide 和 inject 提供依賴注入,功能類似 2.x 的 provide/inject。兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用。
?
這是本篇文章的重點(diǎn)。結(jié)合項(xiàng)目實(shí)戰(zhàn)以此來探索一下未來的 Vue 狀態(tài)管理模式和邏輯復(fù)用模式。
「用法」
?
provide 和 inject 提供依賴注入,功能類似 2.x 的 provide/inject。兩者都只能在當(dāng)前活動(dòng)組件實(shí)例的 setup() 中調(diào)用。
?
import?{?provide,?inject?}?from?'vue'const?ThemeSymbol?=?Symbol()const?Ancestor?=?{??setup()?{????provide(ThemeSymbol,?'dark')??},}const?Descendent?=?{??setup()?{????const?theme?=?inject(ThemeSymbol,?'light'?/*?optional?default?value?*/)????return?{??????theme,????}??},}inject 接受一個(gè)可選的的默認(rèn)值作為第二個(gè)參數(shù)。如果未提供默認(rèn)值,并且在 provide 上下文中未找到該屬性,則 inject 返回 undefined。
- 「注入的響應(yīng)性」
可以使用 ref 來保證 provided 和 injected 時(shí)間值的響應(yīng):
//?提供者:const?themeRef?=?ref('dark')provide(ThemeSymbol,?themeRef)//?使用者:const?theme?=?inject(ThemeSymbol,?ref('light'))watchEffect(()?=>?{??console.log(`theme?set?to:?${theme.value}`)})如果注入一個(gè)響應(yīng)式對(duì)象,則它的狀態(tài)變化也可以被偵聽。
邏輯組合與復(fù)用
引出問題:
我們通常會(huì)基于一堆相同的數(shù)據(jù)進(jìn)行花樣呈現(xiàn),有列表展示、有餅圖占比、有折線圖趨勢(shì)、有熱力圖說明頻次等等,這些組件使用的是相同的一些數(shù)據(jù)和數(shù)據(jù)處理邏輯。對(duì)于數(shù)據(jù)處理邏輯,目前vue有
- Mixins
- 高階組件 (Higher-order Components, aka HOCs)
- Renderless Components (基于 scoped slots / 作用于插槽封裝邏輯的組件)
但是上面的方案是存在一些弊端:
項(xiàng)目預(yù)覽
源碼:https://github.com/961998264/todolist-vue-3.0
項(xiàng)目介紹
項(xiàng)目src目錄
hooks文件夾是專門放hook的
context文件夾以模塊劃分
先來看下context編寫(我這邊是用的ts)
import?{?provide,?ref,?Ref,?inject,?computed,?}?from?'vue'?//vue?apiimport?{?getListApi?}?from?'api/home'?//?mock的api//?以下為定義的ts類型,你也可以單獨(dú)建一個(gè)專門定義類型的文件。type?list?=?listItem[]interface?listItem?{??title:?string,??context:?string,??id:?number,??status:?number,}interface?ListContext?{??list:?Ref,??getList:?()?=>?{},??changeStatus:?(id:?number,?status:?number)?=>?void,??addList:?(item:?listItem)?=>?void,??delList:?(id:?number)?=>?void,??finished:?Ref,??unFinish:?Ref,??setContext:?(id:?number,?context:?string)?=>?void,??setActiveItem:?()?=>?void,}provide名稱,推薦用Symbol
const?listymbol?=?Symbol()提供provide的函數(shù)
export?const?useListProvide?=?()?=>?{??//?全部事件???const?list?=?ref([]);??//?當(dāng)前查看的事件id??const?activeId?=?ref(null)??//?當(dāng)前查看的事件??const?activeItem?=?computed(()?=>?{????if?(activeId.value?||?activeId.value?===?0)?{??????const?item?=?list.value.filter((item:?listItem)?=>?item.id?===?activeId.value)??????return?item[0]????}?else?{??????return?null????}??})??//?獲取list??const?getList?=?async?function?()?{????const?res:?any?=?await?getListApi()????console.log("useListProvide?->?res",?res)????if?(res.code?===?0)?{??????list.value?=?res.data????}??}??//?新增list??const?addList?=?(item:?listItem)?=>?{????list.value.push(item)??}??//修改狀態(tài)??const?changeStatus?=?(id:?number,?status:?number)?=>?{????console.log('status',?status)????const?removeIndex?=?list.value.findIndex((listItem:?listItem)?=>?listItem.id?===?id)????if?(removeIndex?!==?-1)?{??????list.value[removeIndex].status?=?status????}??};??//?修改事件信息??const?setContext?=?(id:?number,?context:?string)?=>?{????const?Index?=?list.value.findIndex((listItem:?listItem)?=>?listItem.id?===?id)????if?(Index?!==?-1)?{??????list.value[Index].context?=?context????}??}??//?刪除事件??const?delList?=?(id:?number)?=>?{????console.log("delList?->?id",?id)????for?(let?i?=?0;?i??{????return?list.value.filter(item?=>?item.status?===?0)??})??//?已完成事件列表??const?finished?=?computed(()?=>?{????return?list.value.filter(item?=>?item.status?===?1)??})????provide(listymbol,?{????list,????unFinish,????finished,????changeStatus,????getList,????addList,????delList,????setContext,????activeItem,????activeId??})}在這個(gè)函數(shù)中定義 待辦事件,并且定義一系列增刪改查函數(shù),通過provide暴露出去。
提供inject的函數(shù)
export?const?useListInject?=?()?=>?{??const?listContext?=?inject(listymbol);??if?(!listContext)?{????throw?new?Error(`useListInject?must?be?used?after?useListProvide`);??}??return?listContext};全局狀態(tài)肯定不止一個(gè)模塊,所以在 context/index.ts 下做統(tǒng)一的導(dǎo)出
import?{?useListProvide,?useListInject?}?from?'./home/index'console.log("useListInject",?useListInject)export?{?useListInject?}export?const?useProvider?=?()?=>?{??useListProvide()}然后在 App.vue 的根組件里使用 provide,在最上層的組件中注入全局狀態(tài)。
import { useProvider } from './context/index'export default { name: 'App', setup () { useProvider() return { } }}在組件中獲取數(shù)據(jù):
import?{?useListInject?}?from?'../../context/home/index'setup?()?{??const?{?list,?changeStatus,?getList,?unFinish,?finished,?addList,?a???ctiveItem,?setContext?}?=?useListInject()}不管是父子組件還是兄弟組件,或者是比關(guān)系套更深的組件,我們都可以通過useListInject來獲取到相應(yīng)式的數(shù)據(jù)。
作者:Mingle
轉(zhuǎn)發(fā)鏈接:https://mp.weixin.qq.com/s/iOq-eeyToDXJ6lvwnC12DQ
總結(jié)
以上是生活随笔為你收集整理的onmounted vue3_基于项目时间阐述vue3.0新型状态管理和逻辑复用方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: python无师自通配套资源_Pytho
- 下一篇: 祝福哥哥升学宴的祝福语