使用vuex 教程 【跟着官网走一遍】
Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
1. 在vue项目中安装vuex
npm install vuex --save
--save 添加到生产依赖中【packae.json文件的dependencies字段中】
2. 在项目的src文件中新建一个vuex文件夹,在文件夹中新建store.js文件
3. 在store.js文件中写入
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
// 方法一
const store = new Vuex.Store({
state: { // store中包含组件中的共享状态
count: 10
},
mutations: { // 改变状态的方法(暂且称作方法)[更改 Vuex 的 store 中的状态的唯一方法是提交 mutation]
add(state) {
state.count++
},
reduce(state) {
state.count--
},
}
})
export default store // 用export default 导出,让外部可以引用
// 方法二
// const state = {
// count: 10
// }
// const mutations = {
// add(state) {
// state.count++
// },
// reduce(state) {
// state.count--
// },
// }
// export default new Vuex.Store({
// state: state,
// mutations // 这里因为对象的key和value相同,可以省略【属性的简洁表示法】
// })
4. 在main.js中配置,配置好了以后,在整个项目中就能全局使用
// main.js
import Vue from 'vue' // 项目构建时自动生成的代码
import App from './App.vue' // 项目构建时自动生成的代码
import store from './vuex/store' // 【自己手动配置的代码】
Vue.config.productionTip = false // 项目构建时自动生成的代码
new Vue({ // 项目构建时自动生成的代码
render: h => h(App), // 项目构建时自动生成的代码
store, // 【自己手动配置的代码】【通过在根实例中注册 store 选项,该 store 实例会注入到根组件下的所有子组件中,且子组件能通过 this.$store 访问到】
}).$mount('#app') // 项目构建时自动生成的代码
5. State 项目中使用vuex
Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态,【这也意味着,每个应用将仅仅包含一个 store 实例】
这个是在项目自带的HelloWorld 组件中使用的
// HelloWorld
<template>
<div class="hello">
<h1>{{ $store.state.count }}</h1>
<h1>{{ count }}</h1>
<button @click="add()">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
computed: { // 通过计算属性获取
count () {
return this.$store.state.count // 【return this.$store.state.count这一句,一定要写this】
}
},
methods:{
add(){
this.$store.commit('add')
}
}
};
</script>
<style scoped>
</style>
效果图:
6.使用mapState 辅助函数
当一个组件需要获取多个状态的时候,将这些状态都声明为计算属性会有些重复和冗余。为了解决这个问题,我们可以使用 mapState 辅助函数帮助我们生成计算属性,让你少按几次键:
在项目中使用mapState
6.1 先修改一下store.js中state的数据【这一步不是必要的】
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
sun: 190 // 【新加一条测试数据】
},
})
export default store
6.2 在组件中使用mapState 辅助函数
- 第一种 箭头函数可使代码更简练
<template>
<div class="hello">
<h1>{{ count }}</h1>
<h1>{{ sun }}</h1>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState({
// 【这里一定要注意 逗号,】
// 第一种:箭头函数可使代码更简练
count() {
return this.$store.state.count;
},
// sun() {
// return this.$store.state.sun;
// },
// 等同于:
sun: (state) => state.sun,
})
};
</script>
<style scoped>
</style>
效果:
- 第二种 传字符串参数 'count' 等同于
state => state.count
<template>
<div class="hello">
<h1>{{ countX }}</h1> <!--【这里有修改】-->
<h1>{{ sun }}</h1>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState({
// 【这里一定要注意 逗号,】
// 第二种 传字符串参数 'count' 等同于 `state => state.count`
countX: "count", // key是计算属性名
sun: "sun",
}),
};
</script>
<style scoped>
</style>
- 第三种 为了能够使用
this获取局部状态,必须使用常规函数
<template>
<div class="hello">
<h1>{{ countX }}</h1>
<h1>{{ sun }}</h1>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
data() { // 【这里有修改】
return {
addNum: "8888",
};
},
computed: mapState({
// 【这里一定要注意 逗号,】
// 第三种 为了能够使用 `this` 获取局部状态,必须使用常规函数
countX(state) {
return state.count + this.addNum; // 加上data中的数据
},
sun(state) {
return state.sun + this.addNum; // 加上data中的数据
},
}),
};
</script>
<style scoped>
</style>
效果:
- 第四种 当映射的计算属性的名称与 state 的子节点名称相同时,我们也可以给 mapState 传一个字符串数组。
<template>
<div class="hello">
<h1>{{ count }}</h1> <!--【这里有修改】-->
<h1>{{ sun }}</h1>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState([ <!--【这里有修改 改为了数组 】-->
// 映射 this.count 为 store.state.count
"count",
"sun",
]),
};
</script>
<style scoped>
</style>
这里一定要保证计算机属性的名称与store.js文件中的state对象中的key相同
- 第五种 对象展开运算符
mapState 函数返回的是一个对象。我们如何将它与局部计算属性混合使用呢?通常,我们需要使用一个工具函数将多个对象合并为一个,以使我们可以将最终对象传给 computed 属性。但是自从有了对象展开运算符,我们可以极大地简化写法:
computed: {
localComputed () { /* ... */ },
// 使用对象展开运算符将此对象混入到外部对象中
...mapState({
// ...
})
}
7.Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
7.1 定义Getter
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
},
getters: { // 定义getters属性
count: function (state) {
return state.count += 100; // 每次count的值改变后,都会加100
}
},
mutations: {
add(state) {
state.count++;
},
reduce(state) {
state.count--;
}
}
})
export default store
7.2 使用Getter
<template>
<div class="hello">
<h1>{{ count }}</h1>
<button @click="add">+</button>
<button @click="reduce">-</button>
</div>
</template>
<script>
export default {
name: "HelloWorld",
computed: {
count() {
// 每次count 的值发生变化的时候,都会进行加100的操作。
return this.$store.getters.count;
},
},
methods: {
add() { // 执行store中的mutations里面add事件
this.$store.commit("add");
},
reduce() {
this.$store.commit("reduce");
},
},
};
</script>
<style scoped>
</style>
8.Mutation
- 更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
- vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。
- 这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
你不能直接调用一个 mutation对象的increment,需要这样:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: { //【添加两个修改count值的事件】
add(state) {
state.count ++;
},
reduce(state) {
state.count--;
}
}
})
export default store
// HelloWorld.vue
<template>
<div class="hello">
<h1>{{ count }}</h1>
<button @click="updata">+</button>
<button @click="$store.commit('reduce')">-</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState(['count']),
methods:{
updata(){
this.$store.commit('add')
}
}
};
</script>
<style scoped>
</style>
8.1 提交载荷(Payload)
你可以向 store.commit 传入额外的参数,即 mutation 的 载荷(payload)
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: {
add(state,n ){ // 【添加第二个参数】
state.count +=n;
},
reduce(state,n) { // 【添加第二个参数】
state.count-=n;
}
}
})
export default store
// HelloWorld.vue
<template>
<div class="hello">
<h1>{{ count }}</h1>
<button @click="updata">+</button>
<button @click="$store.commit('reduce',5)">-</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState(['count']),
methods:{
updata(){
this.$store.commit('add',10)
}
}
};
</script>
<style scoped>
</style>
在大多数情况下,载荷应该是一个对象,这样可以包含多个字段并且记录的 mutation 会更易读:
// store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: {
add(state,n ) { // 【添加第二个参数】
state.count +=n.addNum;
},
reduce(state,n) { // 【添加第二个参数】
state.count-=n.reduceNum;
}
}
})
export default store
// HelloWorld.vue
<template>
<div class="hello">
<h1>{{ count }}</h1>
<button @click="updata">+</button>
<button @click="$store.commit('reduce',{reduceNum:5})">-</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState(['count']),
methods:{
updata(){
this.$store.commit('add',{addNum:8})
}
}
};
</script>
<style scoped>
</style>
8.2 对象风格的提交方式
<template>
<div class="hello">
<h1>{{ count }}</h1>
<button @click="updata">+</button>
<!-- <button @click="$store.commit('reduce', {reduceNum:5})">-</button> -->
<button @click="$store.commit({type:'reduce', reduceNum:5})">-</button>
</div>
</template>
<script>
import { mapState } from "vuex";
export default {
name: "HelloWorld",
computed: mapState(['count']),
methods:{
updata(){
// this.$store.commit('add',{add:8})
this.$store.commit({
type:'add',
addNum:5
})
}
}
};
</script>
<style scoped>
</style>
8.3 Mutation 需遵守 Vue 的响应规则
既然 Vuex 的 store 中的状态是响应式的,那么当我们变更状态时,监视状态的 Vue 组件也会自动更新。这也意味着 Vuex 中的 mutation 也需要与使用 Vue 一样遵守一些注意事项:
- 最好提前在你的 store 中初始化好所有所需属性。
- 当需要在对象上添加新属性时,你应该: 【这里还有点懵】
8.4 使用常量替代 Mutation 事件类型
使用常量替代 mutation 事件类型在各种 Flux 实现中是很常见的模式。这样可以使 linter 之类的工具发挥作用,同时把这些常量放在单独的文件中可以让你的代码合作者对整个 app 包含的 mutation 一目了然:
// 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
}
}
})
用不用常量取决于你——在需要多人协作的大型项目中,这会很有帮助。但如果你不喜欢,你完全可以不这样做
8.5 Mutation 必须是同步函数
一条重要的原则就是要记住 mutation 必须是同步函数。为什么?请参考下面的例子:
8.6 在组件中提交 Mutation
你可以在组件中使用 this.$store.commit('xxx') 提交 mutation,或者使用 mapMutations 辅助函数将组件中的 methods 映射为 store.commit 调用(需要在根节点注入 store)
官网示例:
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment', // 将 `this.increment()` 映射为 `this.$store.commit('increment')`
// `mapMutations` 也支持载荷:
'incrementBy' // 将 `this.incrementBy(amount)` 映射为 `this.$store.commit('incrementBy', amount)`
]),
...mapMutations({
add: 'increment' // 将 `this.add()` 映射为 `this.$store.commit('increment')`
})
}
}
自己演示的dome
//store.js
import Vue from 'vue';
import Vuex from 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 10,
},
mutations: {
add(state) {
state.count++;
},
reduce(state) {
state.count--;
}
}
})
export default store
- 第一种 参数是数组
// HelloWorld.vue
<template>
<div class="hello">
<h1>{{ $store.state.count }}</h1>
<button @click="add">+</button>
<button @click="reduce">-</button>
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: "HelloWorld",
methods: {
// 第一种 参数是数组
...mapMutations(["add","reduce"]), // 使用mapMutations
// 等同于:
// add() { // 原来的形式
// this.$store.commit("add");
// },
// reduce() { // 原来的形式
// this.$store.commit("reduce");
// },
},
};
</script>
<style scoped>
</style>
- 第二种 参数是对象
// HelloWorld.vue
<template>
<div class="hello">
<h1>{{ $store.state.count }}</h1>
<button @click="addFun">+</button> <!-- 【这里修改为addFun】 -->
<button @click="reduceFun">-</button> <!-- 【这里修改为reduceFun】 -->
</div>
</template>
<script>
import { mapMutations } from "vuex";
export default {
name: "HelloWorld",
methods: {
// 第二种 参数是对象
...mapMutations({
addFun: "add", // 将 `this.addFun()` 映射为 `this.$store.commit('add')`
reduceFun: "reduce", // 将 `this.addFun()` 映射为 `this.$store.commit('add')`
}),
},
};
</script>
<style scoped>
</style>
【未完结】
扩展文章:Vuex 通俗版教程
作者: Yeaseon Blog:yeaseonzhang.github.io 原文链接 [已挂]
-
本文基本上是官方教程的盗版,用通俗易懂的文字讲解Vuex,也对原文内容有删减。
-
如果你对以上声明不介意,那么就可以继续看本文,希望对你有所帮助。
-
学习一个新技术,必须要清楚两个W,"What && Why"。
-
"XX 是什么?","为什么要使用 XX ,或者说 XX 有什么好处",最后才是"XX 怎么使用"。
1. Vuex是什么?
Vuex 类似 Redux 的状态管理器,用来管理Vue的所有组件状态。
2. 为什么使用Vuex?
当你打算开发大型单页应用(SPA),会出现多个视图组件依赖同一个状态,来自不同视图的行为需要变更同一个状态。
- 遇到以上情况时候,你就应该考虑使用Vuex了,它能把组件的共享状态抽取出来,当做一个全局单例模式进行管理。这样不管你在何处改变状态,都会通知使用该状态的组件做出相应修改。
下面讲解如何使用Vuex。
3.最简单的Vuex示例
本文就不涉及如何安装Vuex,直接通过代码讲解。
import Vue from 'vue';
import Vuex form 'vuex';
Vue.use(Vuex);
const store = new Vuex.Store({
state: {
count: 0
},
mutations: {
increment (state) {
state.count++
}
}
})
以上就是一个最简单的Vuex,每一个Vuex应用就是一个store,在store中包含组件中的共享状态==state==和改变状态的方法(暂且称作方法)mutations。
需要注意的是只能通过mutations改变store的state的状态,不能通过store.state.count = 5;直接更改(其实可以更改,不建议这么做,不通过mutations改变state,状态不会被同步)。
使用store.commit方法触发mutations改变state:
store.commit('increment');
console.log(store.state.count) // 1
一个简简单单的Vuex应用就实现了。
4.在Vue组件使用Vuex
如果希望Vuex状态更新,相应的Vue组件也得到更新,最简单的方法就是在Vue的computed(计算属性)获取state
// Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return store.state.count;
}
}
}
上面的例子是直接操作全局状态store.state.count,那么每个使用该Vuex的组件都要引入。为了解决这个,Vuex通过store选项,提供了一种机制将状态从根组件注入到每一个子组件中。
// 根组件
import Vue from 'vue';
import Vuex form 'vuex';
Vue.use(Vuex);
const app = new Vue({
el: '#app',
store,
components: {
Counter
},
template: `
<div class="app">
<counter></counter>
</div>
`
})
通过这种注入机制,就能在子组件Counter通过this.$store访问:
// Counter 组件
const Counter = {
template: `<div>{{ count }}</div>`,
computed: {
count () {
return this.$store.state.count
}
}
}
mapState函数
computed: {
count () {
return this.$store.state.count
}
}
这样通过count计算属性获取同名state.count属性,是不是显得太重复了,我们可以使用mapState函数简化这个过程。
import { mapState } from 'vuex';
export default {
computed: mapState ({
count: state => state.count,
countAlias: 'count', // 别名 `count` 等价于 state => state.count
})
}
还有更简单的使用方法:
computed: mapState([
// 映射 this.count 为 store.state.count
'count'
])
Getters对象
如果我们需要对state对象进行做处理计算,如下:
computed: {
doneTodosCount () {
return this.$store.state.todos.filter(todo => todo.done).length
}
}
如果多个组件都要进行这样的处理,那么就要在多个组件中复制该函数。这样是很没有效率的事情,当这个处理过程更改了,还有在多个组件中进行同样的更改,这就更加不易于维护。
Vuex中getters对象,可以方便我们在store中做集中的处理。Getters接受state作为第一个参数:
const store = new Vuex.Store({
state: {
todos: [
{ id: 1, text: '...', done: true },
{ id: 2, text: '...', done: false }
]
},
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
}
}
})
在Vue中通过store.getters对象调用。
computed: {
doneTodos () {
return this.$store.getters.doneTodos
}
}
Getter也可以接受其他getters作为第二个参数:
getters: {
doneTodos: state => {
return state.todos.filter(todo => todo.done)
},
doneTodosCount: (state, getters) => {
return getters.doneTodos.length
}
}
mapGetters辅助函数
与mapState类似,都能达到简化代码的效果。mapGetters辅助函数仅仅是将store中的getters映射到局部计算属性:
import { mapGetters } from 'vuex'
export default {
// ...
computed: {
// 使用对象展开运算符将 getters 混入 computed 对象中
...mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
}
}
上面也可以写作:
computed: mapGetters([
'doneTodosCount',
'anotherGetter',
// ...
])
所以在Vue的computed计算属性中会存在两种辅助函数:
import { mapState, mapGetters } form 'vuex';
export default {
// ...
computed: {
mapState({ ... }),
mapGetter({ ... })
}
}
Mutations
之前也说过了,更改Vuex的store中的状态的唯一方法就是mutations。
每一个mutation都有一个事件类型type和一个回调函数handler。
const store = new Vuex.Store({
state: {
count: 1
},
mutations: {
increment (state) {
// 变更状态
state.count++
}
}
})
调用mutation,需要通过store.commit方法调用mutation type:
store.commit('increment')
Payload 提交载荷
也可以向store.commit传入第二参数,也就是mutation的payload:
mutaion: {
increment (state, n) {
state.count += n;
}
}
store.commit('increment', 10);
单单传入一个n,可能并不能满足我们的业务需要,这时候我们可以选择传入一个payload对象:
mutation: {
increment (state, payload) {
state.totalPrice += payload.price + payload.count;
}
}
store.commit({
type: 'increment',
price: 10,
count: 8
})
mapMutations函数
不例外,mutations也有映射函数mapMutations,帮助我们简化代码,使用mapMutations辅助函数将组件中的methods映射为store.commit调用。
import { mapMutations } from 'vuex'
export default {
// ...
methods: {
...mapMutations([
'increment' // 映射 this.increment() 为 this.$store.commit('increment')
]),
...mapMutations({
add: 'increment' // 映射 this.add() 为 this.$store.commit('increment')
})
}
}
注 Mutations必须是同步函数。
如果我们需要异步操作,Mutations就不能满足我们需求了,这时候我们就需要Actions了。
Aciton
相信看完之前的Vuex的内容,你就已经入门了。那么Action就自己进行学习吧(Action有点复杂,我还需要时间消化)。
结语
上个月看Vuex还是一头雾水,现在看来Vuex也是很容易理解的。
学习一门新技术最重要的就是实践,单单看教程和demo是远远不够的。
前端路途漫漫,共勉。