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. e m i t ( " c h a n g e " , 1000 ) . s y n c : a . s y n c t h i s . emit("change",1000) .sync修饰符 :a.sync this. emit(“updata:a”,2000)
    3.兄弟组件(eventBus) let eventBus=new Vue eventBus. o n ( ) e v e n t B u s . 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的实例就会新增一个 s t o r e store的属性,属性值时一个对象;同时也会给当前实例每一个子孙组件也新增一个 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>
发布了51 篇原创文章 · 获赞 13 · 访问量 3056

猜你喜欢

转载自blog.csdn.net/Sheng_zhenzhen/article/details/104623454