Vue - 用小白的学习方式去掌握 Vuex 使用
文章目录
Vuex 是什么
- 一个专为
Vue.js应用程序开发的响应式状态管理模式.
状态管理是什么?
从我个人角度来看,状态可以指代数据。而状态管理也就可以看作数据管理,主要是用于分层解耦。
在Vuex中状态管理可以看作是全局变量,这个变量可以通过state取出变量映射到view中,也可以根据用户的输入actions改变该变量
响应式是什么?
从我个人角度来看,响应式就是自适应。
在Vuex中响应式是指数据(状态)的改变能够及时的所有数据变更为最新的数据。
不用 Vuex 也是可以的
- 从父组件一层一层将数据传递给子组件(麻烦)
- 通过 EventBus 的订阅/发布模式实现数据数据传递
使用
安装
npm install vuex --save
知识储备
全局 store
参考:shoppint-cart 示例
在 store 目录下创建index.js
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,如:
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源创计划”,欢迎正在阅读的你也加入,一起分享。