Vuex狀態管理(Vue全家桶之一詳解)

Vuex

認識Vuex

Vuex 是一個專爲 Vue.js 應用程序開發的狀態管理模式。它採用集中式存儲管理應用的所有組件的狀態,並以相應的規則保證狀態以一種可預測的方式發生變化。Vuex 也集成到 Vue 的官方調試工具 devtools extension,提供了諸如零配置的 time-travel 調試、狀態快照導入導出等高級調試功能。

Vuex:
state: 數據
view : 視圖;將state數據展示到視圖上;
actions : 動態,更改或者是觸發視圖的動作
數據傳遞:把各個組件共享的狀態抽離出來,並且每個組件都可以使用它,並且還可以更改它;既然是公共狀態,當其中組件把這個公共狀態更改以後,凡是依賴這個公共狀態的組件都要更新;

  1. VUEX的數據存儲是響應式的;vue組件中使用了vuex的數據時,當vuex中的數據更新時,那麼使用數據的組件也會得到高效的更新
  2. 不能直接修改store中的狀態;需要commit一個mutation,這樣方便我們追蹤每一個數據的變化;
    Vuex : 適用中大型單頁面應用
<body>
    <div id="app">
        {{count}}
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
        // VUEX: 
        // state: 數據
        // view : 視圖;將state數據展示到視圖上;
        // actions :  動態,更改或者是觸發視圖的動作
        // 數據傳遞:把各個組件共享的狀態抽離出來,並且每個組件都可以使用它,並且還可以更改它;
      既然是公共狀態,當其中組件把這個公共狀態更改以後,凡是依賴這個公共狀態的組件都要更新;
        let  num =100;
        // 1. VUEX的數據存儲是響應式的;vue組件中使用了vuex的數據時,當vuex中的數據更新時,
那麼使用數據的組件也會得到高效的更新

        // 2. 不能直接修改store中的狀態;需要commit一個mutation,
這樣方便我們追蹤每一個數據的變化;

        // Vuex : 適用中大型單頁面應用
        let vm = new Vue({
            el:"#app",
            data:{
                count:0
            },
            methods:{
                addCount(){
                    this.count++;
                }
            },
            components:{
              
            }
        })
    </script>
</body>

表示“單向數據流”理念的簡單示意

  • 狀態自管理應用包含以下幾個部分:
  • state,驅動應用的數據源;
  • view,以聲明方式將 state 映射到視圖;
  • actions,響應在 view 上的用戶輸入導致的狀態變化。

vuex.jpg

Vuex多個組件共享狀態

vuex1.jpg

數據傳遞–>VueX(store)

組件: 組件就是抽離的公共的部分,這個公共的部分可以有自己獨立的html,css,js
實現複用:可複用,可維護

數據傳遞: vue中數據傳遞有哪些方式?

Vue是單項數據流,當父組件的數據發生更改,如果子組件也使用了父組件的數據,那麼子組件也會隨着更新;如果改變子組件這個數據,那麼父組件不能更改;

  1. 父傳子(props) props props=[“a”] :a=“num”
  2. 子傳父(自定義事件) @change=‘num’ this.emit("change",1000).sync:a.syncthis.emit("change",1000) .sync修飾符 :a.sync this.emit(“updata:a”,2000)
    3.兄弟組件(eventBus) let eventBus=new Vue eventBus.on()eventBus.on(事件池,方法) eventBus.emit(事件池)
    4.VueX(store) 把每一個stroe注入到各個組件裏 [state,mutations,actions,modules,getters]

Vuex的原理圖

  • let store = new Vuex.Store({})

vuex流程圖.jpg

Vuex中的使用-state

如果這個數據是好幾個組件共享的,這個數據放到VUEX的state上;如果這個數據只有當前這一個組件使用,那麼放到對應的組件的data中
store // 當前vue的實例就會新增一個storestore的屬性,屬性值時一個對象;同時也會給當前實例每一個子孫組件也新增一個store屬性
vuex更改store中狀態的唯一方法就是commit;
在vuex中更改公有的數據,必須通過commit一個mutation;
當共享的數據發生改變以後,那麼依賴這個數據的組件都會高效的更新;

<body>
    <div id="app">
        <first></first>
        <second></second>
        <!-- <div>{{this.$store.state.count}}</div> -->
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vuex/dist/vuex.min.js"></script>
    <script>
        // 如果這個數據是好幾個組件共享的,這個數據放到VUEX的state上;
      如果這個數據只有當前這一個組件使用,那麼放到對應的組件的data中

        // 在vuex中更改公有的數據,必須通過commit一個mutation;
        // 當共享的數據發生改變以後,那麼依賴這個數據的組件都會高效的更新;
        let store = new Vuex.Store({
            state: {
                count: 1001, // count : 這就是公共的數據
                shop: [{
                    name: "奔馳"
                }, {
                    name: "奧迪"
                }]
            },
            mutations: {
                increment(state, a) { // 參數接收到當前state
                    state.count += a;
                },
                min() {
                    // mutation 這個對象中的函數的第一個參數的實參都是state;
                }
            }
        });
        let first = {
            data() {
                return {}
            },
            methods: {
                addCount(val) {
                    // 提交的是mutation裏面的函數名;用於修改store中的數據
                    this.$store.commit("increment", val);
                }
            },
            // computed:{
            //     shop(){
            //         return this.$store.state.shop
            //     }
            // },
            // VUEx.mapState:將vuex的store中的state數據,映射到當前實例的計算屬性上;
            //computed:Vuex.mapState(["shop","count"]),
            computed: {
                tt() {

                },
                ...Vuex.mapState(["shop", "count"])
            },
            template: "<div>{{count}}<button @click='addCount(2)'>++</button>
          <li v-for='a in shop'>{{a.name}}</li></div>"
        };
        let second = {
            data() {
                return {}
            },
            template: "<div>{{this.$store.state.count}}</div>"
        }
        let vm = new Vue({
            el: "#app",
            data: {

            },
            components: {
                first,
                second
            },
            store // 當前vue的實例就會新增一個$store的屬性,屬性值時一個對象;
          同時也會給當前實例每一個子孫組件也新增一個$store屬性
        });
        console.log(vm);
    </script>
</body>

Vuex-getters

getters : vue允許定義getter;在store中的計算屬性;也會被緩存;當依賴的值發生變化,纔會發生改變

Vuex的私有屬性提供了mapState mapGetters MapMutations,可以將store中的state、getters、mutations都映射當前的實例的computed或methods;

<body>
    <div id="app">
        <first></first>
        <second></second>
        <!-- <div>{{this.$store.state.count}}</div> -->
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vuex/dist/vuex.min.js"></script>
    <script>
        // getters : vue允許定義getter;在store中的計算屬性;也會被緩存;當依賴的值發生變化,
      纔會發生改變
        let state= {
                count:1001,// count : 這就是公共的數據
                shop:[{name:"奔馳"},{name:"奧迪"}]
            }
        let getters={
            str(state){// state 就是store中的state
                return  state.count%2===0?"偶數":"奇數"
            },
            a(){
                
            }
        }
        let store= new Vuex.Store({
            state,
            getters,
            mutations:{
                increment(state,a){// 參數接收到當前state
                    state.count+=a;
                },
                min(){
                    // mutation 這個對象中的函數的第一個參數的實參都是state;
                }
            }
        });
        let first = {
            data(){
                return { 
                }
            },
            methods:{
                addCount(val){
                    // 提交的是mutation裏面的函數名;用於修改store中的數據
                    this.$store.commit("increment",val);
                }
            },
            // computed:{
            //     shop(){
            //         return this.$store.state.shop
            //     }
            // },
            // VUEx.mapState:將vuex的store中的state數據,映射到當前實例的計算屬性上;
            //computed:Vuex.mapState(["shop","count"]),
            computed:{
               tt(){

               },
               // Vuex的私有屬性提供了mapState  mapGetters MapMutations;
              可以將store中的state、getters、mutations都映射當前的實例的computed或methods;
               // 這幾個函數都返回一個包裝後的對象 {shop:12,count:22}
               // ... : 展開運算符展開,放到了computed這個對象中
               ...Vuex.mapState(["shop","count"]),
               ...Vuex.mapGetters(["str"])
            },
            template:"<div>{{count}}<button @click='addCount(1)'>++</button>
<li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
        };
        let second = {
            data(){
                return {}
            },
            template:"<div>{{this.$store.state.count}}</div>"
        }
        let vm = new Vue({
            el:"#app",
            data:{

            },
            components:{
                first,
                second
            },
            store // 當前vue的實例就會新增一個$store的屬性,屬性值時一個對象;
          同時也會給當前的子組件新增一個$store屬性
        });
        console.log(vm);
    </script>
</body>

Vuex-mutations :同步

在mutation的方法中,都是同步事務,不支持異步;

methods:{
//同步
// add(val){
// this.$store.commit(“add_count”,val);
// }
…Vuex.mapMutations([“add_count”]),
}

<body>
    <div id="app">
        <first></first>
        <second></second>
        <!-- <div>{{this.$store.state.count}}</div> -->
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vuex/dist/vuex.min.js"></script>
    <script>
        let state= {
                count:1001,// count : 這就是公共的數據
                shop:[{name:"奔馳"},{name:"奧迪"}]
            }
        let getters={
            str(state){// state 就是store中的state
                return  state.count%2===0?"偶數":"奇數"
            },
            a(){
                
            }
        }
        let store= new Vuex.Store({
            state,
            getters,
            mutations:{
                increment(state,a){// vuex更改store中狀態的唯一方法就是commit;
                    // state: 狀態    payload: 載荷  
                    //state.count+=a;//在mutation的方法中,都是同步事務,不支持異步;
                    setTimeout(()=>{
                        state.count+=a;
                    },2000)
                },
                min(){
                    
                }
            }
        });
        let first = {
            data(){
                return { 
                }
            },
            methods:{
                // addCount(val){
                //     this.$store.commit("increment",val);
                // }
                //...Vuex.mapMutations(["increment"])
                ...Vuex.mapMutations({
                    add:"increment"// 將this.add()映射成'this.$store.commit("increment")
                })
            },
            computed:{
               tt(){

               },
               ...Vuex.mapState(["shop","count"]),
               ...Vuex.mapGetters(["str"])
            },
            template:"<div>{{count}}<button @click='add(1)'>++</button>
          <li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
        };
        let second = {
            data(){
                return {}
            },
            template:"<div>{{this.$store.state.count}}</div>"
        }
        let vm = new Vue({
            el:"#app",
            data:{

            },
            components:{
                first,
                second
            },
            store 
        });
        
    </script>
</body>

commit提交

store對象風格提交方式.jpg

Vuex-actions :異步

actions 支持異步的,這裏面提交一個mutation的動作;
actions:{
// 支持異步
// 在項目中,經常從後端獲取數據賦值更改store中的state的值,
但是如果這個請求是異步的,大家需要在actions中發送請求,異步地更改state;
add({commit},val){// 提交一個mutations
// commit===KaTeX parse error: Expected 'EOF', got '}' at position 279: … }̲ } <…store.dispatch(“add”,val);
}
// commit==>mutations dipatch===> actions 在actions中是提交的mutations
// …Vuex.mapActions([“add”])
},

<body>
    <div id="app">
        <first></first>
        <second></second>
        <!-- <div>{{this.$store.state.count}}</div> -->
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script src="../node_modules/vuex/dist/vuex.min.js"></script>
    <script>
        // 工作需求中,經常發送異步的請求,請求回來的數據會賦值給Store中的state;
        let state= {
                count:1001,// count : 這就是公共的數據
                shop:[{name:"奔馳"},{name:"奧迪"}]
            }
        let getters={
            str(state){// state 就是store中的state
                return  state.count%2===0?"偶數":"奇數"
            },
            a(){
                
            }
        }
        let store= new Vuex.Store({
            state,
            getters,
            mutations:{
                increment(state,a){// vuex更改store中狀態的唯一方法就是commit;
                    // state: 狀態    payload: 載荷  
                    //state.count+=a;//在mutation的方法中,都是同步事務,不支持異步;
                    state.count+=a;
                },
                min(){
                    
                }
            },
            actions:{
                addNum({commit},a){
                    // commit : 就是store上的commit;提交
                    // actions 支持異步的,這裏面提價一個mutation的動作;
                    setTimeout(function(){
                        commit("increment",a);
                    },2000);
                   
                }
            }
        });
        let first = {
            data(){
                return { 
                }
            },
            methods:{
                // addCount(val){
                //     this.$store.commit("increment",val);
                // }
                //...Vuex.mapMutations(["increment"])
                // ...Vuex.mapMutations({
                //     add:"increment"// 將this.add()映射成'this.$store.commit("increment")
                // }),
                ...Vuex.mapActions({
                    addadd:"addNum"
                })
            },
            computed:{
               tt(){

               },
               ...Vuex.mapState(["shop","count"]),
               ...Vuex.mapGetters(["str"])
            },
            template:"<div>{{count}}<button @click='addadd(1)'>++</button><li v-for='a in shop'>{{a.name}}</li>{{str}}</div>"
        };
        let second = {
            data(){
                return {}
            },
            template:"<div>{{this.$store.state.count}}</div>"
        }
        let vm = new Vue({
            el:"#app",
            data:{

            },
            components:{
                first,
                second
            },
            store 
        });
        
    </script>
</body>

Vuex-module模塊

// module
let moduesA = {
state: {
a: 100
},
getters: {
str1() {
return 99
}
}
};

let store = new Vuex.Store({
state: {
count: 0
},
modules: {
// 這個modules會把各自模塊的state,getters,mutations,actions最終都會注入到該Store中
first: moduesA,
second: moduesB,
}
});
使用: {{$store.state.first.a}}

模塊區分.jpg

模塊區分2.jpg

<body>
    <div id="app">
        <first></first>
        <second></second>
    </div>

    <!--  IMPORT JS -->
    <script src="../node_modules/vuex/dist/vuex.min.js"></script>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
        // module
        let moduesA = {
            state: {
                a: 100
            },
            getters: {
                str1() {
                    return 99
                }
            }
        };
        let moduesB = {
            state: {
                num: 88
            }
        };
        let store = new Vuex.Store({
            state: {
                count: 0
            },
            getters: { //vuex 計算屬性
                str(state) {
                    return state.count % 2 === 0 ? "偶數" : "奇數"
                }

            },
            mutations: {
                add_count(state, payload) { //state,默認接受一個state
                    state.count += payload
                },
                cut_count(state, payload) { //state,默認接受一個state
                    state.count -= payload
                }
            },
            actions: {
                // 支持異步  
                cut({
                    commit
                }, val) { //提交一個mutations
                    // commit===$store.commit
                    setTimeout(_ => {
                        commit("cut_count", val)
                    }, 1000)
                }
            },
            modules: {
                // 這個modules會把各自模塊的state,getters,mutations,actions
              最終都會注入到該Store中
                first: moduesA,
                second: moduesB,
            }
        });
        let first = {
            data() {
                return {}
            },
            methods: {
                // add(val) {
                //     this.$store.commit("add_count", val)
                // }
                ...Vuex.mapMutations(["add_count"]),
                // 1
                // ...Vuex.mapActions(["cut"]),
                // 2
                cut(val) {
                    this.$store.dispatch("cut", val)
                }

            },
            computed: {
                ...Vuex.mapGetters(["str","str1"])
        
            },
            template: "<div>{{$store.state.count}}<button @click='add_count(1)'>++
          </button>{{str}}{{$store.state.first.a}}{{str1}}<button @click=' cut(1)'>++
          </button></div>"
        };
        let second = {
            data() {
                return {}
            },
            template: "<div></div>"
        };
        let vm = new Vue({
            el: '#app',
            created() {
                console.log(this.$store);
                
            },
            data: {

            },
            components: {
                first,
                second
            },
            store //給當前實例新增一個$store的屬性,並且給當前實例每一個子孫組件也新增一個$store
        });
    </script>
</body>

Vuex核心源碼封裝

<body>
    <div id="app">
        {{$store.state.msg}}
        {{$store.getters.str}}
        <child></child>
    </div>
    <script src="../node_modules/vue/dist/vue.js"></script>
    <script>
        // vuex:只要改變了state,凡是依賴state的組件都會高效的更新;
        // new Vue的data屬性;
        let Vuex=(function(){
            class Store{
                constructor(options){
                    // this==> 返回值store實例
                    // 初始化一個state;如果傳遞進來的state,會給其默認一個{}
                    // 爲了能讓數據能夠監聽到,當數據發生改變,依賴的視圖也要更新的;
                    let vm  = new Vue({
                        data:{
                            state:options.state||{}
                        }
                    });
                    //this.state=options.state||{};
                    // 將Vm的state的空間地址賦值給了this.state
                    this.state = vm.state;
                    // this==> $store
                    this.mutations={};
                    let mutations=options.mutations;//這個就是傳遞進來的那個mutations
                    // Object.keys : 把對象中的屬性名挑出來放到一個數組中
                    // 就是在實例身上準備一個mutations對象,裏面包含了options外界傳遞進來的方法,
                  那麼方法中的this已經是指向了store這個實例;
                    // 
                    Object.keys(options.mutations).forEach(key=>{
                        //this.mutations[key].bind(this,this.state)
                        this.mutations[key]=(payload)=>{
                            options.mutations[key].call(this,this.state,payload)
                        }
                    });
                    // 執行私有屬性的方法時,調用原型上的方法;
                    let commit = this.commit;// 把原型的commit給了變量commit;
                    // 給當前實例新增一個commit屬性,屬性值是一個函數
                    this.commit=(type,option)=>{
                        commit.call(this,type,option)
                    }
                    // this.commit=function(type,option){
                    //     options.mutations[type].call(this,option)
                    // }
                    
                    // actions 
                    this.actions = {};
                    let actions = options.actions||{};
                    Object.keys(actions).forEach(key=>{
                        this.actions[key]=(option)=>{
                            // 第一個this指向把函數中的this改成當前實例store
                            // 把store傳給action的方法;
                            actions[key].call(this,this,option)
                        }
                    });
                    let dispatch = this.dispatch;
                    this.dispatch=(type,option)=>{
                        dispatch.call(this,type,option)
                    }

                    // getters
                    this.getters={};
                    let getters = options.getters||{};
                    // Object.keys : 將對象的屬性名收集起來放到一個數組中
                    Object.keys(getters).forEach(key=>{
                        // 給getters中每一個屬性新增一個get方法;
                        Object.defineProperty(this.getters,key,{
                            get:()=>{
                                // 會進行緩存,只有依賴的屬性發生改變會執行;
                                return getters[key].call(this,this.state)
                            }
                        });
                    });
                }
                // 把commit 放到store的原型上
                commit(type,payload){
                    console.log(this);// Store的實例
                    this.mutations[type](payload)
                }
                dispatch(type,option){
                    this.actions[type](option)
                }
            }
            //...Vuex.mapState(['a',"b"]);
            // 將store中的state放到當前的computed屬性中
            function mapState(ary){
                let obj ={};
                ary.forEach(key=>{
                    obj[key]=function(){
                        console.log(this);
                        // this 執行組件的實例
                        return this.$store.state[key]
                    }
                })
                return obj;
            }
            function mapGetters(ary){
                let obj ={};
                ary.forEach(key=>{
                    obj[key]=function(){
                        return this.$store.getters[key]
                    }
                })
                return obj;
            }
            function mapActions(ary){
                let obj ={};
                ary.forEach(key=>{
                    obj[key]=function(option){
                        return this.$store.dispatch(key,option)
                    }
                })
                return obj;
            }
            function mapMutations(ary){
                let obj ={};
                ary.forEach(key=>{
                    obj[key]=function(option){
                        return this.$store.commit(key,option)
                    }
                })
                return obj;
            }
            // ...Vuex.mapState(["count"])
            function install(_Vue){
                // _Vue和外面的Vue指向同一個空間地址
                // _Vue : 就是Vue這個類函數;
                // mixin : 將生成store通過mixin方法注入到每一個組件上
                _Vue.mixin({
                    beforeCreate(){// 比組件內部的beforeCreate早執行
                        // 生成一個組件的實例,就會執行一次;並且在自己的beforecreate之前執行的;
                        //console.log(this);// 代表每一個組件實例;
                        // this --> Vue的實例vm
                        // 第二次執行 組件的實例
                        //this.$store=this.$options.store
                        //console.log(this);
                        // 這個會進行循環遍歷,
                        if(this.$options&&this.$options.store){
                            // 如果該條件是成立的,說明是vm實例;
                            this.$store=this.$options.store;
                        }else{
                            // 如果不成立,說明是子孫組件
                            // 如果父組件存在,那麼就把父組件的$store屬性賦值給子組件的$store屬性;
                            // $parent : 指的是當前組件的父組件
                            this.$store =this.$parent&&this.$parent.$store
                        }
                    }
                })
            }
            return {
                Store,
                install,
                mapState,
                mapActions,
                mapGetters,
                mapMutations
            }
        })();
        // Vue的插件必須使用Vue.use;只是vuex會默認檢測到是vue的官方插件,看不到vue.use;vue.use
執行時,會默認調用裏面的install;
        Vue.use(Vuex);
        // Vuex.Store
        // Vuex.mapState
        // store 是Store的一個實例;並且這個實例最後放到了vue的實例屬性上;
        let store = new Vuex.Store({
            state:{
                count:100,
                msg:"李明帥"
            },
            mutations:{
                add(state,val){
                    // this==> store
                    console.log(state);
                    state.count+=val;
                }
            },
            actions:{
                addNum({commit},val){
                    commit("add",val);
                }
            },
            getters:{
                str(state){
                    return state.count%2===0?"偶數":"奇數";
                }
            }
        });
        let child={
            created(){
                // 組件在使用時,纔會觸發其鉤子函數
            },
            methods:{
                fn(){
                    // this.$store===store==Store的實例
                    this.$store.commit("add",100);
                   // this.$store.dispatch("addNum",1)
                }
            },
            computed:{
                str(){

                },
                ...Vuex.mapState(['count'])
            },
            template:"<div>{{$store.state.count}}{{count}}<button @click='fn'>增加
          </button></div>"
        }
        // $store 會添加到每一個組件的實例上;
        let vm = new Vue({
            el:"#app",
            store,// 會給當前實例以及當前實例的所有子孫組件都會新增一個$store屬性;
            //a:100,
            beforeCreate(){
                //console.log(this);
                // debugger 
            },
            // 把Store的一個實例給了這個store屬性
            // 組件在註冊時,不會調用生命週期鉤子函數,
            components:{child}
        });
        //console.log(vm);// 目前vm身上沒有$store
        // $options:代表 :new的對象,會把對象中的鍵值對添加到當前實例的$options上
        // 1.先準備vuex對象【Store,install】;
        // 2. Vue.use執行調用了裏面install,install執行調用了Vuex.mixin,對Vue這個類進行了修改
        // 3.生成了一個store
        // 4.new Vue;把store放到了實例的$options
        // 5.隨後vm的生命週期,執行了mixin中的beforeCreate=>把options的store直接賦值給了實例的
$store屬性;
            
            
    </script>
</body>
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章