Vuex Axios

Vuex

Vuex是一個專門為Vue.js應用所設計的集中式狀態(tài)管理架構嘉竟,它借鑒了Flux和Redux的設計思想,但簡化了概念洋侨,采用了一種為能更好地發(fā)揮Vue.js數(shù)據(jù)相應機制而專門設計的實現(xiàn)舍扰。

Vuex是一個專為Vue.js應用開發(fā)的狀態(tài)管理模式,采用集中式存儲管理應用組件狀態(tài)凰兑,并以響應規(guī)則保證狀態(tài)以一種可預測的方式發(fā)生變化妥粟。

# 安裝Vuex
$ npm i vuex --S

# 引入
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)

狀態(tài)管理模式

# 計數(shù)器
new Vue({
  // state 驅動應用的數(shù)據(jù)源
  data () {
    return {
      count:0
    }
  },
  // view 以聲明方式將數(shù)據(jù)源映射到視圖
  template:`<span>{{count}}</span>`,
  //actions 響應在視圖上的用戶輸入導致的中狀態(tài)變化
  methods:{
    increment() {
      this.count ++
    }
  }
})

狀態(tài)state的概念,簡單來說可視為項目中使用的數(shù)據(jù)的集合吏够。Vuex使組件本地狀態(tài)和應用層級狀態(tài)有了一定的差異勾给。

  • 組件本地狀態(tài):表示僅僅在組件內(nèi)部使用的狀態(tài),類似于通過配置選項傳入Vue組件內(nèi)部锅知。
  • 應用層級狀態(tài):表示同時被多個組件共享的狀態(tài)層級

單向數(shù)據(jù)流

假如有一個父組件同時包含兩個子組件播急,父組件可以很容易的使用prop屬性向子組件傳遞數(shù)據(jù)。兩個子組件如何和對象互相通信呢售睹?子組件如何傳遞數(shù)據(jù)給父組件呢桩警?項目規(guī)模很小時,可通過事件派發(fā)和監(jiān)聽來完成父組件和子組件的通信昌妹。隨著項目規(guī)模增長捶枢,遇到的問題是:

  • 保持對所有事件追蹤變得越來越困難,到底哪個事件是哪個組件派發(fā)的飞崖,哪個組件該監(jiān)聽哪個事件呢烂叔?
  • 項目邏輯分散在各個組件當中,很容易導致邏輯混亂固歪,不利于項目維護蒜鸡。
  • 父組件變得和子組件耦合度越來越嚴重,因為它需要明確的派發(fā)和監(jiān)聽子組件的某些事件牢裳。
單向數(shù)據(jù)流

當應用遇到多個組件共享狀態(tài)時逢防,單向數(shù)據(jù)流的間接性很容易被破壞:

  • 多個視圖依賴于同一個狀態(tài)
    傳參的方法對于多層嵌套的組件將會非常繁瑣,對于兄弟組件間的狀態(tài)傳遞無能為力蒲讯。
  • 來自不同視圖的行為需要變更同一狀態(tài)
    采用父子組件直接飲用或通過事件來變更和同步狀態(tài)的多份拷貝

為什么不把組件的共享狀態(tài)抽取出來以一個全局單例模式管理呢忘朝?在這種模式下,組件樹構成一個巨大的視圖伶椿,不管在樹的哪個位置辜伟,任何組件都能獲取狀態(tài)或觸發(fā)行為氓侧。

另外,通過定義和隔離狀態(tài)管理中各種概念并強制遵守一定的規(guī)則导狡,代碼將會變得更加結構化且易于維護约巷。

核心概念

  1. 單一狀態(tài)樹
    使用一個對象包含全部應用層級狀態(tài),它作為唯一數(shù)據(jù)源存在旱捧,這意味著独郎,每個應用將緊緊包含一個倉庫store的實例。單一狀態(tài)樹讓我們能夠直接地定位任意特定的狀態(tài)片段枚赡,在調(diào)用過程中也能輕易地取得整個當前應用狀態(tài)的快照氓癌。
  2. 獲取狀態(tài)
    派生狀態(tài)getters用來從倉庫store中獲取Vue組件數(shù)據(jù)
  3. 狀態(tài)變更
    狀態(tài)變更mutators的事件處理器可用來驅動狀態(tài)的變化
  4. 異步操作
    異步操作actions可給組件使用的函數(shù),以此用來驅動事件處理器mutations贫橙。
Vuex應用中的數(shù)據(jù)流向

Vuex規(guī)定贪婉,屬于應用層級的狀態(tài)只能通過狀態(tài)變更mutation中的方法來修改,而派發(fā)mutation中的事件只能通過action卢肃。

從左至右疲迂,從組件觸發(fā),組件中調(diào)用action莫湘,在action層可以和后臺數(shù)據(jù)交互尤蒿,比如獲取初始化數(shù)據(jù)源,或者中間數(shù)據(jù)的過濾等幅垮。然后在action中去派發(fā)mutation腰池。mutation去觸發(fā)狀態(tài)的改變,從而觸發(fā)視圖的更新忙芒。

注意

  • 數(shù)據(jù)流都是單向的
  • 組件能夠調(diào)用action
  • action用來派發(fā)mutation
  • 只有mutation可以改變狀態(tài)
  • store是響應式的示弓,無論state什么時候更新,組件都將同步更新呵萨。

開始

每個Vuex應用的核心就是倉庫(store)避乏,store基本就是一個容器,包含著應用中大部分的狀態(tài)(state)甘桑。Vuex和單純的全局對象由兩點不同:

  • Vuex的狀態(tài)存儲時響應式的,當Vue組件從store中讀取狀態(tài)時歹叮,若store中的狀態(tài)發(fā)生變化跑杭,相應的組件也會得到高效更新。
  • 不能直接改變store中的狀態(tài)咆耿,改變store中的狀態(tài)唯一途徑就是顯式地提交(commit)mutation德谅。這樣使得我們可以方便地跟蹤每一個狀態(tài)的變化。

倉庫

Vuex應用狀態(tài)state都存放在store中萨螺,Vue組件可從store中獲取狀態(tài)窄做,可把store通俗的理解為一個全局變量的倉庫愧驱。和單純的全局變量的區(qū)別是當store中的狀態(tài)發(fā)生變化時,相應的Vue組件也會得到更新椭盏。

//倉庫
const store = new Vuex.Store({
  //狀態(tài)
  state:{
    count:0
  },
  //狀態(tài)變更
  mutations:{
    increment(state){
      state.count++
    }
  }
})
//獲取狀態(tài)對象
store.state.count
//觸發(fā)狀態(tài)變更
store.commit('increment')

狀態(tài)

單一狀態(tài)樹

Vuex使用單一狀態(tài)樹组砚,用一個對象包含了全部應用層級狀態(tài),并作為一個“唯一數(shù)據(jù)源(SSOT)”而存在掏颊。這意味著糟红,每個應用將僅僅包含一個狀態(tài)實例。單一狀態(tài)樹讓我們能夠直接地定位任意特定的狀態(tài)片段乌叶,在調(diào)試過程中也能輕易地去的整個當前應用狀態(tài)的快照盆偿。

在Vue組件中獲得Vuex狀態(tài)

由于Vuex的狀態(tài)存儲的是響應式的,從store實例中讀取狀態(tài)最簡單是方式是在計算屬性中返回某個狀態(tài)准浴。

// 創(chuàng)建組件
const Counter = {
  template:`<div>{{count}}</div>`,
  computed:{
    count () {
      // 每當store.state.count變化時會重新求取計算屬性并觸發(fā)更新相關聯(lián)的DOM
      return store.state.count
    }
  }
}

這種模式導致組件依賴全局狀態(tài)單例事扭,在模塊化的構建系統(tǒng)中,在每個需要使用state的組件中需要頻繁地導入乐横,并在測試組件時需模擬狀態(tài)求橄。

Vuex通過store選項,提供了一種機制將狀態(tài)從根組件注入到每個子組件中晰奖。

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

當一個組件需要獲取多個狀態(tài)時蛆楞,將這些狀態(tài)都聲明為計算屬性會有些重復和冗余溯乒,為解決這個問題,使用mapState輔助函數(shù)幫助我們生成計算屬性豹爹。

// 單獨構建版本中輔助函數(shù)為Vuex.mapState
import {mapState} from 'vuex'

export default {
  computed:mapState({
    //箭頭函數(shù)使得代碼更簡潔
    count:state=>state.count,
    //傳字符串count等同于state=>state.count
    countAlias:'count',
    //為了能夠使用this獲取局部狀態(tài)必須使用常規(guī)函數(shù)
    countPlusLocalState(state){
      return state.count + this.localCount
    }
  })
}

當映射的計算屬性的名稱和state的子節(jié)點名稱相同時裆悄,也可以給mapState傳入一個字符串數(shù)組。

// 映射this.count為store.state.count
computed:mapState(['count'])

mapState函數(shù)返回的是一個對象臂聋,如何將其與局部計算屬性混合使用呢光稼?通常需要使用一個工具函數(shù)將多個對象合并為一個,以便于將最終對象傳給computed屬性孩等。自從有了對象展開運算符可極大地簡化寫法艾君。

computed:{
  localComputed () {
    // 使用對象展開運算符對此對象混入到外部對象中
    ...mapState({
    //...
    })
  }
}

使用Vuex并不意味著需要將所有狀態(tài)放入Vuex,雖然將所有狀態(tài)放入Vuex會使狀態(tài)變化更顯式和易調(diào)用肄方,但也會使代碼變得冗長且不直觀冰垄。有些狀態(tài) 嚴格屬于單個組件,最好還是作為組件的局部狀態(tài)权她。

getter

有時需從倉庫中的狀態(tài)中派生一些狀態(tài)

computed:{
  // 對列表進行過濾并計數(shù)
  doneTodosCount() {
    return this.$store.state.todos.filter(todo=>todo.done).length
  }
}

如果有多個組件需要用到此屬性,要么復制函數(shù)或抽取到一個共享函數(shù)然后再多出導入,但是無論哪種方式都不是很理想。

Vuex允許在倉庫中定義getter(可認為是store的計算屬性),就像計算屬性一樣,getter的返回值會根據(jù)它的依賴被緩存起來,只有當當它的依賴發(fā)生變化了才會被重新計算。

const store = new Vuex.Store({
  state:{
    todos:[
      {id:1, text:'...', done:true},
      {id:2, text:'...', done:false}
    ]
  },
  // getter類似倉庫的計算屬性一樣做个,其返回值會根據(jù)其依賴被緩存起來太闺,只有當以來之發(fā)生改變才會被重新計算最住。
  getters:{
    doneTodos:state=>{
      return state.todos.filter(todo=>todo.done)
    }
  }
})

getter會暴露為store.getters對象甚疟,可以以屬性的形式訪問其值:

# getter會暴露為store.getters對象揽祥,可以以屬性的形式訪問其值:
store.getters.doneTodos

# getter也可以接受其他getter作為第二個參數(shù)
getters:{
  doneTodosCount:(state, getters)=>{
    return getters.doneTodos.length
  }
}

store.getters.doneTodosCount

# 可以很容易在任何組件中使用它
computed:{
  doneTodosCount(){
    return this.$store.getters.doneTodosCount
  }
}

注意:getter在通過屬性訪問時是作為Vue的響應式系統(tǒng)的一部分緩存其中的料按。

可以通過讓getter返回一個函數(shù)來實現(xiàn)給getter傳參烹卒,在對store中的數(shù)組進行查詢時非常有用逢勾。

getters:{
  //可以通過讓getter返回一個函數(shù)來實現(xiàn)給getter傳參
  getTodoById:(state)=>(id)=>{
    return state.todos.find(todo=>todo.id === id)
  }
}

store.getters.getTodoById(2)

注意,getter在通過方法訪問時藐吮,每次都會去進行調(diào)用溺拱,而不會緩存結果。

mapGetters 輔助函數(shù)

# mapGetters輔助函數(shù)僅僅是將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倉庫中的狀態(tài)的唯一方式是提交mutation。vuex中的mutation非常類似于事件:每個mutation都有一個字符串的事件類型(type)和一個回調(diào)函數(shù)(handler)潦闲。這個回調(diào)函數(shù)就是實際進行狀態(tài)更改的地方攒菠,并且它會接收state作為第一個參數(shù)。

const store = new Vuex.Store({
  //狀態(tài)
  state:{count:1},
  //狀態(tài)變更
  mutations:{
    //每個mutation都有一個字符串的事件類型和一個回調(diào)函數(shù)
    increment (state) {
      state.count++ //變更狀態(tài)
    }
  }
})

不能直接調(diào)用一個mutation handler歉闰,這個選項更像是事件注冊:“當觸發(fā)一個類型為increment的mutation時辖众,調(diào)用此函數(shù)”。要喚醒一個mutation handler和敬,你需要以相應的type調(diào)用store.commit方法凹炸。

store.commit('increment')

提交載荷

可以像store.commit傳入額外的參數(shù),即mutation的載荷payload

mutations:{
  increment (state, n){
    state.count += n
  }
}
// 向store.commit傳入額外的參數(shù)及mutation的載荷
store.commit('increment', 10)

多數(shù)情況下昼弟,載荷是一個對象啤它,可包含多個字段并記錄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函數(shù)变骡,因此handler保持不變离赫。
mutations:{
  increment(state, payload){
    state.count += payload.amount
  }
}

mutation需遵守vue的響應規(guī)則

既然vuex的store中的狀態(tài)是響應式的,當變更狀態(tài)時塌碌,監(jiān)視狀態(tài)的vue組件會也自動更新渊胸,這也意味著vuex中的mutation也需要與使用vue一樣遵守一些事項:

  1. 最好提前在store中初始化好所有屬性
  2. 當需要在對象上添加新屬性時,應該
  • 使用Vue.set(obj, 'prop', 123)
  • 以新對象替換老對象
state.obj = {...state.obj, prop:123}

使用常量替代mutation事件類型

使用常量替代mutation事件類型在各種flux實現(xiàn)中最為常見台妆,可使用linter之類的工具發(fā)揮作用翎猛,同時將常量放在單獨的文件中可讓代碼合作者對整個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風格的計算屬性命名功能來使用一個常量作為函數(shù)名稱
    [SOME_MUTATION](state){

    }
  }
})

mutation必須是同步函數(shù)

Axios

Axios是一個基于promise的HTTP庫接剩,可用于瀏覽器和Node.js中切厘。

  • 從瀏覽器中創(chuàng)建XMLHttpRequest
  • 從Node.js中創(chuàng)建HTTP請求
  • 支持Promise API
  • 攔截請求和響應
  • 轉換請求數(shù)據(jù)和響應數(shù)據(jù)
  • 取消請求
  • 自動轉換JSON數(shù)據(jù)
  • 客戶端支持防御XSRF
# 安裝 axios
$ npm i axios -S

# 執(zhí)行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)
})

# 執(zhí)行POST請求
axios.post('/user',{username:'', password:''}).then(function(response){
  console.log(response);
}).catch(function(error){
  console.log(error)
})

# 執(zhí)行多個并發(fā)請求
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傳遞相關配置來創(chuàng)建請求
axios(url[, config])

Vue中使用Axios

Axios并非Vue的插件無法直接引入后使用,只能在每個需要發(fā)送請求的組件中即時引入搂漠。為了解決這個問題迂卢,有兩種思路:

  1. 引入Axios修改原型鏈
# main.js
# 引入
import axios from 'axios`
# 原型鏈綁定
Vue.prototype.$ajax = axios
# 使用
methods:{
  submit(){
    this.$ajax({
      method:'post',
      url:'url',
      data:{username:'username', password:'password'}
    })
  }
}
  1. 結合Vuex封裝action

Vuex的mutation類似于事件,可用于提交Vuex中的狀態(tài)桐汤,action和mutation很類似而克,區(qū)別在于action包含異步操作,還可通過action來提交mutation怔毛。最主要的區(qū)別在于

  • mutation 固有參數(shù)state用于接收Vuex中的state對象
  • action 固有參數(shù)context是state的父級员萍,包含著state、getters拣度。
最后編輯于
?著作權歸作者所有,轉載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末厉斟,一起剝皮案震驚了整個濱河市康谆,隨后出現(xiàn)的幾起案子盯滚,更是在濱河造成了極大的恐慌迅耘,老刑警劉巖,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件冤馏,死亡現(xiàn)場離奇詭異日麸,居然都是意外死亡,警方通過查閱死者的電腦和手機逮光,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進店門代箭,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人涕刚,你說我怎么就攤上這事嗡综。” “怎么了杜漠?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵极景,是天一觀的道長察净。 經(jīng)常有香客問我,道長戴陡,這世上最難降的妖魔是什么塞绿? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮恤批,結果婚禮上,老公的妹妹穿的比我還像新娘裹赴。我一直安慰自己喜庞,他們只是感情好,可當我...
    茶點故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布棋返。 她就那樣靜靜地躺著延都,像睡著了一般。 火紅的嫁衣襯著肌膚如雪睛竣。 梳的紋絲不亂的頭發(fā)上晰房,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天,我揣著相機與錄音射沟,去河邊找鬼殊者。 笑死,一個胖子當著我的面吹牛验夯,可吹牛的內(nèi)容都是我干的猖吴。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼挥转,長吁一口氣:“原來是場噩夢啊……” “哼海蔽!你這毒婦竟也來了?” 一聲冷哼從身側響起绑谣,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤党窜,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后借宵,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體幌衣,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年暇务,在試婚紗的時候發(fā)現(xiàn)自己被綠了泼掠。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡垦细,死狀恐怖择镇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情括改,我是刑警寧澤腻豌,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響吝梅,放射性物質(zhì)發(fā)生泄漏虱疏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一苏携、第九天 我趴在偏房一處隱蔽的房頂上張望做瞪。 院中可真熱鬧,春花似錦右冻、人聲如沸装蓬。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽牍帚。三九已至,卻和暖如春乳蛾,著一層夾襖步出監(jiān)牢的瞬間暗赶,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工肃叶, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留蹂随,地道東北人。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓被环,卻偏偏與公主長得像糙及,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子筛欢,可洞房花燭夜當晚...
    茶點故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容

  • 安裝 npm npm install vuex --save 在一個模塊化的打包系統(tǒng)中浸锨,您必須顯式地通過Vue.u...
    蕭玄辭閱讀 2,938評論 0 7
  • Vuex是什么? Vuex 是一個專為 Vue.js應用程序開發(fā)的狀態(tài)管理模式版姑。它采用集中式存儲管理應用的所有組件...
    蕭玄辭閱讀 3,120評論 0 6
  • Vuex 是一個專為 Vue.js 應用程序開發(fā)的狀態(tài)管理模式柱搜。它采用集中式存儲管理應用的所有組件的狀態(tài),并以相應...
    白水螺絲閱讀 4,667評論 7 61
  • 在vue項目中組件間相互傳值或者后臺獲取的數(shù)據(jù)需要供多個組件使用的情況很多的話,有必要考慮引入vuex來管理這些凌...
    William_Gh閱讀 4,545評論 0 4
  • 愛一個人表制, 一定要在一起嗎 答案…… 隨緣 相愛過健爬,別離過 愛與被愛 曾經(jīng),經(jīng)過的 無論哪種情形 走過么介,回憶 留下...
    水一慧心閱讀 145評論 0 0