Vue - 用小白的学习方式去掌握 Vuex 使用

Vuex 是什么

Vuex是什么javascript

  • 一个专为 Vue.js 应用程序开发的响应式状态管理模式.

状态管理是什么?

从我我的角度来看,状态能够指代数据。而状态管理也就能够看做数据管理,主要是用于分层解耦。html

Vuex中状态管理能够看做是全局变量,这个变量能够经过state取出变量映射到view中,也能够根据用户的输入actions改变该变量vue

响应式是什么?

从我我的角度来看,响应式就是自适应。java

Vuex中响应式是指数据(状态)的改变可以及时的全部数据变动为最新的数据。git

不用 Vuex 也是能够的

  • 从父组件一层一层将数据传递给子组件(麻烦)
  • 经过 EventBus 的订阅/发布模式实现数据数据传递

可看这篇文章介绍:什么状况下我应该使用 Vuexgithub

使用

安装

npm 安装web

npm install vuex --save

知识储备

全局 store

参考:shoppint-cart 示例
store 目录下建立index.jsvuex

import Vue from 'vue'
import Vuex from 'vuex'
import cart from './modules/cart'
import products from './modules/products'
import createLogger from '../../../src/plugins/logger'

Vue.use(Vuex)

const debug = process.env.NODE_ENV !== 'production'

export default new Vuex.Store({ 
 
   
  // 注入其它模块的 store
  modules: { 
 
   
    cart,
    products
  },
  strict: debug,
  plugins: debug ? [createLogger()] : []
})

app.js中注入store,如:npm

import Vue from 'vue'
import App from './components/App.vue'
import store from './store'
import { 
 
    currency } from './currency'

Vue.filter('currency', currency)

new Vue({ 
 
   
  el: '#app',
  store, // 全局注入
  render: h => h(App)
})

如在counter组件中经过this.$store去使用缓存

this.$store.commit('increment')

State

用于声明数据。

computed 计算属性

对于任何复杂逻辑,你都应当使用计算属性。

例以下面的示例中,第二个 message ,若是咱们要在 view 中写也是能够,可是会比较复杂,以下:

<p>Computed reversed message: "{
  
  
   
   
            
   

  { message.split('').reverse().join('') }}"</p>

那么咱们能够将该复杂写法经过计算属性封装成一个方法,直接调用该方法对象便可。
示例:

<div id="example">
  <p>Original message: "{ 
 
   { message }}"</p>
  <p>Computed reversed message: "{ 
 
   { reversedMessage }}"</p>

  <!-- 在 Vuex 中若是不用计算属性,咱们须要这么写 -->
  <p>Computed reversed message: "{ 
 
   { this.$store.state.counter.message.split('').reverse().join('') }}"</p>

</div>

var vm = new Vue({ 
 
   
  el: '#example',
  data: { 
 
   
    message: 'Hello'
  },
  computed: { 
 
   
    // 计算属性的 getter
    reversedMessage: function () { 
 
   
      // `this` 指向 vm 实例
      // 使用 vuex 则可经过 $store 取到数据以后再转换,以下
      // return this.$store.state.counter.message.split('').reverse().join('')
      return this.message.split('').reverse().join('')
    }
  }
})

输出结果:

Original message: "Hello"

Computed reversed message: "olleH"

mapState 辅助函数

当一个组件须要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,咱们可使用 mapState 辅助函数帮助咱们生成计算属性。

辅助函数,用于简化this.$store.state,示例以下:

<template>
  <div>
    <!-- 直接使用 -->
    Clicked: { 
 
   { 
 
    $store.state.counter.count }} times.
    <br>
    <!-- 使用 Vuex mapState -->
    mapState使用: { 
 
   { 
 
    count }} times.
  </div>
</template>

<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { 
 
    mapState } from 'vuex'

export default { 
 
   

  computed:{ 
 
   
    ...mapState({ 
 
   
      count: state => state.counter.count
    }),
  },

  methods: { 
 
   
  }
}
</script>

有时候咱们须要从 store 中的 state 中派生出一些状态。
如上面的字符串反转功能就是派生出来的,咱们可使用Getters去实现。

Getters

能够认为是 store 的计算属性。getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被从新计算。

经过一个示例来看一下getters如何使用。
Counter.vue文件以下:

<template>
  <div>
    <p>hello 字符串反转:{ 
 
   { 
 
    reverseStr }}</p>
    <p>getter 获取 todo done: { 
 
   { 
 
    doneTodosCount }}</p>
    <p>传入 getters 作参数:{ 
 
   { 
 
    doneTodosCount }}</p>
    <p>直接访问:{ 
 
   { 
 
    doneTodos }}</p>
  </div>
</template>

<script>
// 在单独构建的版本中辅助函数为 Vuex.mapGetters
import { 
 
     mapGetters } from "vuex";

export default { 
 
   
  computed: { 
 
   
    // 因为使用了命名空间,所以须要加上,不然获取反转的字符串如: reverseStr:'counter/reverseStr'
    ...mapGetters('counter',{ 
 
   
      reverseStr: "reverseStr",
      doneTodosCount: "doneTodosCount",
      doneTodos: "doneTodos",
    }),
  },

};
</script>

counter.js文件以下:

const state = () => ({ 
 
   
    message: 'hello',
    todos: [{ 
 
   
            id: 1,
            text: '写日报',
            done: true
        },
        { 
 
   
            id: 2,
            text: '看一篇英文文章',
            done: false
        }
    ]
})

// getters
const getters = { 
 
   
    doneTodos: state => { 
 
    
        return state.todos.filter(todo => todo.done)
    },
    doneTodosCount: (state, getters) => { 
 
    // 传入 getters 作参数
        return getters.doneTodos.length
    },
    reverseStr: state => { 
 
   
        return state.message.split('').reverse().join('')
    }
}

export default { 
 
   
    namespaced: true,
    state,
    getters,
}

Mutations

经过提交 mutation 的方式,而非直接改变 store.state.count
其实也就是经过方法的方式去操做数据。并且Mutation 必须是同步函数,若是是异步函数请使用下一节的Action

看个示例:每次点击increment文本+2,每次点击decrement文本-2
Counter.vue文件以下

<template>
  <div>
     <p @click="mIncrement">mutations 获取 increment: { 
 
   { 
 
    mutationsCount }}</p>
     <p @click="mDecrement">mutations 获取 decrement: { 
 
   { 
 
    mutationsCount }}</p>
  </div>
</template>

<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { 
 
    mapState } from "vuex";

export default { 
 
   
  computed: { 
 
   
    ...mapState({ 
 
   
      count: (state) => state.counter.count,
      mutationsCount: (state) => state.counter.mutationsCount,
    }),
  },

  methods: { 
 
   
    mIncrement() { 
 
   
      this.$store.commit("counter/mIncrement",2);
    },
    mDecrement() { 
 
   
      this.$store.commit("counter/mDecrement",{ 
 
   
        amount:2
      });
    },

  },
};
</script>

counter.js以下:

const state = () => ({ 
 
   
    mutationsCount:0,
})
const mutations = { 
 
   

    mIncrement(state, n) { 
 
   
        state.mutationsCount += n
    },
    // 经过对象传递
    mDecrement(state,payload) { 
 
   
        state.mutationsCount -= payload.amount
    },
}

export default { 
 
   
    namespaced: true,
    state,
    mutations
}

Actions

Action 相似于 mutation,不一样在于:

  • Action 提交的是 mutation,而不是直接变动状态。
  • Action 能够包含任意异步操做。

counter计数器示例:
Counter.vue文件以下:

<template>
  <div>
    Clicked: { 
 
   { 
 
    $store.state.counter.count }} times.
    <br />
    <button @click="increment">+</button>
    <button @click="decrement">-</button>
   
  </div>
</template>

<script>
// 在单独构建的版本中辅助函数为 Vuex.mapState
import { 
 
    mapState, mapGetters } from "vuex";

export default { 
 
   
  computed: { 
 
   
    ...mapState({ 
 
   
      count: (state) => state.counter.count,
    }),
  },

  methods: { 
 
   

    increment() { 
 
   
      this.$store.dispatch("counter/increment");
    },
    decrement() { 
 
   
      this.$store.dispatch("counter/decrement");
    },
  },
};
</script>

counter.js文件以下:

const state = () => ({ 
 
   
    count: 0,
})
const mutations = { 
 
   
    increment(state) { 
 
   
        state.count++
    },
    decrement(state) { 
 
   
        state.count--
    },
}

const actions = { 
 
   
    increment: ({ 
 
   
        commit
    }) => commit('increment'),
    decrement: ({ 
 
   
        commit
    }) => commit('decrement'),
}

export default { 
 
   
    namespaced: true,
    state,
    actions,
    mutations
}

Modules

也就是分模块。
例如Counter组件,在 store/modules下建立counter.js用于处理store相关的数据,而Counter.vue组件页面就正常写便可。
接着在store/目录下的 index.js添加 module的注入。具体可看上文的store全局注入。

购物车示例

对照上面的分析,你能够很容易看懂Vuex examples 中的 shoppint-cart示例了
shoppint-cart 示例地址

该示例是经过shop.js模拟数据,而后将products 和 cart组件注入全局 store
而后ProductList.vue展现shop.js中的商品列表,而ShoppingCart.vue展现购物车数据。

END~

本文同步分享在 博客“_龙衣”(CSDN)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。

相关文章
相关标签/搜索