Vuex & Axios
Vuex
Vuex是一個專門為Vue.js應用所設計的集中式狀態管理架構,它借鑑了Flux和Redux的設計思想,但簡化了概念,採用了一種為能更好地發揮Vue.js資料相應機制而專門設計的實現。
Vuex是一個專為Vue.js應用開發的狀態管理模式,採用集中式儲存管理應用元件狀態,並以響應規則保證狀態以一種可預測的方式發生變化。
# 安裝Vuex
$ npm i vuex --S
# 引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
狀態管理模式
# 計數器
new Vue({
// state 驅動應用的資料來源
data () {
return {
count:0
}
},
// view 以宣告方式將資料來源對映到檢視
template:`<span>{{count}}</span>`,
//actions 響應在檢視上的使用者輸入導致的中狀態變化
methods:{
increment() {
this.count ++
}
}
})
狀態state
的概念,簡單來說可視為專案中使用的資料的集合。Vuex使元件本地狀態和應用層級狀態有了一定的差異。
- 元件本地狀態:表示僅僅在元件內部使用的狀態,類似於通過配置選項傳入Vue元件內部。
- 應用層級狀態:表示同時被多個元件共享的狀態層級
單向資料流
假如有一個父元件同時包含兩個子元件,父元件可以很容易的使用prop
屬性向子元件傳遞資料。兩個子元件如何和物件互相通訊呢?子元件如何傳遞資料給父元件呢?專案規模很小時,可通過事件派發和監聽來完成父元件和子元件的通訊。隨著專案規模增長,遇到的問題是:
- 保持對所有事件追蹤變得越來越困難,到底哪個事件是哪個元件派發的,哪個元件該監聽哪個事件呢?
- 專案邏輯分散在各個元件當中,很容易導致邏輯混亂,不利於專案維護。
- 父元件變得和子元件耦合度越來越嚴重,因為它需要明確的派發和監聽子元件的某些事件。
當應用遇到多個元件共享狀態時,單向資料流的間接性很容易被破壞:
- 多個檢視依賴於同一個狀態
傳參的方法對於多層巢狀的元件將會非常繁瑣,對於兄弟元件間的狀態傳遞無能為力。 - 來自不同檢視的行為需要變更同一狀態
採用父子元件直接飲用或通過事件來變更和同步狀態的多份拷貝
為什麼不把元件的共享狀態抽取出來以一個全域性單例模式管理呢?在這種模式下,元件樹構成一個巨大的檢視,不管在樹的哪個位置,任何元件都能獲取狀態或觸發行為。
另外,通過定義和隔離狀態管理中各種概念並強制遵守一定的規則,程式碼將會變得更加結構化且易於維護。
核心概念
- 單一狀態樹
使用一個物件包含全部應用層級狀態,它作為唯一資料來源存在,這意味著,每個應用將緊緊包含一個倉庫store
的例項。單一狀態樹讓我們能夠直接地定位任意特定的狀態片段,在呼叫過程中也能輕易地取得整個當前應用狀態的快照。 - 獲取狀態
派生狀態getters
用來從倉庫store
中獲取Vue元件資料 - 狀態變更
狀態變更mutators
的事件處理器可用來驅動狀態的變化 - 非同步操作
非同步操作actions
可給元件使用的函式,以此用來驅動事件處理器mutations
。
Vuex規定,屬於應用層級的狀態只能通過狀態變更mutation
中的方法來修改,而派發mutation
中的事件只能通過action
。
從左至右,從元件觸發,元件中呼叫action
,在action
層可以和後臺資料互動,比如獲取初始化資料來源,或者中間資料的過濾等。然後在action
中去派發mutation
。mutation
去觸發狀態的改變,從而觸發檢視的更新。
注意
- 資料流都是單向的
- 元件能夠呼叫
action
-
action
用來派發mutation
- 只有
mutation
可以改變狀態 -
store
是響應式的,無論state
什麼時候更新,元件都將同步更新。
開始
每個Vuex應用的核心就是倉庫(store),store基本就是一個容器,包含著應用中大部分的狀態(state)。Vuex和單純的全域性物件由兩點不同:
- Vuex的狀態儲存時響應式的,當Vue元件從store中讀取狀態時,若store中的狀態發生變化,相應的元件也會得到高效更新。
- 不能直接改變store中的狀態,改變store中的狀態唯一途徑就是顯式地提交(commit)mutation。這樣使得我們可以方便地跟蹤每一個狀態的變化。
倉庫
Vuex應用狀態state
都存放在store
中,Vue元件可從store
中獲取狀態,可把store
通俗的理解為一個全域性變數的倉庫。和單純的全域性變數的區別是當store
中的狀態發生變化時,相應的Vue元件也會得到更新。
//倉庫
const store = new Vuex.Store({
//狀態
state:{
count:0
},
//狀態變更
mutations:{
increment(state){
state.count++
}
}
})
//獲取狀態物件
store.state.count
//觸發狀態變更
store.commit('increment')
狀態
單一狀態樹
Vuex使用單一狀態樹,用一個物件包含了全部應用層級狀態,並作為一個“唯一資料來源(SSOT)”而存在。這意味著,每個應用將僅僅包含一個狀態例項。單一狀態樹讓我們能夠直接地定位任意特定的狀態片段,在除錯過程中也能輕易地去的整個當前應用狀態的快照。
在Vue元件中獲得Vuex狀態
由於Vuex的狀態儲存的是響應式的,從store例項中讀取狀態最簡單是方式是在計算屬性中返回某個狀態。
// 建立元件
const Counter = {
template:`<div>{{count}}</div>`,
computed:{
count () {
// 每當store.state.count變化時會重新求取計算屬性並觸發更新相關聯的DOM
return store.state.count
}
}
}
這種模式導致元件依賴全域性狀態單例,在模組化的構建系統中,在每個需要使用state的元件中需要頻繁地匯入,並在測試元件時需模擬狀態。
Vuex通過store選項,提供了一種機制將狀態從根元件注入到每個子元件中。
const app = new Vue({
el:'#app',
store,//將store物件提供給store選項,把store例項注入到所有的子元件中。
componenets:{Counter},
template:`<div class="app"><counter></counter></div>`
})
通過在根例項中註冊store選項,該store例項會注入到根元件下的所有子元件中,且子元件能通過this.$store訪問到。
const Counter = {
template:`<div>{{count}}</div>`,
computed:{
count () {
return this.$store.state.count
}
}
}
當一個元件需要獲取多個狀態時,將這些狀態都宣告為計算屬性會有些重複和冗餘,為解決這個問題,使用mapState輔助函式幫助我們生成計算屬性。
// 單獨構建版本中輔助函式為Vuex.mapState
import {mapState} from 'vuex'
export default {
computed:mapState({
//箭頭函式使得程式碼更簡潔
count:state=>state.count,
//傳字串count等同於state=>state.count
countAlias:'count',
//為了能夠使用this獲取區域性狀態必須使用常規函式
countPlusLocalState(state){
return state.count + this.localCount
}
})
}
當對映的計算屬性的名稱和state的子節點名稱相同時,也可以給mapState傳入一個字串陣列。
// 對映this.count為store.state.count
computed:mapState(['count'])
mapState函式返回的是一個物件,如何將其與區域性計算屬性混合使用呢?通常需要使用一個工具函式將多個物件合併為一個,以便於將最終物件傳給computed屬性。自從有了物件展開運算子可極大地簡化寫法。
computed:{
localComputed () {
// 使用物件展開運算子對此物件混入到外部物件中
...mapState({
//...
})
}
}
使用Vuex並不意味著需要將所有狀態放入Vuex,雖然將所有狀態放入Vuex會使狀態變化更顯式和易呼叫,但也會使程式碼變得冗長且不直觀。有些狀態 嚴格屬於單個元件,最好還是作為元件的區域性狀態。
getter
有時需從倉庫中的狀態中派生一些狀態
computed:{
// 對列表進行過濾並計數
doneTodosCount() {
return this.$store.state.todos.filter(todo=>todo.done).length
}
}
如果有多個元件需要用到此屬性,要麼複製函式或抽取到一個共享函式然後再多出匯入,但是無論哪種方式都不是很理想。
Vuex允許在倉庫中定義getter(可認為是store的計算屬性),就像計算屬性一樣,getter的返回值會根據它的依賴被快取起來,只有噹噹它的依賴發生變化了才會被重新計算。
const store = new Vuex.Store({
state:{
todos:[
{id:1, text:'...', done:true},
{id:2, text:'...', done:false}
]
},
// getter類似倉庫的計算屬性一樣,其返回值會根據其依賴被快取起來,只有當以來之發生改變才會被重新計算。
getters:{
doneTodos:state=>{
return state.todos.filter(todo=>todo.done)
}
}
})
getter會暴露為store.getters物件,可以以屬性的形式訪問其值:
# getter會暴露為store.getters物件,可以以屬性的形式訪問其值:
store.getters.doneTodos
# getter也可以接受其他getter作為第二個引數
getters:{
doneTodosCount:(state, getters)=>{
return getters.doneTodos.length
}
}
store.getters.doneTodosCount
# 可以很容易在任何元件中使用它
computed:{
doneTodosCount(){
return this.$store.getters.doneTodosCount
}
}
注意:getter在通過屬性訪問時是作為Vue的響應式系統的一部分快取其中的。
可以通過讓getter返回一個函式來實現給getter傳參,在對store中的陣列進行查詢時非常有用。
getters:{
//可以通過讓getter返回一個函式來實現給getter傳參
getTodoById:(state)=>(id)=>{
return state.todos.find(todo=>todo.id === id)
}
}
store.getters.getTodoById(2)
注意,getter在通過方法訪問時,每次都會去進行呼叫,而不會快取結果。
mapGetters 輔助函式
# mapGetters輔助函式僅僅是將store中的getter對映到區域性計算屬性
import {mapGetters} from 'vuex'
export default {
computed:{
//使用物件展開運算子將getter混入computed物件中
...mapGetters(['doneTodosCount','anotherGetter'])
}
}
# 若想將一個getter屬性另取一個名字,使用物件形式。
mapGetters({
//將this.doneCount對映為this.$store.getters.doneTodosCount
doneCount:'doneTodosCount'
})
mutation
更改Vuex倉庫中的狀態的唯一方式是提交mutation。vuex中的mutation非常類似於事件:每個mutation都有一個字串的事件型別(type)和一個回撥函式(handler)。這個回撥函式就是實際進行狀態更改的地方,並且它會接收state作為第一個引數。
const store = new Vuex.Store({
//狀態
state:{count:1},
//狀態變更
mutations:{
//每個mutation都有一個字串的事件型別和一個回撥函式
increment (state) {
state.count++ //變更狀態
}
}
})
不能直接呼叫一個mutation handler,這個選項更像是事件註冊:“當觸發一個型別為increment的mutation時,呼叫此函式”。要喚醒一個mutation handler,你需要以相應的type呼叫store.commit方法。
store.commit('increment')
提交載荷
可以像store.commit傳入額外的引數,即mutation的載荷payload
mutations:{
increment (state, n){
state.count += n
}
}
// 向store.commit傳入額外的引數及mutation的載荷
store.commit('increment', 10)
多數情況下,載荷是一個物件,可包含多個欄位並記錄mutation會更易讀。
mutations:{
increment(state, payload) {
state.count += payload.amount
}
}
// 載荷為物件
store.commit('increment',{acount:10})
物件風格的提交方式
提交mutation的另一種方式是直接使用包含type屬性的物件
# 使用包含type屬性的物件提交mutation
store.commit({type:'increment', amount:10})
# 使用物件風格提交方式,整個物件都作為載荷傳給mutation函式,因此handler保持不變。
mutations:{
increment(state, payload){
state.count += payload.amount
}
}
mutation需遵守vue的響應規則
既然vuex的store中的狀態是響應式的,當變更狀態時,監視狀態的vue元件會也自動更新,這也意味著vuex中的mutation也需要與使用vue一樣遵守一些事項:
- 最好提前在store中初始化好所有屬性
- 當需要在物件上新增新屬性時,應該
- 使用Vue.set(obj, 'prop', 123)
- 以新物件替換老物件
state.obj = {...state.obj, prop:123}
使用常量替代mutation事件型別
使用常量替代mutation事件型別在各種flux實現中最為常見,可使用linter之類的工具發揮作用,同時將常量放在單獨的檔案中可讓程式碼合作者對整個app包含的mutation一目瞭然。
// mutation-types.js
export const SOME_MUTATION = ''
//store.js
import Vuex from 'vuex'
import {SOME_MUTATION} from './mutation-types'
const store = new Vuex.Store({
state:{...},
mutations:{
//使用ES2015風格的計算屬性命名功能來使用一個常量作為函式名稱
[SOME_MUTATION](state){
}
}
})
mutation必須是同步函式
Axios
Axios是一個基於promise的HTTP庫,可用於瀏覽器和Node.js中。
- 從瀏覽器中建立XMLHttpRequest
- 從Node.js中建立HTTP請求
- 支援Promise API
- 攔截請求和響應
- 轉換請求資料和響應資料
- 取消請求
- 自動轉換JSON資料
- 客戶端支援防禦XSRF
# 安裝 axios
$ npm i axios -S
# 執行GET請求
axios.get('/user?id=1').then(function(response){
console.log(response);
}).catch(function(error){
console.log(error)
})
axios.get('/user', {params:{id:1}}).then(function(response){
console.log(response);
}).catch(function(error){
console.log(error)
})
# 執行POST請求
axios.post('/user',{username:'', password:''}).then(function(response){
console.log(response);
}).catch(function(error){
console.log(error)
})
# 執行多個併發請求
function getUsername(){
return axios.get('/user/1')
}
function getPermission(){
return axios.get('/user/1/permission')
}
axios.all([getUsername(), getPermission()]).then(axios.spread(function(username, permission){
}))
axios API
# 向axios傳遞相關配置來建立請求
axios(url[, config])
Vue中使用Axios
Axios並非Vue的外掛無法直接引入後使用,只能在每個需要傳送請求的元件中即時引入。為了解決這個問題,有兩種思路:
- 引入Axios修改原型鏈
# main.js
# 引入
import axios from 'axios`
# 原型鏈繫結
Vue.prototype.$ajax = axios
# 使用
methods:{
submit(){
this.$ajax({
method:'post',
url:'url',
data:{username:'username', password:'password'}
})
}
}
- 結合Vuex封裝action
Vuex的mutation類似於事件,可用於提交Vuex中的狀態,action和mutation很類似,區別在於action包含非同步操作,還可通過action來提交mutation。最主要的區別在於
- mutation 固有引數state用於接收Vuex中的state物件
- action 固有引數context是state的父級,包含著state、getters。
相關文章
- Vuex結合Axios非同步請求資料的封裝VueiOS非同步封裝
- vue2+vuex+vux+axios構建移動端單頁面個人部落格(1——VueUXiOS
- 【爬坑日記】基於vue cli+vuex+axios+iview+sass初始化專案步驟VueiOSView
- [Vuex]Vuex學習手記Vue
- VuexVue
- vuex 原始碼:深入 vuex 之 getterVue原始碼
- vuex 原始碼:深入 vuex 之 namespacedVue原始碼namespace
- vuex 原始碼:深入 vuex 之 moduleVue原始碼
- vuex 原始碼:深入 vuex 之 actionVue原始碼
- vuex 原始碼:深入 vuex 之 mutationVue原始碼
- AxiosiOS
- [Vuex系列] - Vuex中的getter的用法Vue
- Vuex and TypescriptVueTypeScript
- EventBus & Vuex?Vue
- Vuex 提升Vue
- vue - VuexVue
- Vuex教程Vue
- 手撕Vuex-Vuex實現原理分析Vue
- Vuex 進階——模組化組織 VuexVue
- [Vuex系列] - 初嘗Vuex第一個例子Vue
- VueX Vue: Could not find a declaration file for module vuex.Vue
- vue axiosVueiOS
- Axios初步iOS
- 1.vuex是什麼? 2.vuex的核心概念;(vuex的屬性;vuex的資料傳遞流程;) 3.為什麼要用vuex?Vue
- Axios、axios攔截器、fetch-jsonp ——0807iOSJSON
- 傻瓜式vuex語法糖kiss-vuexVue
- vuex-pathify 一個基於vuex進行封裝的 vuex助手語法外掛Vue封裝
- axios的全域性攔截之axios.interceptorsiOS
- ajax和fetch、axios的區別以及axios原理iOS
- axios,Ajax,jQuery ajax,axios和fetch的區別iOSjQuery
- 圖解 vuex圖解Vue
- 巧學vuexVue
- 淺談vuexVue
- 玩爛 VuexVue
- Vuex全解析Vue
- Vuex之整合Vue
- Vuex之moduleVue
- Vuex之stateVue