1. Vuex

  • 状态管理

2.Vuex管理些什么

  • 用户登录状态, token , 头像 ,地理位置等
  • 商品收藏,购物车中的东西

3. Vuex安装

  • 使用npm安装Vuex

    npm install vuex --save
    
  • 在项目的src目录下面创建一个store目录,并在该目录下面创建一个index.js

    index.js内容如下:

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    // 1. 安装插件
    Vue.use(Vuex)
    // 2. 创建对象
    const store = new Vuex.Store({
        state:{},
        mutations:{},
        actions:{},
        getters:{},
        modules:{}
    })
    
    // 3. 导出store独享
    export default store
    
  • 在main.js中挂载vuex

    import Vue from 'vue'
    import App from './App.vue'
    import store from "./store";
    Vue.config.productionTip = false
    
    new Vue({
      render: h => h(App),
      store
    }).$mount('#app')
    
    

4. Vuex的基本使用

4.1 state

  • 在state中定义一个变量之后,全局都可以使用.而且还是响应式的,使用起来很方便
const store = new Vuex.Store({
    state:{
        counter: 1000  // 定义一个counter
    },
    mutations:{},
    actions:{},
    getters:{},
    modules:{}
})
  • 在组件中使用counter
  <div class="hello">
    <h2>{{$store.state.counter}}</h2>
  </div>

4.2mutations

  • mutations是对state中的状态数据进行管理.

    const store = new Vuex.Store({
        state: {
            counter: 1000
        },
        mutations: {
            // 提供一个increment方法,供组件进行调用
            increment(state) {
                state.counter++;
            },
              // 提供一个increment方法,供组件进行调用
            decrement(state) {
                state.counter--
            }
        },
        actions: {},
        getters: {},
        modules: {}
    })
    
  • 组件调用mutations中的方法

    <script>
        export default {
            name: 'HelloWorld',
            props: {
                msg: String
            },
            methods: {
                add() {
                    this.$store.commit('increment')  // 调用increment方法
                },
                sub() {
                    this.$store.commit('decrement') // 调用decrement方法
                }
            }
        }
    </script>
    
  • 示例2:

    • 需求: 对counter每次添加一个任意数

    • mutations方法定义incrementNum

          mutations: {
              // 定义一些方法
              increment(state) {
                  state.counter++;
              },
              decrement(state) {
                  state.counter--
              },
              // 每次添加x
              incrementNum(state, number) {
                  return state.counter += number
              }
          },
      
    • 组件调用mutations中的incrementNum方法

       <button @click="addNum(5)">+5</button>
       <button @click="addNum(10)">+10</button>
      
      methods: {
                  add() {
                      this.$store.commit('increment')
                  },
                  sub() {
                      this.$store.commit('decrement')
                  },
                  addNum(number) {
                      this.$store.commit('incrementNum',number)
                  }
              }
      
    • 看得出来,mutations中方法传参很方便. 当然也可以使用对象传参.

  • 示例3. 响应式新增/删除属性

    •  state: {
              info: {
                  name: 'jet',
                  age: 19,
                  gender: 'female'
              }
          },
      
    •   mutations: {
              updateInfo(state) {
                  // 给info添加一个address属性.
                  // state.info.address='bj'  // 这种方式可以修改state中的info属性,但是info不会响应式更新组件
                  Vue.set(state.info, 'address', 'Bj')  // 使用这种方式,可以响应式的更新info中的属性.
      
                  // 删除属性
                  // delete state.info.age  // 不是响应式
                  Vue.delete(state.info,'age')  // 响应式的删除info中的age属性
              }
          },
      

4.3 getters

  • 如果想要获取state中数据产生的变异结果,就可以使用getters,它相当于vue中的计算属性

  • 示例1

    const store = new Vuex.Store({
        state: {
            counter: 1000
        },
        mutations: {
            increment(state) {
                state.counter++;
            },
            decrement(state) {
                state.counter--
            }
        },
        actions: {},
        getters: {
            powerCounter(state) {  // 定义一个方法
                return state.counter * state.counter
            }
        },
        modules: {}
    })
    
  • Vue组件如何调用getters方法

     <h2>演示getters方法: {{$store.getters.powerCounter}}</h2>
    
  • 示例2

    const store = new Vuex.Store({
        state: {
            counter: 1000,
            students:[
                {id:1,name:'kobe',age:30},
                {id:2,name:'jet',age:18},
                {id:3,name:'kk',age:39},
                {id:4,name:'jj',age:26},
            ]
        },
        mutations: {
            // 定义一些方法
            increment(state) {
                state.counter++;
            },
            decrement(state) {
                state.counter--
            }
        },
        actions: {},
        getters: {
            powerCounter(state) {  // 定义一个方法
                return state.counter * state.counter
            },
            // 获取年纪大于20的student
            ageGt20(state) {
                return state.students.filter(x => x.age > 20);
            }
        },
        modules: {}
    })
    
    <h2>HelloWorld,演示getters方法: {{$store.getters.ageGt20}}</h2>
    
    <h1>APP 演示getters{{$store.getters.ageGt20}}</h1>
    

    可以看到在使用getters的好处是,定义一个方法,在项目中任一组件中都可以方便的调用. -

  • 示例2也可以使用计算属性进行实现

 computed: {
            ageMore20() {
                return this.$store.state.students.filter(x => x.age > 20)
            }
        },
<h2>HelloWorld,使用计算属性: {{ageMore20}}</h2>

但是,使用计算属性有个缺点,那就是在哪个组件中声明就只能在那个组件中使用.

  • getters中定义的方法还可以传入getters作为入参

        getters: {
            powerCounter(state) {  // 定义一个方法
                return state.counter * state.counter
            },
            // 获取年纪大于20的student
            ageGt20(state) {
                return state.students.filter(x => x.age > 20);
            },
            // 获取年纪大于20岁的student的个数
            ageGt20Count(state,getters) {
                return getters.ageGt20.length
            }
        },
    

    总结 : getters中定义的方法可以有两个入参,第1个是state,第2是getters本身.

  • 示例3 (高级版)

    • 需求: 获取年纪大于x岁的学生信息

    • vuex中getters中的代码

      const store = new Vuex.Store({
          state: {
              counter: 1000,
              students:[
                  {id:1,name:'kobe',age:30},
                  {id:2,name:'jet',age:18},
                  {id:3,name:'kk',age:39},
                  {id:4,name:'jj',age:26},
              ]
          },
          mutations: {
              // 定义一些方法
              increment(state) {
                  state.counter++;
              },
              decrement(state) {
                  state.counter--
              }
          },
          actions: {},
          getters: {
              powerCounter(state) {  // 定义一个方法
                  return state.counter * state.counter
              },
              // 获取年纪大于20的student
              ageGt20(state) {
                  return state.students.filter(x => x.age > 20);
              },
              // 获取年纪大于20岁的student的个数
              ageGt20Count(state,getters) {
                  return getters.ageGt20.length
              },
              
              // 获取年纪大于x岁的student
              ageGtx(state) {
                  return function (x) {
                      return state.students.filter(stu=> stu.age > x)
                  }
              }
          },
          modules: {}
      })
      
    • 组件调用ageGtx

              <h2>HelloWorld,演示getters使用,获取年纪大于x岁的: {{$store.getters.ageGtx(10)}}</h2>
      
      
    • 说明

      •   ageGtx(state) {
                    return function (x) {
                        return state.students.filter(stu=> stu.age > x)
                    }
                }
        

        看这段代码 ,ageGtx返回的是一个function,且这个function还是一个带参的function

      • {{$store.getters.ageGtx(10)}}
        

        {{$store.getters.ageGtx}}这段代码的结果其实就是ageGtx返回的function, 而(10)就是调用这个function,并且入参是10.

      • 通过这种方式实现,感觉很巧妙呀.

      • 上面的ageGtx可以用箭头函数进行简写

         ageGtx(state) {
                  return x => {
                        return state.students.filter(stu => stu.age > x)
                    }
                }
        

4.4 actions

  • 所有的异步操作都在actions中完成.

  • 示例1

    • Vue组件---> Actions----> Mutations----> State ---> 响应到Vue组件.

    • Mutations中定义的方法,直接操作State

       mutations: {
              updateInfo(state) {
                  // 给info添加一个address属性.
                  // state.info.address='bj'  // 这种方式可以修改state中的info属性,但是info不会响应式更新组件
                  Vue.set(state.info, 'address', 'Bj')  // 使用这种方式,可以响应式的更新info中的属性.
      
                  // 删除属性
                  // delete state.info.age  // 不是响应式
                  Vue.delete(state.info,'age')  // 响应式的删除info中的age属性
              }
          },
      
    • Actions中定义一个aUpdateInfo方法,方法内部的逻辑是异步执行.

         actions: {
              aUpdateInfo(context) {
              	// 异步方法
                  setTimeout(() => {
                      context.commit('updateInfo')  // 在actions中调用mutations中的updateInfo方法,对state中的info进行修改
                  }, 1000);
              }
      
          },
      
    • Vue组件调用Actions中的方法

       methods: {
               
                  updateInfo() {
                      // this.$store.commit('updateInfo')// 这个通过Mutations操作state
                      this.$store.dispatch('aUpdateInfo') // 这个通过Actions操作Mutations,再通过Mutations操作state
                  }
              }
      
    • 注意: 操作Mutations中方法用commit方法,操作Actions中方法用dispatch

  • 示例2(传参)

    • Vue组件在调用Actions中的方法时,传参,跟调用Mutations中的方法一样, 可以直接传对象

    • Vue组件的methods属性中定义方法updateXXX

      updateXXX() {
      	this.$store.dispatch('updateXXX', '测试传参');// 调用Actions中的方法
      }
      
    • Vuex中Actions定义的updateXXX

       updateXXX(context,msg) {
                  setTimeout(()=>{
                      console.log(msg);  // 测试传参
                  },1000)
              }
      
  • 示例3(回调)

    • 需求: 以示例2为基础,如果Actions中的updateXXX方法,打印msg成功之后,给Vue组件发个通知(回调函数)

    • Vue组件的methods属性中定义方法updateXXX

      updateXXX() {
                      this.$store.dispatch('updateXXX', {
                          msg:'测试传参',
                          callback: ()=>{
                              console.log("传参成功,回调打印...")
                          }
                      });
                  }
      
    • Vuex中Actions定义的updateXXX

       updateXXX(context,payload) {
                  setTimeout(()=>{
                      console.log(payload.msg);
                      payload.callback()  // 执行回调函数
                  },1000)
              }
      
    • 如果不传参,在Vue组件的updateXXX方法中,可以直接传函数

       updateXXX() {
              
              this.$store.dispatch('updateXXX',()=>{
              	console.log("传参成功,回调打印...")
              })
         }
      
  • 示例4(回调,使用Promise,推荐.)

    • Actions中定义updateXXX方法

       updateXXX(context, msg) {
                  return new Promise((resolve, reject) => {
                      setTimeout(() => {
                          console.log(msg);
                          resolve("action方法执行完毕,这是一条回调信息")
                      }, 1000)
                  })
              }
      
    • Vue组件methods中定义调用action的方法

        updateXXX() {
                      this.$store
                          .dispatch('updateXXX', '我是一条msg')
                          .then(res=>{
                              console.log('即将打印action的回调信息');
                              console.log(res)
                          });
                  }
      
    • 总结 action中的updateXXX返回的是一个Promise对象,所以调用方使用then去接收回调信息.

4.5 module

  • 使用示例

    • 声明一模块a

      // 定义一个模块a
      const a = {
          state: {
              name: 'kobe'
          },
          mutations: {},
          actions: {},
          getters: {}
      }
      
    • 将模块a挂到Vuex.Store的module中

          modules: {
              a
          }
      
    • Vue组件中如何调用a模块中的name属性呢

        <h2>模块a中的name : {{$store.state.a.name}}</h2>
      
  • 示例2: 如何操作模块a中的mutations方法

    • 先在module a 的mutations中定义一个updateName方法

      // 定义一个模块a
      const a = {
          state: {
              name: 'kobe'
          },
          mutations: {
              // 这个state是模块a的state,并非全局的state
              updateName(state, payload) {
                  state.name = payload;
              }
          },
          actions: {},
          getters: {}
      }
      
    • 在Vue组件中的methods中定义一个updateName方法,操作mutations中的updateName方法

       updateName() {
                      this.$store.commit('updateName', '小明')
                  }
      
    • 结论: 跟操作全局的mutations方法一样的.

  • 示例3, 如何操作模块a中的getters方法

    • 先在模块a定义一个getters方法 say

          getters: {
              say(state) {
                 return  "i like " + state.name
              }
          }
      
    • Vue组件使用say方法

          <h2>{{$store.getters.say}}</h2>
      
    • 结论: 跟操作全局的getters方法一样.

  • 示例4, 操作全局的state

    • getters方法可以有3个入参:
      • 第1个: 本模块的state
      • 第2个: 本模块的getters
      • 第3个: 全局的state
        getters: {
            say(state) {
               return  "i like " + state.name
            },
            // state : 本模块的state, getters: 本模块的getters
            say2(state, getters) {
                return getters.say + ' en, but --------'
            },
            // 全局的state
            say3(state, getters, rootState) {
                return getters.say2 + rootState.students[0].name
            }
        }
    
  • 示例5, 如何操作模块a中的actions方法

    • 先定义一个actions方法

          mutations: {
              // 这个state是模块a的state,并非全局的state
              updateName(state, payload) {
                  state.name = payload;
              }
          },
          actions: {
              aUpdateName(context,param) {
                  setTimeout(()=>{
                      console.log(context);
                      // 操作本模块mutaions中的updateName方法
                      context.commit('updateName',param)
      
                  },1000)
              }
          },
      
    • Vue组件调用模块a中的actions方法

      asyncUpdateName() {
      	this.$store.dispatch('aUpdateName','jet')
      }
      
    • 结论 跟调用全局actions方法一样

    • 注意 context与全局的context对像不一样, 可以console.log打印, 可知,模块actions中context包含了rootGetters和rootState

5. 核心概念

  • State , 单一状态树 , 一个项目中只建一个store对象.

  • Getters , 相当于计算属性, 用于获取State中变异的数据.

  • Mutations , 操作state状态 , 同步方法. 如果是异步方法,devtools就会监控失效.

  • Action , 类似于Mulations一样,用来替代Mulations, 进行异步操作

  • Module , 模块. Vuex推荐使用单一state, 但是如果state中的数据太多,也可以按模块进行划分. 而每个模块里面又可以包含state,mutations, actions,getters

    const moduleA = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    const moduleB = {
      state: () => ({ ... }),
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: moduleA,
        b: moduleB
      }
    })
    
    store.state.a // -> moduleA 的状态
    store.state.b // -> moduleB 的状态
    

6. 目录结构

​ Vuex 并不限制你的代码结构。但是,它规定了一些需要遵守的规则:

  1. 应用层级的状态应该集中到单个 store 对象中。
  2. 提交 mutation 是更改状态的唯一方法,并且这个过程是同步的。
  3. 异步逻辑都应该封装到 action 里面。

只要你遵守以上规则,如何组织代码随你便。如果你的 store 文件太大,只需将 action、mutation 和 getter 分割到单独的文件。

├── index.html
├── main.js
├── api
│   └── ... # 抽取出API请求
├── components
│   ├── App.vue
│   └── ...
└── store
    ├── index.js          # 我们组装模块并导出 store 的地方
    ├── actions.js        # 根级别的 action
    ├── mutations.js      # 根级别的 mutation
    └── modules
        ├── cart.js       # 购物车模块
        └── products.js   # 产品模块

7.表单处理

  • 需求: input框是store的state获取数据显示 ,如果input框中的值发生改变,更新state中的值.

  • 解决方案: 使用计算属性

    <input v-model="message">
    
    computed: {
      message: {
        get () {
          return this.$store.state.obj.message
        },
        set (value) {  // 这个value就是input框中更新的值.
          this.$store.commit('updateMessage', value)
        }
      }
    }
    
    mutations: {
      updateMessage (state, message) {
        state.obj.message = message
      }
    }
    

    很赞呀!

posted on 2020-06-06 16:02  显示账号  阅读(104)  评论(0编辑  收藏  举报