Vuex

1.什么是Vuex?

官方回答:Vuex 是一個專為 Vue.js 應(yīng)用程序開發(fā)的狀態(tài)管理模式。它采用集中式存儲管理應(yīng)用的所有組件的狀態(tài),并以相應(yīng)的規(guī)則保證狀態(tài)以一種可預(yù)測的方式發(fā)生變化。


2.Vuex能解決什么問題?

當我們的應(yīng)用遇到多個組件共享狀態(tài)時,單向數(shù)據(jù)流的簡潔性很容易被破壞:

  • 多個視圖依賴于同一狀態(tài)。
  • 來自不同視圖的行為需要變更同一狀態(tài)。

對于問題一,傳參的方法對于多層嵌套的組件將會非常繁瑣,并且對于兄弟組件間的狀態(tài)傳遞無能為力。
對于問題二,我們經(jīng)常會采用父子組件直接引用或者通過事件來變更和同步狀態(tài)的多份拷貝。以上的這些模式非常脆弱,通常會導(dǎo)致無法維護的代碼。

因此,把組件的共享狀態(tài)抽取出來,以一個全局單例模式管理。在這種模式下,我們的組件樹構(gòu)成了一個巨大的“視圖”,不管在樹的哪個位置,任何組件都能獲取狀態(tài)或者觸發(fā)行為!
通過定義和隔離狀態(tài)管理中的各種概念并通過強制規(guī)則維持視圖和狀態(tài)間的獨立性,我們的代碼將會變得更結(jié)構(gòu)化且易維護。



3.創(chuàng)建Vuex的步驟:

  • 下載并引用vuex.js文件,插入到vue.js文件后面
  • 創(chuàng)建Vuex實例對象
const store = new Vuex.Store({
       state:{
              msg:"魷小魚"
        },
        mutations:{},
        actions:{},
        getters:{}
});
  • 在祖先組件中添加store的key保存Vuex對象; 只要祖先組件中保存了Vuex對象 , 那么祖先組件和所有的后代組件就可以使用Vuex中保存的共享數(shù)據(jù)了
Vue.component("father",{
                template:"#father",
                //在祖先組件中添加store的key保存Vuex對象
                store:store,
                data:function(){
                    return{}
                },
                methods:{},
                computed:{},
                components:{}
})
  • 引用vuex中的共享數(shù)據(jù)方法
<template id="father">
    <div>
        <p>{{this.$store.state.msg}}</p>
        <son></son>
    </div>
</template>

4.State

  • state屬性和組件的data屬性類似;state作用:用來保存全局共享數(shù)據(jù)

在引入vuex 以后,我們需要在創(chuàng)建Vuex對象的state中定義變量:

const store = new Vuex.Store({
        // 這里的state就相當于組件中的data, 就是專門用于保存共享數(shù)據(jù)的
        state:{
            msg:"魷小魚"
        }
});

組件中內(nèi)容:

<div id="app">
    <father></father>
</div>
<template id="father">
    <div>
        <!--4.在使用Vuex中保存的共享數(shù)據(jù)的時候, 必須通過如下的格式來使用-->
        <p>{{this.$store.state.msg}}</p>
        <son></son>
    </div>
</template>
<template id="son">
    <div>
        <p>{{this.$store.state.msg}}</p>
    </div>
</template>

有了vuex,我們不必在考慮組件之間的傳值,直接就可以通過$store來獲取不同的數(shù)據(jù),,但是如果需要vuex中的多個數(shù)據(jù)的這時候,這樣寫就太啰嗦了,我們可以將它定義在computed中。

<template id="son">
    <div>
        <p>{{msg}}</p>
    </div>
</template>
 Vue.component("father", {
        template: "#father",
        // 在祖先組件中添加store的key保存Vuex對象
        // 只要祖先組件中保存了Vuex對象 , 那么祖先組件和所有的后代組件就可以使用Vuex中保存的共享數(shù)據(jù)了
        store:store,
        // 子組件
        components: {
            "son": {
                template: "#son",
                computed: {
                    nickname(){
                        return this.$store.state.msg
                    }
                }
            }
        }
 });

這樣引入就方便了很多。

  • mapState 輔助函數(shù)
    mapState輔助函數(shù)作用:可以輔助獲取到多個state的值

怎么使用?
1.Vuex實例對象部分:

state:{
  msg:"魷小魚"
}

2.在.vue組件中引入,在js塊中引入

import { mapState } from 'vuex'

3.在vue組件computed中定義一個數(shù)組

 computed:{ 
    /*通過mapState將 store 中的  state 的全局共享數(shù)據(jù)名稱 “映射”到“局部計算屬性”*/
    ...mapState([  //mapState本是一個函數(shù),在里面寫一個數(shù)組,記得加...
        'msg'  //存的數(shù)據(jù),把 `this.msg` 映射為 `this.$store.state.msg`
    ])
}

這一句代碼就相當于下面這句:

computed:{
    msg(){
        return this.$store.state.msg
    }
}

4.然后就可以不用$store.state.msg引用了,直接插值

{{msg}}

5.Getters

  • getters和computed相似;getters的作用:用來保存獲取共享數(shù)據(jù)的計算屬性方法
    就像計算屬性一樣,getter 的返回值會根據(jù)它的依賴被緩存起來,且只有當它的依賴值發(fā)生了改變才會被重新計算

補充:

  • 什么是計算屬性?
    ?將計算結(jié)果緩存起來,只要數(shù)據(jù)沒有發(fā)生變化就只會計算一次,以后使用的都是緩存>起來的數(shù)據(jù)
  • 函數(shù)和計算屬性的區(qū)別?
    ?函數(shù)的特點:每次調(diào)用都會執(zhí)行
    ?計算屬性的特點:只要返回的結(jié)果沒有發(fā)生變化,那么計算屬性只會被執(zhí)行一次
data:{
           message:"abcdefg"
       },
       //5.專門用于儲存監(jiān)聽事件回調(diào)函數(shù)
       methods:{
           /*函數(shù)的特點:每次調(diào)用都會執(zhí)行*/
           msg1(){
               console.log("msg1被執(zhí)行了");
               let res = this.message.split('').reverse().join('');
               return res;
           }
       },
       //6.專門用于定義計算屬性的
       computed: {
           /*計算屬性的特點:只要返回的結(jié)果沒有發(fā)生變化,那么計算屬性只會被執(zhí)行一次
            計算屬性應(yīng)用的場景:由于計算屬性會將返回的結(jié)果緩存起來
                              如果返回的數(shù)據(jù)不會頻繁的發(fā)生改變,
                              使用計算屬性會比函數(shù)的性能高
           */
           // 計算屬性的 getter
           reversedMessage: function () {
               console.log("reversedMessage被執(zhí)行了");
               // `this` 指向 vm 實例
               let res = this.message.split('').reverse().join('');
               return res;
           }
       }
  • 如何把數(shù)據(jù)緩存起來?
    ?這個數(shù)據(jù)如果是組件中的,就使用computed來緩存
    ?這個數(shù)據(jù)如果是Vuex中的,就使用getters來緩存
<div id="app">
          <father></father>
      </div>
      <template id="father">
          <div>
              <p>{{this.$store.getters.format}}</p>
              <p>{{this.$store.getters.format}}</p>
              <p>{{this.$store.getters.format}}</p>
          </div>
      </template>
<script type="text/javascript">
  const store = new Vuex.Store({
              state:{
                  name:"魷小魚"
              },
              mutations:{},
              getters:{
                  format(state){
                      console.log("getters被調(diào)用了")
                      return state.name + "2541873074@qq.com"
                  }
              }
          })
          Vue.component("father",{
              template:"#father",
              store:store,
          })
          let vue= new Vue({
              el:"#app",
          })      
</script>

效果:
數(shù)據(jù)沒用發(fā)生改變,format方法只會被調(diào)用一次

  • getters相當于vue中的計算屬性,通過getters進一步處理,得到我們想要的值,而且允許傳參;

第一個參數(shù)就是state`:

const store = new Vuex.Store({
  state: {
    todos: [
      { id: 1, text: '...', done: true },
      { id: 2, text: '...', done: false }
    ]
  },
  getters: {
    doneTodos: state => {
      return state.todos.filter(todo => todo.done)
    }
  }
})

通過屬性訪問:Getter 會暴露為 store.getters 對象,你可以以屬性的形式訪問這些值

store.getters.doneTodos // -> [{ id: 1, text: '...', done: true }]

Getter 也可以接受其他 getter 作為第二個參數(shù):

getters: {
  // ...
  doneTodosCount: (state, getters) => {
    return getters.doneTodos.length
  }
}

通過屬性訪問:

store.getters.doneTodosCount // -> 1

通過方法訪問:可以通過讓 getter 返回一個函數(shù),來實現(xiàn)給 getter 傳參。在你對 store 里的數(shù)組進行查詢時非常有用。

getters: {
  // ...
  getTodoById: (state) => (id) => {
    return state.todos.find(todo => todo.id === id)
  }
}
store.getters.getTodoById(2) // -> { id: 2, text: '...', done: false }
  • mapGetters 輔助函數(shù)
    mapGetters 輔助函數(shù)的作用:僅僅是將 store 中的 getter 映射到局部計算屬性

怎么使用?
1.Vuex實例對象部分:

state:{
  count:0
},
 // getters:獲取全局共享的數(shù)據(jù)
getters: {
    count (state) {
      return state.count
    }
 }

2.在.vue組件中引入,在js塊中引入

import { mapGetters} from 'vuex'

3.在vue組件computed中定義一個數(shù)組:

computed: {
  // 使用對象展開運算符將 getter 混入 computed 對象中
    ...mapGetters([
      'count ', // 把 `this.count` 映射為 `this.$store.getters.count`
      // ...
    ])
  }

以上代碼就相當于下面的代碼:

computed: {
      count(state){
          return this.$store.getters.count
      }
  }

6.Mutations

  • mutations和組件中的methods屬性類似;mutations作用:用來保存修改全局共享數(shù)據(jù)的方法
<div id="app">
   <father></father>
</div>
<template id="father">
   <div>
      <son1></son1>
   </div>
</template>
<template id="son1">
   <div>
       <button @click="add">增加</button>
       <button @click="sub">減少</button>
       <input type="text" :value="this.$store.state.count">
   </div>
</template>
<script>
const store = new Vuex.Store({
       // 這里的state就相當于組件中的data, 就是專門用于保存共享數(shù)據(jù)的
       state: {
           count:0
       },
       // mutations:用于保存修改共享數(shù)據(jù)的方法的
       mutations: {
           /*
           注意點:在執(zhí)行mutations中定義的方法的時候, 系統(tǒng)會自動給這些方法傳遞一個state參數(shù)
           state中就保存了共享的數(shù)據(jù)
           */
           mAdd(state){
               state.count = state.count + 1;
           },
           mSub(state){
               state.count = state.count - 1;
           }
       }
   });
Vue.component("father", {
       template: "#father",
       store:store,
       //子組件
       components: {
           "son1": {
               template: "#son1",
               methods:{
                   add(){
                       //注意點:在Vuex中不推薦直接修改共享數(shù)據(jù)
                       // this.$store.state.count = this.$store.state.count + 1;
                       this.$store.commit("mAdd");
                   },
                  sub(){
                       // this.$store.state.count = this.$store.state.count - 1;
                      this.$store.commit("mSub");
                   }
               }
           }
       }
});
let vue = new Vue({
       el: '#app',
      });
</script>
  • mutations需要通過commit來調(diào)用其里面的方法,它也可以傳入?yún)?shù),第一個參數(shù)是state,第二個參數(shù)是載荷(payLoad),也就是額外的參數(shù)

Vuex實例對象部分:

state:{
  count:0
},
mutations: { //類似于methods
  changecount(state,payLoad){ //第一個參數(shù)是`state`,第二個參數(shù)是`載荷(payLoad)`,也就是額外的參數(shù)
    state.count += payLoad.number
  }
}

template模板部分:

<div class="home">
   <div><button @click="test">我是按鈕</button></div>
</div>

Vue實例對象部分:

methods:{
 test(){
   this.$store.commit(' changecount',{   //第二個參數(shù)最好寫成對象形式
     number:5
   })
 }
}

調(diào)用的時候第二個參數(shù)最好寫成對象形式,這樣我們就可以傳遞更多信息。

但是,這樣寫還是會遇到同樣的問題,就是如果需要操作多個數(shù)據(jù),就會變的麻煩,這時候我們就需要mapMutations輔助函數(shù),通過它將mutations中方法映射到組件的methods屬性中

這里是通過mapMutationsmutations中方法映射組件的methods屬性,需要提前做的準備須知

  • 使用常量替代 Mutation 事件類型

① 新建一個mutations-type.js文件:

// mutation-types.js
export const CHANGE_COUNT = 'CHANGE_COUNT'

② store.js文件中:

// store.js
import Vuex from 'vuex'
import { CHANGE_COUNT } from './mutation-types'

const store = new Vuex.Store({
 state: { 
       count:0
 },
//這里在做項目時也可以把mutation中代碼單獨提取到一個文件中,命名為mutatons.js文件
 mutations: {
   // 我們可以使用 ES2015 風(fēng)格的計算屬性命名功能來使用一個常量作為函數(shù)名
   [CHANGE_COUNT] (state,payLoad) { // payLoad 傳遞的值
     // mutate state
        state.count = payLoad
   }
 }
})

為什么要使用常量替代 Mutation 事件類型?
?因為在Actions中通過commit('changecount', playLoad)觸發(fā)Mutationschangecount修改數(shù)據(jù)的方法時,用的是 “”changecount包裹成字符串,由于字符串在書寫過程中不會報錯,所以將Mutations中的方法名定義為常量,這樣在書寫不正確時會報錯,方便維護

使用常量之actions中調(diào)用mutations中方法:

//這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件
actions: {
//用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候)
setchangecount({ commit },playLoad){
      commit('changecount',playLoad)
   }
}

使用常量之actions中調(diào)用mutations中方法:

//這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件
import {CHANGE_COUNT} from './mutations-type'
actions: {
//用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候)
setchangecount({ commit },playLoad){   
      commit(CHANGE_COUNT,playLoad)
   }
}
  • Mutation 必須是同步函數(shù)
    一條重要的原則就是要記住 mutation 必須是同步函數(shù)。
    下面Vue官網(wǎng)原話:
mutations: {
 someMutation (state) {
   api.callAsyncMethod(() => {
     state.count++
   })
 }
}

現(xiàn)在想象,我們正在 debug 一個 app 并且觀察 devtool 中的 mutation 日志。每一條 mutation 被記錄,devtools 都需要捕捉到前一狀態(tài)和后一狀態(tài)的快照。然而,在上面的例子中 mutation 中的異步函數(shù)中的回調(diào)讓這不可能完成:因為當 mutation 觸發(fā)的時候,回調(diào)函數(shù)還沒有被調(diào)用,devtools 不知道什么時候回調(diào)函數(shù)實際上被調(diào)用——實質(zhì)上任何在回調(diào)函數(shù)中進行的狀態(tài)的改變都是不可追蹤的。

  • mapMutations輔助函數(shù)
    mapMutations輔助函數(shù)的作用:在組件中調(diào)用 mutaions 中的方法;
    原理: 就如同,通過 store.commit(mutations中保存的修改state數(shù)據(jù)的方法) 調(diào)用mutaions 中的方法

怎么使用?
1.Vuex實例對象部分:

state:{
  count:0
},
mutations: { //類似于methods
  changecount(state,payLoad){
    state.count  = payLoad
  }
}

2.在.vue組件中引入,在js塊中引入

import { mapMutations } from 'vuex'

3.在vue組件methods中定義一個數(shù)組(Vue實例對象部分):

methods: {
    ...mapMutations([
      'changecount', // 將 `this.changecount()` 映射為 `this.$store.commit('changecount')`
    ])
  }

以上代碼就相當于下面的代碼:

methods: {
   changecount(playLoad){
        this.$store.commit('changecount',playLoad)
    }
}

5.參數(shù)我們可以在調(diào)用這個方法的時候?qū)懭?/p>

<button @click="changecount({number:5})">我是按鈕</button>

5.使用常量之actions中調(diào)用mutations中方法:

//這里在做項目時也可以把actions中代碼單獨提取到一個文件中,命名為actions.js文件
actions: {
   setchangecount({ commit },playLoad){
       commit('changecount',playLoad)
    }
}
  • 為什么要繞一圈,從mutations里面去改state呢?我能不能直接改state呢?

比如這樣:

changecount(){
 this.$store.state.count +=5;
}

實際看結(jié)果也可以,那我為什么從mutations里面中轉(zhuǎn)一下呢?以下的解釋是我看別人寫的,寫的不錯就在這里利用一下

原因如下:

① 在mutations中不僅僅能做賦值操作

② 作者在mutations中做了類似埋點操作,如果從mutations中操作的話, 能被檢測到,可以更方便用調(diào)試工具調(diào)試,調(diào)試工具可以檢測到實時變化,而直接改變state中的屬性,則無法實時監(jiān)測

注意:mutations只能寫同步方法,不能寫異步,比如axios、setTimeout等,這些都不能寫,mutations的主要作用就是為了修改state的。

原因類似如果在mutations中寫異步,也能夠調(diào)成功,但是由于是異步的,不能被調(diào)試工具追蹤到,所有不推薦這樣寫,不利于調(diào)試,這是官方的約定。


7.Actions

  • action類似于mutation;actions的作用:保存觸發(fā)Mutations中修改全局共享數(shù)據(jù)的方法

不同在于:

  • action 提交(commit)的是 mutation ,而不是直接變更狀態(tài)

  • action 包含異步操作,類似于axios請求,可以都放在action中寫

  • action中的方法默認的就是異步,并且返回promise

代碼如下:

sate.js

export default {
    count: 0
  }

mutations-typa.js

export const CHANGE_COUNT = 'CHANGE_COUNT'

mutations.js

import {CHANGE_COUNT} from './mutations-type'
export default {
    [CHANGE_COUNT] (state,payLoad) {
      state.count = payLoad
    }
  }

actions.js

import {CHANGE_COUNT} from './mutations-type'

export default {
    setchangecount (context) {
      context.commit(CHANGE_COUNT)
    }
  }

Action 函數(shù)接受一個與 store 實例具有相同方法和屬性的 context 對象,因此你可以調(diào)用 context.commit 提交一個 mutation,或者通過 context.statecontext.getters 來獲取 state 和 getters。當我們在之后介紹到 Modules 時,你就知道 context 對象為什么不是 store 實例本身了。

  • 實踐中,我們會經(jīng)常用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 commit 很多次的時候):
actions: {
//用到 ES2015 的 參數(shù)解構(gòu) 來簡化代碼(特別是我們需要調(diào)用 `commit` 很多次的時候)
  setchangecount ({ commit } , payLoad) {
    commit(CHANGE_COUNT , payLoad)
  }
}
  • 在action內(nèi)部可以執(zhí)行異步操作:
actions: {
 setchangecount ({ commit }) {
    setTimeout(() => {
      commit(CHANGE_COUNT)
    }, 1000)
  }
}
  • mapActions 輔助函數(shù)
    mapActions 輔助函數(shù)的作用: 將actions中的方法映射組件中;
    原理:就如同,通過 store.dispatch(actions保存的觸發(fā)mutation中的方法) 映射actions中的方法

分發(fā)Action的兩種方式:
分發(fā)Action:也就相當于在界面中觸發(fā)Action方法

1.Action 通過 store.dispatch 方法觸發(fā):

store.dispatch('setchangecount')

2.在組件中分發(fā) Action:使用 mapActions 輔助函數(shù)將組件的 methods 映射為 store.dispatch 調(diào)用

如何使用mapActions 輔助函數(shù)?
1.Vuex實例對象部分:

state:{
  count:0
},
//這里并沒有使用常量替代 Mutation 事件
mutations: { //類似于methods
  changecount(state,payLoad){
    state.count  = payLoad
  }
}
actions: {
  setchangecount ({ commit } , payLoad) {
    commit('changecount' , payLoad)
  }
}

2.在.vue組件中引入,在js塊中引入

import { mapActions } from 'vuex'

3.在vue組件methods中定義一個數(shù)組(Vue實例對象部分):

  methods: {
    ...mapActions([
      'setchangecount', // 將 `this.setchangecount()` 映射為`this.$store.dispatch('setchangecount')`
    ]),
  }

上面的代碼就相當于下面的代碼:

  methods: {
     setchangecount(){
          return this.$store.dispatch('setchangecount')
    }
  }
  • Actions 支持同樣的載荷方式和對象方式進行分發(fā)
// 以載荷形式分發(fā)
store.dispatch('setchangecount', {
  amount: 10
})

// 以對象形式分發(fā)
store.dispatch({
  type: 'setchangecount',
  amount: 10
})
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容