zoukankan      html  css  js  c++  java
  • Vuex基本概念

    Vuex基本概念

    • State
    • Getter
    • Mutation
    • Action
    • Module

    简单的Store

    import Vue from 'vue';
    import Vuex from 'vuex';
    
    Vue.use(vuex);
    
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++;
        } 
      }
    });
    
    store.commit('increment');
    console.log(store.state.count);// 1
    
    

    常见流程

    Vue Component --dispatch--> Action

    Action --commit--> Mutations

    Mutations --mutate--> State

    State --render--> Vue Component

    State

    获取vuex中的状态方法

    计算属性:computed中返回某个状态,要获取Vuex那个状态,要在computed中定义

    由于在全局使用了Vue.use(Vuex),所有组件可以通过this.$store拿到Vuex的store

    const Counter = {
      template: `<div>{{count}}</div>`,
      computed: {
        count () {
          return this.$store.state.count
        }
      }
    }
    

    由于computed的属性是函数,那么在返回状态之前,还有更多操作的空间。

    mapState辅助函数

    import {mapState} from 'vuex'
    

    mapState辅助函数接收一个对象或者一个字符串数组,返回计算属性:computed

    //这个可以单独放在一个文件中,重复使用
    const vuexComputed = mapState({
      //使用函数一定要有返回值
    
      //箭头函数写法,第一参数是state
      count: state => state.count,
      
      //字符串形式 'count' 等同于 state => state.count
      countAlias: 'count',
      
      //可以使用'this'获取局部状态,使用常规函数,'this'会绑定为 computed的组件实例
      countPlusLocalState (state) {
        return state.count + this.localCount;
      }
    });
    
    

    然后将mapState生成的vuexComputed和Vue组件实例的computed合并

    使用对象展开运算符...

    {
      //...某个组件
      
      computed: {
        localComputed () {},
        //使用对象展开运算符将vuexComputed混入到外部对象中
        ...vuexComputed
      }
    }
    

    如果状态都只是单纯的显示,可以传一个字符串数组mapState

    const vuexComputed = mapState(['count']);
    

    完整的demo:

    //main.js
    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import Vuex from 'vuex'
    import axios from 'axios'
    
    Vue.use(Vuex)
    
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      }
    })
    
    store.commit('increment')
    console.log(store.state.count)
    
    Vue.config.productionTip = false
    Vue.prototype.$http = axios
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      store,
      router,
      components: { App },
      template: '<App/>'
    })
    

    main.js要使用Vue.use(Vuex)以及将store添加到全局new Vue({store})

    下面的所有子组件才可以拿到store

    <template>
      <div class="hello">
        <div>{{count}}</div>
        <div>{{countAlias}}</div>
        <div>{{countPlusLocalState}}</div>
      </div>
    </template>
    
    //Component HelloWorld
    import {mapState} from 'vuex'
    
    const vuexComputed = mapState({
      count: state => state.count,
      countAlias: 'count',
      countPlusLocalState (state) {
        return state.count + this.localCount
      }
    })
    
    export default {
      name: 'HelloWorld',
      data () {
        return {
          localCount: 2
        }
      },
      computed: {
        ...vuexComputed
      }
    }
    

    Getter

    Getter计算属性:computed功能一样,只不过,它是store的,也会缓存计算的值。

    组件中拿到getter

    Getter会暴露store.getters对象。

    const store = new Vuex.Store({
      state: {
        count: 0,
        arr: [1, 2, 3, 4, 5, 6]
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      getters: {
        get6: state => {
          return state.arr.filter(num => num === 6)
        }
      }
    })
    
    {
      //...某个组件
      computed: {
        get6 () {
          return this.$store.getters.get6
        }
      }
    }
    

    在定义getter时,有两个参数传入stategetters

    getters: {
      get6: state => state.arr.filter(num => num === 6),
      getNum: (state, getters) => {
        return getters.get6
      }
    }
    

    定义getter方法

    //要使用函数要封装2层函数以上,在组件中拿到getTodoById时,默认执行第一层
    getters: {
      getTodoById: state => id => state.todos.find(todo => todo.id === id)
    }
    
    store.getters.getTodoById(2)
    

    mapGetters
    功能:将store中的getter映射到局部的计算属性computed

    mapState类似,支持传入对象或者字符串数组,不过只能获取,不能重写,或者定义

    //store
    {
      state: {
        arr: [1, 2, 3, 4, 5, 6, 7, 8]
      },
      getters: {
        get6: state => {
          return state.arr.find(num => num === 6)
        },
        getNum: state => num => state.arr.find(n => n === num) || '错误num'
      }
    }
    
    <template>
      <div class='helloworld'>
        <div>{{get6}}</div>
        <div>{{getNum(4)}}</div>
      </div>
    </template>
    
    const vuexGetter = mapGetters(['get6', 'getNum']);
    
    //如果想定义别名使用对象的方式
    //const vuexGetter = mapGetters({getSix: 'get6', getNumber: 'getNum'})
    
    export default {
      name: 'HelloWorld',
      data () {
      },
      computed: {
        ...vuexGetter
      }
    }
    

    Mutation

    修改Vuexstore中的状态的唯一方式是commit mutation

    每个mutation都有一个字符串的事件类型(type)和一个回调函数(handler)

    const store = new Vuex.Store({
      state: {
        count: 1
      },
      mutations: {
        increment (state) {
          // 变更状态
          state.count++
        }
      }
    })
    
    //type: increment
    store.commit('increment')
    

    Payload

    mutations: {
      increment (state, n) {
        state.count += n
      }
    }
    store.commit('increment', 10)
    

    store.commit可以支持多参数模式,或者一个对象模式

    store.commit({
      type: 'increment',
      amount: 10
    })
    

    其中一个对象模式redux很像

    而且事件类型type也可以使用常量来替代

    // mutation-types.js
    export const SOME_MUTATION = 'SOME_MUTATION';
    
    // store.js
    import Vuex from 'vuex';
    import { SOME_MUTATION } from './mutation-types';
    
    const store = new Vuex.Store({
      state: { ... },
      mutations: {
        // 我们可以使用 ES2015 风格的计算属性命名功能来使用一个常量作为函数名
        [SOME_MUTATION] (state) {
          // mutate state
        }
      }
    })
    

    组件中提交mutation

    在组件中可以通过this.$store.commit('xxx')提交mutation

    还有就是使用mapMutations辅助函数,不过这次不是映射到计算属性,而是methods

    使用方式和mapGetters一模一样

    import { mapMutations } from 'vuex';
    
    //对象的方式可以重新命名,字符串数组是直接引用
    const vuexMutations = mapMutations(['increment']);
    
    export default {
    
      methods: {
        ...vuexMutations
      }
    }
    
    

    Mutation 和 Reduce

    Reduce的基本形式

    (state, Action) => newState || state
    

    转化为Mutation,那么

    const Action = {
      type: 'increment',
      amount: 10
    }
    
    const initState = {
      count: 0
    }
    
    export default (state = initState, Action) => {
      switch(Action.type){
        'increment':
          return Object.assign({},state,{count: Action.amount + state.count})
        default:
          return state
      }  
    }
    

    redux中改变状态:

    store.dispatch(Action) -> reduce(state, Action) -> newState -> render view

    mutation中改变状态:

    store.commit('increment', playload) -> mutations['increment'](state, playload) -> newState -> render view

    Action

    一般Action可以处理异步任务,而Mutation必须只能同步。

    在异步流程中,先异步获得所需的数据,然后将返回的数据组合成Action

    在生成Action之前,有个createAction函数

    //Redux 思维的Action
    const createAction = async (url) => {
      const resData = await asyncGetData(url)
      
      return {
        type: 'SOME_TYPE',
        resData  
      };
    };
    
    //Action 是对象
    const Action = createAction('some url')
    
    store.commit(Action)
    

    Vuex中的ActionRedux是有区别的

    Vuex中的Action充当着createAction的功能

    commit应当只在action中使用,在commit之前还有dispatch

    Redux中,dispatch分发的直接是action

    Vuex中,dispatch分发的是createAction或者mutation,之后再commit action

    Action 不同于 Mutation

    • Action提交的是mutation,而不是直接变更状态
    • Action可以包含任意异步操作
    const store = new Vuex.Store({
      state: {
        count: 0
      },
      mutations: {
        increment (state) {
          state.count++
        }
      },
      actions: {
        increment (context) {
          context.commit('increment')
        }
      }
    })
    

    Action函数接受一个与store实例具有相同方法和属性的context对象

    其中context不是store实例

    分发Action

    store.dispatch('increment')
    

    组件中分发Action
    组件中使用this.$store.dispatch('xxx')

    或者使用mapActions辅助函数将actions映射到methods

    组合Action
    dispatch函数在结束后返回一个Promise

    actions: {
      actionA ({ commit }) {
        return new Promise((resolve, reject) => {
          setTimeout(() => {
            commit('someMutation')
            resolve()
          }, 1000)
        })
      }
    }
    

    Module

    可以将store分割成模块Module

    每个模块拥有自己的State、Mutation、Action、Getter

    然后将所有模块组合成一个,就形成了一个状态树。

    //一般我们可以按页面或者功能模块来划分模块
    //大型项目可以按一个大模块划分
    //然后在大漠块中再按页面划分
    //如果还要更细,页面也可以切割成多个模块
    
    //页面A的状态
    const pageA = {
      state: { ... },
      mutations: { ... },
      actions: { ... },
      getters: { ... }
    }
    
    //页面B的状态
    const pageAB = {
      state: { ... },
      mutations: { ... },
      actions: { ... }
    }
    
    const store = new Vuex.Store({
      modules: {
        a: pageA,
        b: pageAB
      }
    })
    
    store.state.a // -> pageA 的状态
    store.state.b // -> pageAB 的状态
    

    模块的局部状态

    每个模块的state都是局部状态,模块中的gettermutation、传进来的state都是局部的

    action可以通过context.state拿到局部的状态,context.rootState拿到全局的

    不同于mutationgetter也可以拿到全局状态,getter的第三个参数rootState

    命名空间

    默认情况下,模块内部的action、mutation和getter是注册在全局命名空间的--多个模块对同一mutation或action作出响应

    多个模块对同一mutation或action作出响应,类似一个事件拥有多个处理程序(观察者模式)

    在模块中添加namespace: true可以避免添加到全局队列中

    添加命名空间后的getter : (state, getters, rootState, rootGetters) => {}

    添加命名空间后的action context : getters访问局部的 rootGetters访问全局的

    如果添加了命名空间,但是还是想暴露某个actiongetter为全局,使用root:true

    {
      actions: {
        someOtherAction ({dispatch}) {
          dispatch('someAction')
        }
      },
      modules: {
        foo: {
          namespaced: true,
    
          actions: {
            someAction: {
              root: true,
              handler (namespacedContext, payload) { ... } // -> 'someAction'
            }
          }
        }
      }
    }
    

    mapStatemapGettersmapActionsmapMutations绑定带命名空间的模块。

    computed: {
      ...mapState('some/nested/module', {
        a: state => state.a,
        b: state => state.b
      })
    },
    methods: {
      ...mapActions('some/nested/module', [
        'foo',
        'bar'
      ])
    }
    

    还可以使用createNamespacedHelpers来绑定命名空间值,类似bind(context)

    import { createNamespacedHelpers } from 'vuex';
    
    const { mapState, mapActions } = createNamespacedHelpers('some/nested/module');
    
    export default {
      computed: {
        // 在 `some/nested/module` 中查找
        ...mapState({
          a: state => state.a,
          b: state => state.b
        })
      },
      methods: {
        // 在 `some/nested/module` 中查找
        ...mapActions([
          'foo',
          'bar'
        ])
      }
    }
    

    模块动态注册

    // 注册模块 `myModule`
    store.registerModule('myModule', {
      // ...
    })
    // 注册嵌套模块 `nested/myModule`
    store.registerModule(['nested', 'myModule'], {
      // ...
    })
    
    //卸载'myModule',只能卸载动态装载的模块
     store.unregisterModule('myModule')
    

    应用

    如果是小型应用,可以按页面来分模块

    //pagea.js
    export default {
      namespace:true,
      state: {},
      getters: {},
      mutations: {},
      actions: {}
    }
    
    //pageb.js
    //pagec.js
    //paged.js
    //以上页面也是按pagea.js的方式
    

    然后用一个文件引进所有模块,且全部暴露export

    //modules.js
    import pageA from './pagea.js'
    import pageB from './pageb.js'
    import pageC from './pagec.js'
    import pageD from './paged.js'
    
    export default {
      pageA,
      pageB,
      pageC,
      pageD
    }
    
    

    最后在main.js引入

    //main.js
    import modules from './modules/modules'
    
    //将模块装进Store树中
    const store = new Vuex.Store({
      //全局
      state: {},
      getters: {},
      mutations: {},
      actions: {},
      //模块
      modules
    }) 
    
    //在组件中可以
    this.$store.state.pageA
    this.$store.state.pageB
    this.$store.state.pageC
    this.$store.state.pageD
    
  • 相关阅读:
    Python解析excel文件并存入sqlite数据库
    python简单爬虫
    Python开发简单记事本
    Django初体验——搭建简易blog
    LeetCode — (1)
    机器学习(九)—FP-growth算法
    机器学习(八)—Apriori算法
    linux下debug工具
    linux同步与通信
    后台开发之IO缓冲区管理
  • 原文地址:https://www.cnblogs.com/lantuoxie/p/8761742.html
Copyright © 2011-2022 走看看