• vue学习之vuex


    父组件通过props传数据到子组件,子组件通过事件回传数据给父组件,那么几个不相关的组件想共享一个数据,就可以用到vuex。

    概念

    state

    存放要共享的数据。在组件中,state的属性没有直接放在组件的data部分,一般是在computed里,依赖于state属性的改变而更新页面数据。

    mutations

    定义操作state属性的方法,mutation 非常类似于事件,组件不能直接操作state的属性,需要通过mutations操作,注意:mutation 都是同步事务。

    触发 mutation 事件的方式不是直接调用,比如 increment(state) 是不行的,而要通过 store.commit 方法:

    store.commit('increment')

    actions

    通过actions里各方法的commit(‘mutations方法名’),与mutations里的方法关联起来,Vuex 加入了 Action 来处理异步,想法是把同步和异步拆分开,异步操作想咋搞咋搞,但是不要干扰了同步操作。

    Vuex 把同步和异步操作通过 mutation 和 Action 来分开处理,是一种方式。但不代表是唯一的方式,还有很多方式,比如就不用 Action,而是在应用内部调用异步请求,请求完毕直接 commit mutation,当然也可以。

    页面接收到用户的交互行为分发事件到actions的时候,执行对应的mutations里的方法改变state,,从而更新页面使用到的state数据部分

    Getter

    Vuex 还引入了 Getter,这个可有可无,只不过是方便计算属性的复用。

    实践

    大概过程就是:创建一个管理state的文件——导出vuex实例——根实例中注册实例——在组件中使用实例

    安装vuex:npm install vuex --save

    创建一个store.js文件

    import Vue from 'vue'
    import Vuex from 'vuex'
    
    Vue.use(Vuex)
    //声明state
    const state={
      count:1
    }
    //声明mutations,定义方法操作state的属性 const mutations
    ={ add(state){ state.count++ }, reduce(state){ state.count-- } } //声明actions,通过commit与mutations的方法关联起来 const actions={ onAdd:({commit})=>{//通过解构的形式,使用对应的commit方法 commit('add'); }, onReduce:({commit})=>{ commit('reduce'); } } //导出模块 export default new Vuex.Store({state,mutations,actions})

    在组件中使用state

    在main.js里引入模块

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import store from './store'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      store,
      components: { App },
      template: '<App/>'
    })

    创建一个使用state数据的组件count.vue

    <template lang="html">
      <div class="count">
        {{$store.state.count}}
        <button type="button" name="button" @click="onAdd"></button><!--onAdd:methods里的方法-->
        <button type="button" name="button" @click="onReduce"></button>
      </div>
    </template>
    <script>
      import {mapActions} from 'vuex'
    
        export default{
          methods:mapActions([
            'onAdd',//和store.js中action里的increment关联起来
            'onReduce'
          ])
        }
    </script>
    <style lang="css">
    </style>

    部分解说:

    1、在组件中分发action:可以使用 this.$store.dispatch('actions里的方法名称') 分发 action,或者使用 mapActions 辅助函数将组件的 methods 映射为 store.dispatch 调用(需要先在根节点注入 store

    上面代码中,事件使用的都是methods里的方法,直接写method的方法是没法和action关联上的,这里通过mapActions使它的onAdd和action里的onAdd关联了起来。、


     2、{{$store.state.count}}中$store来自于main里引入的store。

    前面提到过state的属性没有直接放在组件的data部分,一般是在computed里,每当 store.state.count 变化的时候, 都会重新求取计算属性,并且触发更新相关联的 DOM。

    要是对每个属性进行计算,下面这样的写法就不方便了

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

    使用mapState函数,mapState 函数返回的是一个对象

    当映射的计算属性的名称与 state 的子节点名称相同时,可以给 mapState 传一个字符串数组。
    computed: mapState([
      // 映射 this.count 为 store.state.count
      'count'
    ])

    或使用对象展开符混合mapSate的对象到computed里:

    computed: {
      localComputed () { /* ... */ },
      // 使用对象展开运算符将此对象混入到外部对象中
      ...mapState({
        // ...
      })
    }

    在App.vue中引入这个组件

    <template>
      <div id="app">
         <count/>
         <router-view></router-view>
      </div>
    </template>
    
    <script>
      import count from 'components/count.vue'
    export default {
      name: 'App',
      components:{
        count
      }
    }
    </script>

    state应用再更复杂的场景

    Vuex 单一状态树并不影响模块化,把 State 拆了,最后组合在一起就行。Vuex 引入了 Module 的概念,每个 Module 有自己的 state、mutation、action、getter,其实就是把一个大的 Store 拆开。

    比如a,b,c三个页面,a页面由多个组件构成,这些组件共享一个state。b页面也由多个组件构成,这些组件又共享一个state。此时如果只有一个state去管理a和b页面的所有state属性,可能会导致命名冲突或者操作失误,也不方便管理。这个时候就想要他们管理各自的state。

    创建一个store目录

    a和b.js里面声明各自的state,mutations,actions

    //a.js和b.js的代码
    const state = { money: 1
    //money:10 b的money初始值
    } const mutations = { add(state){ state.money++ }, reduce(state){ state.money-- } } const actions = { onAdd: ({commit})=> { commit('add'); }, onReduce: ({commit})=> { commit('reduce'); } } export default { namespaced:true,//开启命名空间 state, mutations, actions }

    在index.js中引入a和b再通过modules导出

    import Vue from 'vue'
    import Vuex from 'vuex'
    import moneya  from './modules/a'
    import moneyb from './modules/b'
    
    Vue.use(Vuex)
    
    export default new Vuex.Store({
     modules:{
       moneya,
       moneyb
     }
    })

    在main.js中引入index.js

    import Vue from 'vue'
    import App from './App'
    import router from './router'
    import store from './store/index'
    
    Vue.config.productionTip = false
    
    /* eslint-disable no-new */
    new Vue({
      el: '#app',
      router,
      store,
      components: { App },
      template: '<App/>'
    })

    在a.vue和b.vue中使用各自的state

    a.vue

    <template lang="html">
      <div>
        pagea:{{$store.state.moneya.money}}//$store.state和index.js中的modules对应
      </div>
    </template>
    <script>
        export default{}
    </script>
    <style lang="css">
    </style>

    b.vue

    <template lang="html">
      <div>
        pagea:{{$store.state.moneyb.money}}
      </div>
    </template>
    <script>
        export default{}
    </script>
    <style lang="css">
    </style>

    在App.vue中加载a和b组件

    <template>
      <div id="app">
         <pagea></pagea>
         <pageb></pageb>
    
      </div>
    </template>
    
    <script>
    
    import pagea from "./components/a.vue"
    import pageb from "./components/b.vue"
    export default {
      name: 'App',
      components:{
        pagea,
        pageb
      }
    }
    </script>

    结果如下:

    那么a和b组件在交互方面怎么样呢?

     以a.vue为例,增加两个按钮操作a.js里的money

    <template lang="html">
      <div>
        pagea:{{$store.state.moneya.money}}
         <button type="button" name="button" @click="onAdd">增加</button>
         <button type="button" name="button" @click="onReduce">减少</button>
      </div>
    </template>
    <script>
      import {mapActions} from 'vuex'
        export default{
          methods:mapActions('moneya',["onAdd","onReduce"])//注意这里的写法
        }
    </script>
    <style lang="css">
    </style>

    点击a组件的按钮,b的money不受影响

    补充

    给mutations传参

    从交互的地方触发action的时候传进来参数。以a.vue和a.js为例

    <template lang="html">
      <div>
        pagea:{{$store.state.moneya.money}}
         <button type="button" name="button" @click="onAdd(2)">增加</button>
         <button type="button" name="button" @click="onReduce">减少</button>
      </div>
    </template>
    <script>
      import {mapActions} from 'vuex'
        export default{
          methods:mapActions('moneya',["onAdd","onReduce"])
        }
    </script>

    a.js中

    const mutations = {
      add(state,params){
        console.log(params);
        state.money++
      },
      reduce(state){

    state.money
    -- } } const actions = { onAdd: ({commit},params)=> { commit('add',params);//接收参数 }, onReduce: ({commit})=> { commit('reduce'); } }

     结果,点击增加按钮,输出2

    关于store的使用

    不管是Vue,还是 React,都需要管理状态(state),比如组件之间都有共享状态的需要。

    什么是共享状态?比如一个组件需要使用另一个组件的状态,或者一个组件需要改变另一个组件的状态,都是共享状态。

    父子组件之间,兄弟组件之间共享状态,往往需要写很多没有必要的代码,比如把状态提升到父组件里,或者给兄弟组件写一个父组件。

     每一个 Vuex 里面有一个全局的 Store,包含着应用中的状态 State,这个 State 只是组件中共享的数据,不用放所有的 State,没必要。

    Vuex通过 store 选项,把 state 注入到了整个应用中,这样子组件能通过 this.$store 访问到 state 了。

    在软件开发里,有些通用的思想,比如隔离变化,约定优于配置等。

    隔离变化就是说做好抽象,把一些容易变化的地方找到共性,隔离出来,不要去影响其他的代码,对于状态管理的解决思路就是:把组件之间需要共享的状态抽取出来,遵循特定的约定,统一来管理,让状态的变化可以预测。根据这个思路,产生了很多的模式和库:

    比如:Vuex、Flux、Redux、Redux-saga、Dva、MobX

    约定优于配置就是很多东西我们不一定要写一大堆的配置,比如我们几个人约定,view 文件夹里只能放视图,不能放过滤器,过滤器必须放到 filter 文件夹里,那这就是一种约定,约定好之后,我们就不用写一大堆配置文件了,我们要找所有的视图,直接从 view 文件夹里找就行。

    关于修改store的state:

    需要规定一下,组件不允许直接修改属于 store 实例的 state,组件必须通过 action 来改变 state,也就是说,组件里面应该执行 action 来分发 (dispatch) 事件通知 store 去改变。

    因为没有限制组件里面不能修改 store 里面的 state,万一组件瞎胡修改,不通过 action,那我们也没法跟踪这些修改是怎么发生的。

    通过action 来分发 (dispatch) 事件通知 store 去改变我们能够记录所有 store 中发生的 state 改变,同时实现能做到记录变更 (mutation)、保存状态快照、历史回滚/时光旅行的先进的调试工具。

  • 相关阅读:
    Bookmarks_www2
    Linux系统各发行版镜像下载(持续更新)
    tiny-rtems-src
    rtems-os-source
    OpenRCT2-ext
    PAT甲级1004题解——并查集思想改
    PAT甲级1008水题飘过
    PAT甲级1007题解——贪心
    PAT甲级1006水题飘过
    PAT甲级1005水题飘过
  • 原文地址:https://www.cnblogs.com/yaoyao-sun/p/10361351.html
Copyright © 2020-2023  润新知