我回了橙子建站的驗(yàn)證碼上海疫情最新情況
VueX 簡介
Vue官方:狀態(tài)管理工具
狀態(tài)管理是什么
需要在多個組件中共享的狀態(tài)、且是響應(yīng)式的、一個變,全都改變。
例如一些全局要用的的狀態(tài)信息:用戶登錄狀態(tài)、用戶名稱、地理位置信息、購物車中商品、等等
這時(shí)候我們就需要這么一個工具來進(jìn)行全局的狀態(tài)管理,Vuex就是這樣的一個工具。
單頁面的狀態(tài)管理
View–>Actions—>State
視圖層(view)觸發(fā)操作(action)更改狀態(tài)(state)響應(yīng)回視圖層(view)
多頁狀態(tài)管理
vuex store對象屬性介紹
vue3 中的 setup 在 beforecreate 和 created 前執(zhí)行,此時(shí) vue對象還未被創(chuàng)建,沒有了之前的this,所以此處我們需要用到另一種方法來獲取到 store 對象。
import { useStore } from 'vuex' // 引入useStore 方法
const store = useStore() // 該方法用于返回store 實(shí)例
console.log(store) // store 實(shí)例對象
1.?state
存放數(shù)據(jù)的地方
state: {
? count: 100,
? num: 10
},
可以在 state 中直接進(jìn)行數(shù)據(jù)變化的操作,但Vue不建議這么做。因?yàn)閷τ趘ue開發(fā)工具 devtools 來說,直接在state中進(jìn)行數(shù)據(jù)改變,devtools是跟蹤不到的。vuex中希望通過 action(進(jìn)行異步操作)或是 mutations(同步操作)來進(jìn)行數(shù)據(jù)改變的操作,這樣在 devtools 中才能直接觀察出數(shù)據(jù)的變化以及記錄,方便開發(fā)者調(diào)試。
另外,在vue3 中對state 中對象新增屬性或刪除時(shí),不再需要通過?vue.set()
?, 或是?vue.delete()
?來進(jìn)行對象的響應(yīng)式處理了,直接新增的對象屬性已經(jīng)具有響應(yīng)式
2.?mutations
vuex的store狀態(tài)更新的唯一方式:提交 mutation
同步操作可以直接在mutatuions中直接進(jìn)行
mutions 主要包含2部分:
-
字符串的事件類型 (type)
-
一個**回調(diào)函數(shù)(handler)**該回調(diào)函數(shù)的第一個參數(shù)是 state
mutations: {
// 傳入 state
increment (state) {
state.count++
}
}
template 中通過?$store.commit('方法名')
?觸發(fā)
// 導(dǎo)入 useStore 函數(shù)
import { useStore } from 'vuex'
const store = useStore()
store.commit('increment')
mution 的參數(shù)與傳參方法
mution 接收參數(shù)直接寫在定義的方法里邊即可接受傳遞的參數(shù)
// ...state定義count
mutations: {sum (state, num) {state.count += num}
}
通過 commit 的payload 進(jìn)行參數(shù)傳遞
使用?store.commit('mution中函數(shù)名', '需要傳遞的參數(shù)' )
?在commit里添加參數(shù)的方式進(jìn)行傳遞
<h2>{{this.$store.state.count}}</h2>
<button @click="add(10)">++</button>
...
<script setup>
// 獲取store實(shí)例,獲取方式看上邊獲取store實(shí)例方法
const add = (num) => {store.commit('sum', num)
}
</script>
mution 的提交風(fēng)格
前面提到了 mution 主要包含 type 和 回調(diào)函數(shù) 兩部分, 和通過commit payload的方式進(jìn)行參數(shù)傳遞(提交),下面我們可以
用這種方式進(jìn)行 mution 的提交
const add = (num) => {store.commit({type: 'sum', // 類型就是mution中定義的方法名稱num})
}...
mutations: {sum (state, payload) {state.count += payload.num}
}
?3.?actions
異步操作在action中進(jìn)行,再傳遞到mutation
action基本使用如下:
action 中定義的方法默認(rèn)參數(shù)為**?context
?上下文**, 可以理解為 store 對象
通過 context 上下文對象,拿到store,通過?commit
?觸發(fā) mution 中的方法,以此來完成異步操作
...
mutations: {sum (state, num) {state.count += num}
},
actions: {// context 上下文對象,可以理解為storesum_actions (context, num) {setTimeout(() => {context.commit('sum', num) // 通過context去觸發(fā)mutions中的sum}, 1000)}
},
在template 中通過dispatch
?調(diào)用action 中定義的sum_action 方法
// ...template
store.dispatch('sum_actions', num)
通過 promise 實(shí)現(xiàn)異步操作完成,通知組件異步執(zhí)行成功或是失敗。
// ...
const addAction = (num) => {store.dispatch('sum_actions', {num}).then((res) => {console.log(res)}).catch((err) => {console.log(err)})
}
sun_action方法返回一個promise,當(dāng)累加的值大于30時(shí)不再累加,拋出錯誤。
actions: {sum_actions (context, payload) {return new Promise((resolve, reject) => {setTimeout(() => {// 通過 context 上下文對象拿到 countif (context.state.count < 30) {context.commit('sum', payload.num)resolve('異步操作執(zhí)行成功')} else {reject(new Error('異步操作執(zhí)行錯誤'))}}, 1000)})}},
4.?getters
類似于組件的計(jì)算屬性
import { createStore } from 'vuex'export default createStore({state: {students: [{ name: 'mjy', age: '18'}, { name: 'cjy', age: '22'}, { name: 'ajy', age: '21'}]},getters: {more20stu (state) { return state.students.filter(item => item.age >= 20)}}
})
使用 通過$store.getters.方法名
?進(jìn)行調(diào)用
//...template
<h2>{{$store.getters.more20stu}}</h2> // 展示出小于20歲的學(xué)生
getters 的入?yún)? getters 可以接收兩個參數(shù),一個是?state
, 一個是自身的?getters
?,并對自身存在的方法進(jìn)行調(diào)用。
getters: {more20stu (state, getters) { return getters.more20stu.length}
}
getters 的參數(shù)與傳參方法
上面是getters固定的兩個參數(shù),如果你想給getters傳遞參數(shù),讓其篩選大于 age 的人,可以這么做
返回一個 function 該 function 接受 Age,并處理
getters: {more20stu (state, getters) { return getters.more20stu.length},moreAgestu (state) {return function (Age) {return state.students.filter(item =>item.age >= Age)}}// 該寫法與上邊寫法相同但更簡潔,用到了ES6中的箭頭函數(shù),如想了解es6箭頭函數(shù)的寫法// 可以看這篇文章 https://blog.csdn.net/qq_45934504/article/details/123405813?spm=1001.2014.3001.5501moreAgestu_Es6: state => {return Age => {return state.students.filter(item => item.age >= Age)}}
}
使用
//...template
<h2>{{$store.getters.more20stu}}</h2> // 展示出小于20歲的學(xué)生
<h2>{{$store.getters.moreAgestu(18)}}</h2> // 通過參數(shù)傳遞, 展示出年齡小于18的學(xué)生
5.?modules
當(dāng)應(yīng)用變得復(fù)雜時(shí),state中管理的變量變多,store對象就有可能變得相當(dāng)臃腫。
為了解決這個問題,vuex允許我們將store分割成模塊化(modules),而每個模塊擁有著自己的state、mutation、action、getters等
在store文件中新建modules文件夾
在modules中可以創(chuàng)建單一的模塊,一個模塊處理一個模塊的功能
store/modules/user.js 處理用戶相關(guān)功能
store/modules/pay.js 處理支付相關(guān)功能
store/modules/cat.js 處理購物車相關(guān)功能
// user.js模塊
// 導(dǎo)出
export default {namespaced: true, // 為每個模塊添加一個前綴名,保證模塊命明不沖突 state: () => {},mutations: {},actions: {}
}
最終通過?store/index.js
?中進(jìn)行引入
// store/index.js
import { createStore } from 'vuex'
import user from './modules/user.js'
import user from './modules/pay.js'
import user from './modules/cat.js'
export default createStore({modules: {user,pay,cat}
})
在template中模塊中的寫法和無模塊的寫法大同小異,帶上模塊的名稱即可
<h2>{{$store.state.user.count}}</h2>store.commit('user/sum', num) // 參數(shù)帶上模塊名稱
store.dispatch('user/sum_actions', sum)
?
一、使用vuex
vuex的安裝:
npm i vuex
vuex的配置,@/store/index.js:
import {createStore} from 'vuex'//導(dǎo)入createStore構(gòu)造函數(shù)
export default createStore({ state:{ //Vuex的狀態(tài),實(shí)際上就是存數(shù)據(jù)的地方person:{name:'jack',age:200}},getters:{ //提供獲取Vux狀態(tài)的方式, 注意在組件中調(diào)用時(shí)getPerson是以屬性的方式被訪問getPerson(state){return state.person}},mutations:{ //提供直接操作Vuex的方法,注意mutations里的方法中不能有任何異步操做ageGrow(state, value){//第一個參數(shù)state為Vuex狀態(tài);第二個參數(shù)為commit函數(shù)傳來的值state.person.age += value}},actions:{ //提供通過mutations方法來簡介操作Vuex的方法ageGrow(context, value){ //第一個參數(shù)context為上下文,提供一些方法;第二個參數(shù)為dispatch函數(shù)傳來的值context.commit('ageGrow', value)}},
})
在@/main.js中引入:
import { createApp } from 'vue'
import App from './App.vue'
import store from './store'
const app = createApp(App)
app.use(store)
app.mount('#app')
在組件中使用Vuex:
<template><h1>名字:{{person.name}}--年齡:{{person.age}}</h1><input type="text" v-model="value"><button @click="ageGrow(value)">增加年齡</button><!-- 在input框輸入一個數(shù)字,點(diǎn)擊按鈕,可以看到年齡發(fā)生變化,說明Vuex正常工作 -->
</template><script>import {useStore} from 'vuex' import {ref} from 'vue'export default {setup(){const store = useStore() //獲取store對象let person = store.getters.getPerson //從組件中獲取狀態(tài)(數(shù)據(jù))person 方式一// let person = store.state.person //從組件中獲取狀態(tài)(數(shù)據(jù))person 方式二let value = ref('輸入年齡的增量')function ageGrow(ageGrowth){ageGrowth = parseInt(ageGrowth)if(isNaN(ageGrowth)){ageGrowth = 0}store.dispatch('ageGrow', ageGrowth)//通過dispatch來調(diào)用actions里的'ageGrow'方法,參數(shù)為ageGrowth//actions的方法又會通過commit來調(diào)用mutations里的方法,從而引起狀態(tài)(數(shù)據(jù))的變化//也可以在組件里跳過dispatch actions,直接store.commit}return {person, value,ageGrow}}}
</script>
<style></style>
小結(jié):安裝完vuex之后,首先要用creatRouter構(gòu)造函數(shù)創(chuàng)建一個router對象,并在main.js中引入這個對象。然后在組件中,通過userStore方法來獲取這個router對象,進(jìn)一步通過getter或者state可以得到Vuex狀態(tài)(數(shù)據(jù)),通過dispatch->actions->mutations->state的數(shù)據(jù)傳送方式可以操作和改變Vuex的狀態(tài)(數(shù)據(jù))
2. Module
Vuex官方原話:“由于使用單一狀態(tài)樹,應(yīng)用的所有狀態(tài)會集中到一個比較大的對象。當(dāng)應(yīng)用變得非常復(fù)雜時(shí),store 對象就有可能變得相當(dāng)臃腫?!?/p>
什么叫單一狀態(tài)樹呢,其實(shí)就是上文中的state對象。在Vuex的基本使用中,我們使用state對象來存儲Vuex的狀態(tài),state對象里面可以嵌套其他的對象,它是一個樹形的結(jié)構(gòu),而且這個state對象是唯一的,所有的狀態(tài)都要存儲在這一個對象里。因此,我們稱之為單一狀態(tài)樹。
這種單一狀態(tài)樹的弊端是顯而易見的,對于中大型項(xiàng)目來說,要托管給Vuex的狀態(tài)有很多,把這些海量的數(shù)據(jù)如果都塞到一個文件里面的一個對象里面,未免顯得過于臃腫,不管是開發(fā)起來還是維護(hù)起來都會有很多不變。
對此,官方給出了解決方案:
?
“為了解決以上問題,Vuex 允許我們將 store 分割成模塊(module)?。每個模塊擁有自己的 state、mutation、action、getter、甚至是嵌套子模塊——從上至下進(jìn)行同樣方式的分割”
2.1 vuex中模塊化的基本使用
vuex的模塊化沒什么難理解的,就是把store給拆開,一個對象拆成多個對象,一個文件拆成多個文件,并且彼此可以擁有獨(dú)立的命名空間。
道理很簡單,所以直接看樣例:
文件結(jié)構(gòu)
├──src├── components│ └── Test.vue└── store├── index.js└── modules├── male.js└── female.js
index.js
import {createStore} from 'vuex'
import female from './modules/female' //導(dǎo)入模塊
import male from './modules/male' //導(dǎo)入模塊
export default createStore({ modules:{ //使用模塊female,male}
})
male.js
export default {namespaced:true, //namespaced:true代表該模塊帶有獨(dú)立命名空間state:{ //否則,默認(rèn)是處于全局命名空間,就和非模塊化一樣personList:[{name:'張飛', id:'004'},{name:'武則天', id:'005'},{name:'秀吉', id:'006'},]},mutations:{addMutation(state, value){ //往personList中添加一個人state.personList.push(value)},removeMutaion(state, value){ //往personList中刪除一個人state.personList = state.personList.filter((el) => el.id != value.id)}},actions:{addAction(context, value){setTimeout(() => {context.commit('addMutation', value) // ->'male/addMutation'}, 1000);},removeAction(context, value){context.commit('removeMutaion', value)}},getters:{personList(state){return state.personList}}
}
female.js
export default {namespaced:true, //namespaced:true代表該模塊帶有獨(dú)立命名空間state:{ //否則,默認(rèn)是處于全局命名空間,就和非模塊化一樣personList:[{name:'李白', id:'001'},{name:'孫尚香', id:'002'},{name:'大喬', id:'003'},]},mutations:{addMutation(state, value){ //往personList中添加一個人state.personList.push(value)},removeMutaion(state, value){ //往personList中刪除一個人state.personList = state.personList.filter((el) => el.id != value.id)}},actions:{addAction(context, value){setTimeout(() => {context.commit('addMutation', value) // ->'female/addMutation'}, 1000);},removeAction(context, value){context.commit('removeMutaion', value)}},getters:{personList(state){return state.personList}}
}
Test.vue
<template><h1>女人:</h1><li v-for="femalePerson in femalePersons" :key="femalePerson.id">{{femalePerson.name}}<button @click="addToMale(femalePerson)">添加到男人</button></li><h1>男人:</h1><li v-for="malePerson in malePersons" :key="malePerson.id">{{malePerson.name}}<button @click="addToFemale(malePerson)">添加到女人</button></li><!-- 有兩個列表,分布是男人和女人,通過點(diǎn)擊按鈕可以把列表中的某些項(xiàng)添加到另一個列表中 --><!-- 建議粘貼復(fù)制并運(yùn)行代碼,這樣更直觀 -->
</template><script>import { computed } from '@vue/runtime-core';import {useStore} from 'vuex'export default {setup(){let store = useStore()let malePersons = computed(() => store.getters['male/personList']) //通過getter獲取statelet femalePersons = computed(() => store.state.female.personList) //直接獲取statefunction addToMale(person){store.dispatch('male/addAction', person)store.dispatch('female/removeAction', person)//如果模塊中namespaced === true,那么要在方法名前面添加模塊的邏輯路徑//index.js里使用的模塊為路徑的起點(diǎn)。//比如index里面有一個moduleA,moduleA有一個子模塊moduleB,module有一個action是actionx//那么調(diào)用方式為 store.dispatch('moduleA/moduleB/actionx', value)}function addToFemale(person){store.dispatch('female/addAction', person)store.dispatch('male/removeAction', person)}return {malePersons,femalePersons,addToMale,addToFemale}}}
</script>
<style></style>
2.2 在命名空間中訪問全局內(nèi)容
什么是全局內(nèi)容?不在同一個模塊中的內(nèi)容就是全局內(nèi)容。
比如,對于上文中的female模塊來說,male模塊中的getters state action mutations就是全局內(nèi)容,接下來將會講解如何在一個模塊中訪問到全局內(nèi)容。
為了便于理解,我創(chuàng)造了一個新的樣例:
├──src├── components│ └── Test.vue└── store├── index.js└── modules├── moduleA.js└── moduleB.js
index是所有的模塊的根,moduleA和moduleB是兩個子模塊,接下來要做的事情就是在index.js、moduleA.js、moduleB.js中寫一些getters state action mutations,最終達(dá)成的效果是在index中訪問moduleA的內(nèi)容,在moduleA中訪問moduleB的內(nèi)容,在moduleB中訪問index的內(nèi)容。
Test.vue:
<template><li> {{rootModule.name}}---{{rootModule.num}} <button @click="rootClick">rootAction---A++</button> </li><li> {{moduleA.name}}---{{moduleA.num}} <button @click="aClick">moduleAction---B++</button> </li><li> {{moduleB.name}}---{{moduleB.num}} <button @click="bClick">moduleAction---root++</button> </li><!-- 點(diǎn)擊root,moduleA數(shù)字加一;點(diǎn)擊moduleA,moduleB數(shù)字加一;點(diǎn)擊moduleB,root數(shù)字加一 --><button @click="store.dispatch('globalAction')">觸發(fā)全局action</button>
</template><script>import {useStore} from 'vuex'export default {setup(){let store = useStore()console.log(store.getters);let rootModule = store.getters['moduleB/rootStateThroughModuleB'].info //通過moduleB的getters獲得root的狀態(tài)let moduleA = store.getters['moduleAStateThroughRoot'].info //通過root的getters獲得moduleA的狀態(tài)let moduleB = store.getters['moduleA/moduleBStateThroughModuleA'].info //通過moduleA的getters獲得moduleB的狀態(tài)// let moduleB = store.state.moduleB.info// let moduleA = store.state.moduleA.info// let rootModule = store.state.infofunction rootClick(){store.dispatch('addAction', 1)} //調(diào)用root中的action,改變的是moduleA的狀態(tài)function aClick(){store.dispatch('moduleA/addAction', 1)}//調(diào)用moduleA中的action,改變的是moduleB的狀態(tài)function bClick(){store.dispatch('moduleB/addAction', 1)}//調(diào)用moduleB中的action,改變的是root的狀態(tài)return {rootModule,moduleA,moduleB,rootClick,aClick,bClick,store}}}
</script>
<style></style>
3. vuex的typescript用法
vuex的typescript用法其實(shí)就是把state加上ts里的類型限制
3.1 不使用模塊化
store文件
// store.ts
import { InjectionKey } from 'vue'
import { createStore, Store } from 'vuex'// 為 store state 聲明類型
export interface State {count: number
}// 定義 injection key
export const key: InjectionKey<Store<State>> = Symbol()export const store = createStore<State>({state: {count: 0}
})
在main.ts里引入
// main.ts
import { createApp } from 'vue'
import { store, key } from './store'const app = createApp({ ... })// 傳入 injection key
app.use(store, key)app.mount('#app')
在組件中使用useStore()獲取store,將上述 injection key 傳入 useStore 方法可以獲取類型化的 store。
// vue 組件
import { useStore } from 'vuex'
import { key } from './store'export default {setup () {const store = useStore(key)store.state.count // 類型為 number}
}
在每個組件中都導(dǎo)入key有些重復(fù)且麻煩,我們可以將useStore()封裝成myUseStore()
// 在store.ts增加下面幾行
import { useStore } from 'vuex'
export function myUseStore () {return useStore(key)
}
3.2 使用模塊化
store/index.ts
import { createStore, Store } from 'vuex'
import moduleA from './modules/moduleA'
import moduleB from './modules/moduleB'
import { InjectionKey } from 'vue'//模塊A state的類型
interface moduleAState {name:string,age:number
}//模塊B state的類型
interface moduleBState {id:string,adult:boolean
}//vuex state類型
interface State{moduleA:moduleAState,moduleB:moduleBState
}//如果使用模塊化的話,在createStore參數(shù)里面就不要寫state了,否則會報(bào)錯
export const key: InjectionKey<Store<State>> = Symbol()
export const store = createStore<State>({// state:{}, //不可以加這一行,否則報(bào)錯modules: {moduleA,moduleB}
})
在組件中訪問:
// vue 組件
import { useStore } from 'vuex'
import { key } from './store'export default {setup () {const store = useStore(key)store.state.moduleA// 類型為 moduleAStatestore.state.moduleB// 類型為 moduleBState}
}
Pinia 簡介?
Pinia是vue生態(tài)里Vuex的替代者,一個全新的vue狀態(tài)管理庫。在Vue3成為正式版以后,尤雨溪強(qiáng)勢推薦的項(xiàng)目就是Pinia。
那先來看看Pinia比Vuex好的地方,也就是Pinia的五大優(yōu)勢。
可以對Vue2和Vue3做到很好的支持,也就是老項(xiàng)目也可以使用Pinia。
拋棄了Mutations的操作,只有state、getters和actions.極大的簡化了狀態(tài)管理庫的使用,讓代碼編寫更加容易直觀。
不需要嵌套模塊,符合Vue3的Composition api ,讓代碼更加扁平化。
完整的TypeScript支持。Vue3版本的一大優(yōu)勢就是對TypeScript的支持,所以Pinia也做到了完整的支持。如果你對Vuex很熟悉的化,一定知道Vuex對TS的語法支持不是完整的(經(jīng)常被吐槽)。
代碼更加簡潔,可以實(shí)現(xiàn)很好的代碼自動分割。Vue2的時(shí)代,寫代碼需要來回翻滾屏幕屏幕找變量,非常的麻煩,Vue3的Composition api完美了解決這個問題。 可以實(shí)現(xiàn)代碼自動分割,pinia也同樣繼承了這個優(yōu)點(diǎn)。
安裝和配置Pinia
安裝和配置Pinia非常簡單,像其他Vue插件一樣,Pinia需要通過yarn或npm進(jìn)行安裝并且與Vue應(yīng)用程序進(jìn)行綁定,可以使用以下命令進(jìn)行安裝:
yarn add pinia
# 或者使用 npm
npm install pinia
?
在安裝完P(guān)inia包之后,需要在main.ts文件中導(dǎo)入createPinia函數(shù)并將Pinia插件與Vue應(yīng)用程序綁定,如下所示:
import { createApp } from 'vue';
import { createPinia } from 'pinia';
import App from './App.vue';const app = createApp(App);const pinia = createPinia();
app.use(pinia);app.mount('#app');
使用 createPinia() 函數(shù)創(chuàng)建并初始化Pinia插件實(shí)例,將其與Vue應(yīng)用程序綁定使用app.use(pinia)。至此,我們就可以使用Pinia來管理Vue應(yīng)用程序的狀態(tài)了。
Pinia的核心
Store
Store是 Pinia 中管理狀態(tài)的核心概念。它相當(dāng)于一個 Vue 組件中的狀態(tài),但是 Store是一個獨(dú)立的模塊。
Store 是用 defineStore() 定義的,它的第一個參數(shù)要求是一個獨(dú)一無二的名字,這個名字 ,也被用作 id ,是必須傳入的, Pinia 將用它來連接 store 和 devtools。為了養(yǎng)成習(xí)慣性的用法,將返回的函數(shù)命名為 use… 是一個符合組合式函數(shù)風(fēng)格的約定。
defineStore() 的第二個參數(shù)可接受兩類值:Setup 函數(shù)或 Option 對象。
定義Store的示例代碼:
import { defineStore } from 'pinia'// 你可以對 `defineStore()` 的返回值進(jìn)行任意命名,但最好使用 store 的名字,
同時(shí)以 `use` 開頭且以 `Store` 結(jié)尾。
(比如 `useUserStore`,`useCartStore`,`useProductStore`)
// 第一個參數(shù)是你的應(yīng)用中 Store 的唯一 ID。
export const useAlertsStore = defineStore('alerts', {// 其他配置...
})
- defineStore( ) 方法的第一個參數(shù):相當(dāng)于為容器起一個名字。注意:這里的名字必須唯一,不能重復(fù)。
- defineStore( ) 方法的第二個參數(shù):可以簡單理解為一個配置對象,里邊是對容器倉庫的配置說明。當(dāng)然這種說明是以對象的形式。
- state 屬性: 用來存儲全局的狀態(tài)的,這里邊定義的,就可以是為SPA里全局的狀態(tài)了。
- getters屬性: 用來監(jiān)視或者說是計(jì)算狀態(tài)的變化的,有緩存的功能。
- actions屬性: 對state里數(shù)據(jù)變化的業(yè)務(wù)邏輯,需求不同,編寫邏輯不同。說白了就是修改state全局狀態(tài)數(shù)據(jù)的。
?
State
State 是 store 中存儲數(shù)據(jù)的地方。通過定義 State,可以在 store 的任何位置訪問和修改數(shù)據(jù)。
在 Pinia 中,state 被定義為一個返回初始狀態(tài)的函數(shù)。這使得 Pinia 可以同時(shí)支持服務(wù)端和客戶端。
定義State的示例代碼如下:
import { defineStore } from 'pinia'const useStore = defineStore('storeId', {// 為了完整類型推理,推薦使用箭頭函數(shù)state: () => {return {// 所有這些屬性都將自動推斷出它們的類型count: 0,name: 'Eduardo',isAdmin: true,items: [],hasChanged: true,}},
})
Getter
Getter 用來獲取從 state 派生的數(shù)據(jù),類似于 Vue 組件中的 computed 計(jì)算屬性??梢酝ㄟ^ defineStore() 中的 getters 屬性來定義它們。推薦使用箭頭函數(shù),并且它將接收 state 作為第一個參數(shù):
export const useStore = defineStore('main', {state: () => ({count: 0,}),getters: {doubleCount: (state) => state.count * 2,},
})
Action
Action 相當(dāng)于組件中的 方法。它們可以通過 defineStore() 中的 actions 屬性來定義;Action 是一種將異步操作封裝在 store中的方式,它是一個可以被調(diào)用的函數(shù),也可以接收參數(shù)并修改 store 中的狀態(tài)。 Action應(yīng)該始終是同步的,并返回一個 Promise 對象,以便在處理異步操作時(shí)能夠很好地處理結(jié)果。
Pinia 中的 Action 由 defineStore 創(chuàng)建,可以通過在 actions 中定義它們來使用它們。例如,下面是一個 store 中的 Action 定義:
import { defineStore } from 'pinia'export const myStore = defineStore('myStore',{ state: () => ({message: 'Hello',}),actions: {async fetchMessage() {const response = await fetch('http://127.0.0.1:5173/message')const data = await response.json()this.message = data.message},},
})
在上面的示例中,我們?yōu)?myStore 定義了一個 Action , fetchMessage() ,它會從后臺 API 中獲取數(shù)據(jù),并更新 store 中的狀態(tài)。然后,我們可以從組件或其他 Action 中調(diào)用該 Action :
import { useStore } from 'pinia'export default {setup() {const store = useStore('myStore')function handleClick() {store.fetchMessage()}return {handleClick,}},
}
創(chuàng)建和使用Pinia
創(chuàng)建Pinia
前面我們已經(jīng)安裝和配置好了Pinia,在創(chuàng)建Pinia之前,為了代碼的統(tǒng)一管理和可維護(hù)性,我們依然先創(chuàng)建一個store文件夾,然后在來創(chuàng)建相關(guān)的Pinia,具體步驟如下
- 在src文件夾下新建store文件夾,后面所有涉及需要Pinia進(jìn)行狀態(tài)管理的代碼都放在該文件夾下
- 在store文件夾下新建movieListStore.js文件,創(chuàng)建完成后,打開該文件
- 在movieListStore.js文件中引入Pinia中的defineStore 方法
?
import { defineStore } from 'pinia'
創(chuàng)建defineStore 對象,定義一個useMovieListStore用于接收defineStore創(chuàng)建的對象,并將其通過export default 導(dǎo)出
const useMovieListStore = defineStore('movie',{ state: () => ({isShow: true,movies: [],}),getters: {getIsShow() {return this.isShow},getMovies() {return this.movies},},actions: {setIsShow(value) {this.isShow = value},async fetchMovies() {const response = await fetch('https://api.movies.com/movies')const data = await response.json()this.movies = data},},
})
export default useMovieListStore
注意:
這里需要注意,官方建議我們在定義鉤子函數(shù)時(shí),建議使用use開頭Store結(jié)尾的命名方式來對上面創(chuàng)建的對象進(jìn)行命名,如上面的useMovieListStore
使用Pinia
前面我們已經(jīng)創(chuàng)建好了Pinia,接下來,我們就可以在組件中使用了。
在Vue組件中使用store,我們需要通過 useStore() 函數(shù)訪問store的實(shí)例。
在Vue組件中使用Pinia的步驟如下
1 先使用 import 引入Pinia 中的 useStore
import { useStore } from 'pinia'
2? 創(chuàng)建useStore對象
const store = useStore('movie')
- 在需要獲取狀態(tài)的地方通過上面定義的store.getIsShow()獲取狀態(tài)
return {isShow: store.getIsShow(),
}
Menu.vue中完整的示例代碼如下:
<template><nav><ul><li v-show="isShow">{{ $route.name }} </li><li><router-link to="/">Home</router-link></li><li><router-link to="/movies">Movies</router-link></li></ul></nav>
</template><script>
import { defineComponent } from 'vue'
import { useStore } from 'pinia'export default defineComponent({name: 'Menu',setup() {const store = useStore('movie')return {isShow: store.getIsShow(),}},
})
</script>
Pinia的Setup Store方式定義 Store
Setup Store與Option Store稍有不同,它與 Vue 組合式 API 的 setup 函數(shù) 相似,我們通過傳入一個函數(shù),該函數(shù)定義了一些響應(yīng)式屬性和方法,并且返回一個帶有我們想暴露出去的屬性和方法的對象。示例代碼如下:
export const useCounterStore = defineStore('counter', () => {const count = ref(0)function increment() {count.value++}return { count, increment }
})
在 Setup Store 中:
- ref() 就是 state 屬性
- computed() 就是 getters
- function() 就是 actions
pinia在API里的使用
?1.$reset :重置到初始值
這個 $reset 可以將 state 的數(shù)據(jù)初始到初始值,比如我們有一個數(shù)據(jù),點(diǎn)擊按鈕改變了,然后我們可以通過這個 API ,將數(shù)據(jù)恢復(fù)到初始狀態(tài)值。
??2.$subscribe:監(jiān)聽 state 數(shù)據(jù)變化
$subscribe 使用來監(jiān)聽的,監(jiān)聽 state 數(shù)據(jù)的變化,只要 state 里面的數(shù)據(jù)發(fā)生了變化,就會自動走這個函數(shù)。
? ?3.$onAction:一調(diào)用 actions 就觸發(fā)
這個看名字就很好理解了吧,就是 action 一調(diào)用就會被觸發(fā)。
它里面只有一個參數(shù) args。寫一下關(guān)鍵代碼吧。
Pinia 與 VueX的區(qū)別與優(yōu)缺點(diǎn):
pinia和vuex的區(qū)別
(1)pinia它沒有mutation,他只有state,getters,action【同步、異步】使用他來修改state數(shù)據(jù)
?(2)pinia他默認(rèn)也是存入內(nèi)存中,如果需要使用本地存儲,在配置上比vuex麻煩一點(diǎn)
?(3)pinia語法上比vuex更容易理解和使用,靈活。
?(4)pinia沒有modules配置,沒一個獨(dú)立的倉庫都是definStore生成出來的
?(5)pinia state是一個對象返回一個對象和組件的data是一樣的語法
?
Vuex 和 Pinia 的優(yōu)缺點(diǎn)
Pinia的優(yōu)點(diǎn)
完整的 TypeScript 支持:與在 Vuex 中添加 TypeScript 相比,添加 TypeScript 更容易
極其輕巧(體積約 1KB)
store 的 action 被調(diào)度為常規(guī)的函數(shù)調(diào)用,而不是使用 dispatch 方法或 MapAction 輔助函數(shù),這在 Vuex 中很常見
支持多個Store
支持 Vue devtools、SSR 和 webpack 代碼拆分
Pinia的缺點(diǎn)
不支持時(shí)間旅行和編輯等調(diào)試功能? ?
Vuex的優(yōu)點(diǎn)
- 支持調(diào)試功能,如時(shí)間旅行和編輯
- 適用于大型、高復(fù)雜度的Vue.js項(xiàng)目
- 從 Vue 3 開始,getter 的結(jié)果不會像計(jì)算屬性那樣緩存
- Vuex 4有一些與類型安全相關(guān)的問題
何時(shí)使用Pinia,何時(shí)使用Vuex
個人感覺:,由于Pinea是輕量級的,體積很小,它適合于中小型應(yīng)用。它也適用于低復(fù)雜度的Vue.js項(xiàng)目,因?yàn)橐恍┱{(diào)試功能,如時(shí)間旅行和編輯仍然不被支持。
將 Vuex 用于中小型 Vue.js 項(xiàng)目是過度的,因?yàn)樗亓考壍?#xff0c;對性能降低有很大影響。因此,Vuex 適用于大規(guī)模、高復(fù)雜度的 Vue.js 項(xiàng)目。
?
Vue3使用Vuex_vue3 vuex官網(wǎng)_BigJF的博客-CSDN博客
Vue3中Vuex的使用_vue3中使用vuex_普通網(wǎng)友的博客-CSDN博客
Vue3之Vuex_vue3的vuex_開longlong了嗎?的博客-CSDN博客
Vue3中使用Pinia詳解_九仞山的博客-CSDN博客
pinia和vuex的區(qū)別 Vuex 和 Pinia 的優(yōu)缺點(diǎn) 何時(shí)使用Pinia,何時(shí)使用Vuex_pinia和vuex區(qū)別_more名奇妙的博客-CSDN博客