简述Vue中使用Vuex

1、为什么要用vuex

      在vue组件通信的过程中,我们通信的目的往往就是在组件之间传递数据或组件的状态(这里将数据和状态统称为状态),进而更改状态。但可以看到如果我们通过最基本的方式来
进行通信,一旦需要管理的状态多了,代码就会变得十分混乱。对所有状态的管理便会显得力不从心,尤其是多人合作的时候。此时vuex出现了,他就是帮助我们把公用的状态全抽出来放
在vuex的容器中,然后根据一定的规则来进行管理。

2、概念

  • 概念:vuex是一个状态管理工具,每一个Vuex应用的核心就是store(仓库);“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state),它的实例是唯一的-----单例模式;  一般把需要共享的数据放到store中;
  • 功能:存数据、取数据、改数据;
  • 出现场景:
    • 涉及到非父子关系的组件,例如兄弟关系、祖孙关系,甚至更远的关系组件之间的联系;
    • 在大型单页应用中,考虑如何更好地在组件外部管理状态;
  • 注意:vuex的数据处理流程是一个"单向"的数据流 ;
  • vuex的核心:
    • state:存放数据(状态);    取数据  this.$store.state.变量名
    • mutations:存放如何更改状态,同步方法放到mutations(同步)里面;    调用方法 this.$store.commit("方法名","参数")
    • getters:相当于计算属性,从state中派生出状态,比如将state中的某个状态进行过滤然后获取新的状态;      this.$store.getters.方法名(sum)
    • actions:mutations的加强版,异步方法放在了actions(动作)里面;      调用方法this.$store.dispatch("方法名"),异步方法则会调用mutations里面的同步方法
    • modules:就是当用这个容器来装这些状态还是显得混乱的时候,我们就可以把容器分成几块,把状态和管理规则分类来装。这和我们创建js模块是一个目的,让代码结构更清晰。

3、基本使用

  • vuex的安装
    cnpm i vuex --save
  • 安装成功后,
    • 新手入门    注意:vuex中是不允许直接修改store的状态值,我们必须通过mutations进行修改操作
      • 新建store文件夹,内容如下

                               

      • 在app.js里面进行引入
        import store from './store'
        new Vue({
            router,
            i18n,
            store,
            el: '#app',
            render: h => h(App)
        })
      • store文件夹中index.js的内容为
        import Vue from 'vue'
        import Vuex from 'vuex'
        
        import state from './state'
        import actions from './actions'
        import mutations from './mutations'
        
        Vue.use(Vuex)
        
        export default new Vuex.Store({
            state,
            actions,
            mutations
        })
      • 在state.js中定义状态
        export default {
            count: 1,
        }
      • 在mutations.js里面定义同步方法
        export default {
            // 改变状态的执行者   用于同步更改状态
            stateMutationsAdd(state, payLoad) {
                // 第一个参数是state   第二个参数是调用mutations时传入的参数
                state.count += payLoad;
            },
            stateMutationsReduce(state, payLoad) {
                state.count -= payLoad;
            }
        }
      • 在actions.js里面定义异步操作
        export default {
            // actions并不直接更改状态   而是发起mutations来更改状态
            stateAsyncReduce(context) {
                // context 是一个与 store 实例具有相同方法和属性的 context 对象
                // 这个异步操作  我们可以发送http请求、定时器、
                setTimeout(() => {
                    context.commit("stateMutationsReduce", 5)//不能用this.$store,为undefined
                }, 1000)
            }
        }
      • 在vue组件中使用(将state与getters结合进组件需要使用计算属性,将mutations与actions结合进组件需要在methods里面进行调用)
        <template>
          <div class="home">
            <div class="block">
              <h1>vuex的基本使用</h1>
              <el-button size="mini" type="primary" @click="reduceNumber">-</el-button>
              &nbsp;&nbsp;{{count}}
              <el-button size="mini" type="primary" @click="addNumber">+</el-button>
              <h1>vuex练习结束</h1>
            </div>
          </div>
        </template>
        <script>
        export default {
          name: "home",
          data() {
            return {};
          },
          computed: {
            count() {
              return this.$store.state.count;
            }
          },
          methods: {
            /**
             * [addNumber  对count数据进行增加操作  采用同步方式]
             * @return {[type]} [description]
             */
            addNumber() {
              // 第一个参数是同步方法的名称   第二个参数是传递给方法的数据
              this.$store.commit("stateMutationsAdd", 10);
            },
            /**
             * [reduceNumber  对count数据进行减少操作   采用异步方式]
             * @return {[type]} [description]
             */
            reduceNumber() {
              // dispatch返回的是actions执行的结果,是一个promise对象,如果异步操作之后还需要其他操作,可以使用.then/.catch等
              this.$store.dispatch("stateAsyncReduce");
            }
          },
          mounted() {}
        };
        </script>
        View Code
      • 效果图

                             

        点击 + 进行同步增加     

        点击-进行异步减少,每隔1s减少5

    • 新手入门之后,我们可以尝试将mutations里面的一些方法名称提取出来,从而提高代码维护性;
      • state.js
        export default {
            count: 1,
        }
      • mutation-types.js
        export const STATE_MUTATIONS_ADD = 'stateMutationsAdd'
        export const STATE_MUTATIONS_REDUCE = 'stateMutationsReduce'
      • mutations.js
        import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from './mutation-types'
        export default {
            // 改变状态的执行者   用于同步更改状态
            [STATE_MUTATIONS_ADD](state, payLoad) {
                // 第一个参数是state   第二个参数是调用mutations时传入的参数
                state.count += payLoad;
            },
            [STATE_MUTATIONS_REDUCE](state, payLoad) {
                state.count -= payLoad;
            }
        }
        View Code
      • actions.js
        import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from './mutation-types'
        export default {
            // actions并不直接更改状态   而是发起mutations来更改状态
            stateAsyncReduce(context) {
                // context 是一个与 store 实例具有相同方法和属性的 context 对象
                // 这个异步操作  我们可以发送http请求、定时器、
                setTimeout(() => {
                    context.commit(STATE_MUTATIONS_REDUCE, 5)//不能用this.$store,为undefined
                }, 1000)
            }
        }
        View Code
      • vue组件中的使用
        <template>
          <div class="home">
            <div class="block">
              <h1>vuex的基本使用</h1>
              <el-button size="mini" type="primary" @click="reduceNumber">-</el-button>
              &nbsp;&nbsp;{{count}}
              <el-button size="mini" type="primary" @click="addNumber">+</el-button>
              <h1>vuex练习结束</h1>
            </div>
          </div>
        </template>
        <script>
        import {
          STATE_MUTATIONS_ADD,
          STATE_MUTATIONS_REDUCE
        } from "../store/mutation-types";
        export default {
          name: "home",
          data() {
            return {};
          },
          computed: {
            count() {
              return this.$store.state.count;
            }
          },
          methods: {
            /**
             * [addNumber  对count数据进行增加操作  采用同步方式]
             * @return {[type]} [description]
             */
            addNumber() {
              // 第一个参数是同步方法的名称   第二个参数是传递给方法的数据
              this.$store.commit(STATE_MUTATIONS_ADD, 10);
            },
            /**
             * [reduceNumber  对count数据进行减少操作   采用异步方式]
             * @return {[type]} [description]
             */
            reduceNumber() {
              // dispatch返回的是actions执行的结果,是一个promise对象,如果异步操作之后还需要其他操作,可以使用.then/.catch等
              this.$store.dispatch("stateAsyncReduce");
            }
          },
          mounted() {}
        };
        </script>
        View Code
    • 为了方便起见,利用vuex提供的mapState、mapGetters、mapMutations以及mapActions四个方法将这些功能结合进组件
      • 其他文件跟上述保持一致
      • vue组件中的使用
        <template>
          <div class="home">
            <div class="block">
              <h1>vuex的基本使用</h1>
              <el-button size="mini" type="primary" @click="stateAsyncReduce">-</el-button>
              &nbsp;&nbsp;{{count}}
              <p>使用getters{{name}}</p>
              <el-button size="mini" type="primary" @click="stateMutationsAdd(10)">+</el-button>
              <h1>vuex练习结束</h1>
            </div>
          </div>
        </template>
        <script>
        import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
        export default {
          name: "home",
          data() {
            return {};
          },
          computed: {
            ...mapState(["count"]),
          },
          methods: {
            ...mapActions(["stateAsyncReduce"]),
            ...mapMutations(["stateMutationsAdd"])
          },
          mounted() {}
        };
        </script>
        View Code
    • 当你采用了上述方式进行整合之后,依旧存在过多的状态,导致代码混乱的现象,我们可以将store分割成模块(module),每个模块都拥有自己的state、getters、mutations以及actions,甚至是嵌套子模块,从上至下按照同样的方式进行分割。
      • 首先,我们看一下目录结构

                              

 

      •  store/index.js
        import Vue from 'vue'
        import Vuex from 'vuex'
        import * as actions from './actions'
        import modules from './modules'
        Vue.use(Vuex)
        export default new Vuex.Store ({
            actions,
            modules,
            strict: false
        })
      • store/mutation-types.js
        export const STATE_MUTATIONS_ADD = 'stateMutationsAdd'
        export const STATE_MUTATIONS_REDUCE = 'stateMutationsReduce'
      • store/actions.js(大型项目中我们可以将所有的异步操作提取出来)
        import * as types from './mutation-types'
        const makeAction = (type) => {
          return ({ commit }, ...args) => commit(type, ...args)
        }
        //
        export const stateAsyncReduce = makeAction(types.STATE_MUTATIONS_REDUCE)
        export function stateAsyncReduce(context) {
          // context 是一个与 store 实例具有相同方法和属性的 context 对象
          // 这个异步操作  我们可以发送http请求、定时器、
          setTimeout(() => {
            context.commit(types.STATE_MUTATIONS_REDUCE, 5)//不能用this.$store,为undefined
          }, 1000)
        }
      • module/index.js
        // 这段代码是一个 JavaScript 模块化的导入和导出示例。首先,通过 `require.context('.', false, /\.js$/)` 创建了一个上下文,表示从当前目录下导入所有以 `.js` 结尾的文件
        // `modules` 是一个空对象,用来存储导入的模块。然后,通过 `files.keys().forEach()` 遍历上下文中的文件,在每次循环中判断文件的路径是否为 `./index.js`,如果是的话就跳过不处理。
        // 其他文件的路径经过正则表达式替换的处理后,将其作为属性名,将对应的模块(即 `files(key).default` 的值)存储在 `modules` 对象中。
        // 最后,通过 `export default modules` 将 `modules` 导出为默认模块,即其他文件可以通过 `import modules from './modules'` 导入`modules` 对象并使用其中的模块
        
        // require.context('.', false, /\.js$/) 是 Webpack 中的一个方法,用于创建一个上下文来导入指定目录下的所有满足特定条件的模块
        // 第一个参数 '.' 表示上下文的目录,这里表示当前目录
        // 第二个参数 false 表示是否递归遍历子目录,这里设为 false 表示不递归遍历子目录,只导入当前目录下的模块
        // 第三个参数 /\.js$/ 是一个正则表达式,用于匹配文件名的模式。这里的 /\.js$/ 表示匹配以.js结尾的文件
        
        // 通过 require.context 创建的上下文可以在编译时动态导入模块,常用于在组件中自动导入匹配特定条件的模块文件
        
        // import.meta.glob 是 JavaScript 中的一个 API,用于根据模式动态导入模块。与 require.context 不同的是,import.meta.glob 在导入模块时会自动递归遍历子目录。
        
        // import.meta.glob 不接受第二个参数来控制是否递归遍历子目录,而是使用了 ** 通配符来表示递归遍历子目录。这使得它更方便地处理包含多级目录的模块导入
        
        const files = require.context('.', false, /\.js$/)
        
        const modules = {}
        
        files.keys().forEach((key) => {
          if (key === './index.js') return
          modules[key.replace(/(\.\/|\.js)/g, '')] = files(key).default
        })
        
        export default modules
      • module/home.js(home可以自定义,以后项目中随机建立的模块名称)
        import { STATE_MUTATIONS_ADD, STATE_MUTATIONS_REDUCE } from '../mutation-types'
        const state = {
            count: 1
        }
        
        const mutations = {
            [STATE_MUTATIONS_ADD](state, payLoad) {
                // 第一个参数是state   第二个参数是调用mutations时传入的参数
                state.count += payLoad;
            },
            [STATE_MUTATIONS_REDUCE](state, payLoad) {
                state.count -= payLoad;
            }
        }
        
        export default {
            state,
            mutations
        }
        View Code
      • vue组件中的使用
        <template>
          <div class="home">
            <div class="block">
              <h1>vuex的基本使用</h1>
              <el-button size="mini" type="primary" @click="stateAsyncReduce">-</el-button>
              <span>{{count}}</span>
              <el-button size="mini" type="primary" @click="stateMutationsAdd(10)">+</el-button>
              <h1>vuex练习结束</h1>
            </div>
          </div>
        </template>
        <script>
        import { mapState, mapGetters, mapMutations, mapActions } from "vuex";
        export default {
          name: "home",
          data() {
            return {};
          },
          computed: {
            ...mapState({
              count: state => state.home.count
            })
          },
          methods: {
            ...mapActions(["stateAsyncReduce"]),
            ...mapMutations(["stateMutationsAdd"])
          },
          mounted() {}
        };
        </script>
        View Code
      • 特别强调的是:
        ...mapState({
           count: state => state.home.count(home为模块的名称)
        })

4、遇到的问题

   暂无

 
posted @ 2019-12-04 17:48  北栀女孩儿  阅读(790)  评论(0编辑  收藏  举报