什么是Vuex
Vuex是一个专门为Vue.js应用程序开发的状态管理模式, 它采用集中式存储管理所有组件的公共状态, 并以相应的规则保证状态以一种可预测的方式发生变化.
安装vuex
npm install vuex --save
与模块系统一起使用时,必须通过'Vue.use()以下方式显示安装Vuex,在main.js添加
import Vuex from 'vuex'
Vue.use( Vuex );
const store = new Vuex.Store({
//待添加
})
new Vue({
el: "#app",
store,
render: h => h(App)
});
以下通过demo的示例来了解下Vuex的五大核心
这个demo分别有两个组件
productListOne.vue和productListTwo.vue,在App.vue中保存着共有的商品列表
//App.vue中的初始化代码
<template>
<div id="app">
<product-list-one v-bind:products="products"></product-list-one>
<product-list-two v-bind:products="products"></product-list-two>
</div>
</template>
<script>
import productListOne from "./views/productListOne";
import productListTwo from "./views/productListTwo";
export default {
name: 'app',
components: {
"product-list-one": productListOne,
"product-list-two": productListTwo
},
data () {
return {
products: [
{name: '鼠标', price: 20},
{name: '键盘', price: 40},
{name: '耳机', price: 60},
{name: '显示屏', price: 80}
]
}
}
}
</script>
<style>
body{
font-family: Ubuntu;
color: #555;
}
</style>
//productListOne.vue
<template>
<div id="product-list-one">
<h2>Product List One</h2>
<ul>
<li v-for="product in products">
<span class="name">{{ product.name }}</span>
<span class="price">${{ product.price }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["products"],
data() {
return {
};
}
};
</script>
<style scoped>
#product-list-one {
background: #fff8b1;
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.2);
margin-bottom: 30px;
padding: 10px 20px;
}
#product-list-one ul {
padding: 0;
}
#product-list-one li {
display: inline-block;
margin-right: 10px;
margin-top: 10px;
padding: 20px;
background: rgba(255, 255, 255, 0.7);
}
.price {
font-weight: bold;
color: #e8800c;
}
</style>
//productListTwo.vue
<template>
<div id="product-list-two">
<h2>Product List Two</h2>
<ul>
<li v-for="product in products">
<span class="name">{{ product.name }}</span>
<span class="price">${{ product.price }}</span>
</li>
</ul>
</div>
</template>
<script>
export default {
props: ["products"],
data() {
return {
};
}
};
</script>
<style scoped>
#product-list-two {
background: #d1e4ff;
box-shadow: 1px 2px 3px rgba(0, 0, 0, 0.2);
margin-bottom: 30px;
padding: 10px 20px;
}
#product-list-two ul {
padding: 0;
list-style-type: none;
}
#product-list-two li {
margin-right: 10px;
margin-top: 10px;
padding: 20px;
background: rgba(255, 255, 255, 0.7);
}
.price {
font-weight: bold;
color: #860ce8;
display: block;
}
</style>
核心概念1:State
state就是Vuex中的公共的状态,用于保存所有组件的公共数据
此时我们就把App.vue中的两个组件共同使用的data抽离出来,放到state中
//main.js
const store = new Vuex.Store({
state:{
products: [
{ name: "鼠标", price: 20 },
{ name: "键盘", price: 40 },
{ name: "耳机", price: 60 },
{ name: "显示屏", price: 80 }
]
}
})
productListOne.vue和productListTwo.vue做相应的更改
//productListOne.vue
export default {
data() {
return {
products: this.$store.state.products //获取store中state的数据
};
}
};
//productListTwo.vue
export default {
data() {
return {
products: this.$store.state.products //获取store中state的数据
};
}
};
页面不会发生改变
核心概念2:Getters
将
getters属性理解为所有组件的computed属性,也就是计算属性,getters的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算
我们在main.js中添加一个getters属性,其中的saleProducts对象将state中的价格减少一半(除以2)
//main.js
const store = new Vuex.Store({
state:{
products: [
{ name: "鼠标", price: 20 },
{ name: "键盘", price: 40 },
{ name: "耳机", price: 60 },
{ name: "显示屏", price: 80 }
]
},
getters:{
saleProducts:(state)=>{
let saleProducts = state.products.map( product => {
return{
name: product.name,
price: product.price / 2
}
})
return saleProducts;
}
}
})
将productListOne.vue中的products的值更换为this.$store.getters.saleProducts
export default {
data() {
return {
products: this.$store.getters.saleProducts
};
}
};
可以看到每项商品的价格都减少了一半
核心概念3:Mutations
将
mutations理解为store中的methods,mutations对象中保存着更改数据的回调函数,该函数名官方规定叫type,第一个参数是state,第二个参数是payload,也就是自定义的参数
我们在main.js中添加mutations属性,其中minusPrice这个回调函数用于将商品的价格减少payload这么多
//main.js
const store = new Vuex.Store({
state:{
products: [
{ name: "鼠标", price: 20 },
{ name: "键盘", price: 40 },
{ name: "耳机", price: 60 },
{ name: "显示屏", price: 80 }
]
},
getters:{
saleProducts:(state)=>{
let saleProducts = state.products.map( product => {
return{
name: product.name,
price: product.price / 2
}
})
return saleProducts;
}
},
mutations:{
minusPrice (state, payload ){
let newPrice = state.products.forEach( product => {
product.price -= payload //product.price = product.price - payload
})
}
}
})
在productListTwo.vue中添加一个按钮,为其添加一个点击事件, 给点击事件触发minusPrice方法
<template>
<div id="product-list-two">
<h2>Product List Two</h2>
<ul>
<li v-for="product in products">
<span class="name">{{ product.name }}</span>
<span class="price">${{ product.price }}</span>
</li>
<button @click="minusPrice">减少价格</button>
</ul>
</div>
</template>
在productListTwo.vue中注册minusPrice方法, 在该方法中commitmutations中的minusPrice这个回调函数
注意:调用mutaions中回调函数, 只能使用store.commit(type, payload)
export default {
data() {
return {
products: this.$store.state.products //获取store中state的数据
};
},
methods: {
minusPrice() {
this.$store.commit('minusPrice', 2); //提交`minusPrice,payload为2
}
}
};
可以发现每触发一次价格相对应的减少了2
核心概念4:Actions
actions类似于mutations,不同在于:
actions提交的是mutations而不是直接变更状态actions中可以包含异步操作,mutations中绝对不允许出现异步actions中的回调函数的第一个参数是context, 是一个与store实例具有相同属性和方法的对象
我们在store中添加actions属性,其中minusPriceAsync采用setTimeout来模拟异步操作,延迟2s执行,该方法用于异步改变我们刚才在mutations中定义的minusPrice
//main.js
const store = new Vuex.Store({
state:{
products: [
{ name: "鼠标", price: 20 },
{ name: "键盘", price: 40 },
{ name: "耳机", price: 60 },
{ name: "显示屏", price: 80 }
]
},
getters:{
saleProducts:(state)=>{
let saleProducts = state.products.map( product => {
return{
name: product.name,
price: product.price / 2
}
})
return saleProducts;
}
},
mutations:{
minusPrice (state, payload ){
let newPrice = state.products.forEach( product => {
product.price -= payload //product.price = product.price - payload
})
}
},
actions:{
minusPriceAsync(context, payload){
setTimeout( () => {
context.commit( 'minusPrice', payload );
},2000)
}
}
})
在productListTwo.vue中添加一个按钮,为其添加一个点击事件, 给点击事件触发minusPriceAsync方法
<template>
<div id="product-list-two">
<h2>Product List Two</h2>
<ul>
<li v-for="product in products">
<span class="name">{{ product.name }}</span>
<span class="price">${{ product.price }}</span>
</li>
<button @click="minusPrice">减少价格</button>
<button @click="minusPriceAsync">异步减少价格</button>
</ul>
</div>
</template>
在productListTwo.vue中注册minusPriceAsync方法, 在该方法中dispatchactions中的minusPriceAsync这个回调函数
export default {
data() {
return {
products: this.$store.state.products //获取store中state的数据
};
},
methods: {
minusPrice() {
this.$store.commit('minusPrice', 2); //提交`minusPrice,payload为2
},
minusPriceAsync() {
this.$store.dispatch('minusPriceAsync', 5);
}
}
};
可以看到触发后价格延迟了2s后减少了5
核心概念5:Modules
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割
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 的状态