一、Vuex是什么?
介绍:Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。
理解:核心就是 store(仓库),仓库是用来干什么的?你就当它用来储存东西的。
二、上方介绍提到的状态管理模式是什么?
首先我们先看一张图:

这个状态自管理应用包含以下几个部分:
- state,驱动应用的数据源;
- view,以声明方式将 state 映射到视图;
- actions,响应在 view 上的用户输入导致的状态变化。
new Vue({
// state
data () {
return {
count: 0
}
},
// view
template: `
<div>{{ count }}</div>
`,
// actions
methods: {
increment () {
this.count++
}
}
})
三、我们什么时候应该用到Vuex呢?
1.小应用不建议使用Vuex,因为小项目使用 Vuex 可能会比较繁琐冗余;
2.中大型单页应用,因为要考虑如何更好地在组件外部管理状态,Vuex 将会成为自然而然的选择;
四、对于使用Vuex的理解是什么?
由于Vue是单向数据流,子组件内部不能直接修改从父级传递过来的数据,子组件与子组件之间无法相互传递数据。如果我们想让两个子组件之间进行通信的话,可以借助子组件 A 向父组件传值,父组件接收子组件 A 的数据后再传给 B 组件这样的方式进行通信。
但是这样会有一个问题,就是如果子组件 A 的父组件上面还有一层爷爷组件,或者还有更多祖父类型的层级,那么是不是会很麻烦。
因此,我们会想到能不能我们将一个共有的数据存在一个特定的地方,用的时候自己去拿,这样就不需要一层层传值,于是乎 Vuex 就应运而生了。
五、核心概念
有五种属性,分别是 State、 Getter、Mutation 、Action、 Module。

State # 单一状态树
Vuex 使用单一状态树——是的,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。
Getter
Vuex 允许我们在 store 中定义“getter”(可以认为是 store 的计算属性)。就像计算属性一样,getter 的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
Mutation
更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。Vuex 中的 mutation 非常类似于事件:每个 mutation 都有一个字符串的 事件类型 (type) 和 一个 回调函数 (handler)。这个回调函数就是我们实际进行状态更改的地方,并且它会接受 state 作为第一个参数:
Action
Action 类似于 mutation,不同在于:
- Action 提交的是 mutation,而不是直接变更状态。
- Action 可以包含任意异步操作。
Module
由于使用单一状态树,应用的所有状态会集中到一个比较大的对象。当应用变得非常复杂时,store 对象就有可能变得相当臃肿。为了解决以上问题,Vuex 允许我们将 store 分割成模块(module)。每个模块拥有自己的 state、mutation、action、getter、甚至是嵌套子模块——从上至下进行同样方式的分割:
六、创建项目,开始实战
安装vuex:
npm install vuex --save
项目目录(具体处已用箭头标注):
目录我们可以看到有一个父组件HelloWorld和两个子组件JobList和JobTop。我们接下来就在这几个父子组件里面来介绍传值方法;
HelloWorld.vue
我们在父组件里面引入两个子组件,并呈现出数据,方便接下来我们继续传值操作:
<template>
<div>
<job-top :job="job"></job-top>
<job-list :jobs="jobs"></job-list>
</div>
</template>
<script>
import JobTop from './component/JobTop';
import JobList from './component/JobList';
export default {
name: 'HelloWorld',
props: {},
data () {
return {
job:'',
jobs:[]
}
},
components:{
JobTop,
JobList,
},
methods: {
getListInfo() {
//这里模拟的接口数据
this.getListInfoSucc({
"code":true,
"job":"web",
"data":{
"jobs":[
{
"id":1,
"name":"web"
},
{
"id":2,
"name":"C++"
},
{
"id":3,
"name":"python"
},
{
"id":4,
"name":"java"
}
]
}
})
},
getListInfoSucc(res){
if(res.code){
this.job=res.job;
this.jobs=res.data.jobs;
}
},
},
mounted() {
this.getListInfo();
},
}
</script>
<style scoped>
</style>
JobTop.vue
这里我们继续单个数据呈现,到时候方便显示切换后的数据:
<template>
<p>{{job}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
}
</script>
<style>
</style>
JobList.vue
这里是父组件里面通过接口获取的数组,进行遍历所呈现出列表形式,我们之后再进行点击传值
<template>
<div>
<button v-for="item of jobs" :key="item.id">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
}
</script>
<style>
</style>
页面呈现效果如下:

好,现在页面呈现出来了,我接着要写点击事件来进行传值,我们会想到三种传值方式:
1.父子组件传值
在 jobList.vue 中定义 jobClick 方法,将 changeJob 方法注入,并将所选中的 job 值传入
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//父组件传值
jobClick(name){
this.$emit("changeJob",name)
}
}
}
</script>
<style>
</style>
在父组件 HelloWorld.vue 中接收
<template>
<div>
<job-top :job="job"></job-top>
<job-list :jobs="jobs" @changeJob="ChangeJobClick"></job-list>
</div>
</template>
<script>
import JobTop from './component/JobTop';
import JobList from './component/JobList';
export default {
name: 'HelloWorld',
props: {},
data () {
return {
job:'',
jobs:[]
}
},
components:{
JobTop,
JobList,
},
methods: {
getListInfo() {
this.getListInfoSucc({
"code":true,
"job":"web",
"data":{
"jobs":[
{
"id":1,
"name":"web"
},
{
"id":2,
"name":"C++"
},
{
"id":3,
"name":"python"
},
{
"id":4,
"name":"java"
}
]
}
})
},
getListInfoSucc(res){
if(res.code){
this.job=res.job;
this.jobs=res.data.jobs;
}
},
ChangeJobClick(name){
console.log("选中的岗位",name);
this.job=name;
}
},
mounted() {
this.getListInfo();
},
}
</script>
<style scoped>
</style>
在父组件的的 job-list 标签中写上子组件 JobLict.vue注入的 changeJob方法,并指向ChangeJobClick 方法,ChangeJobClick 方法中接收传过来的选中的 job 值,并将该值传递给子组件 JobTop.vue,JobTop页面无需改动,页面效果如下:
2、原型链继承
正好之前一篇博客讲过 javascript 的原型链继承,vue 其实就是 js 的一种框架,它也有prototype 属性,我们叫做 Bus总线方法。如下:
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
import Vue from 'vue'
Vue.prototype.bus=new Vue();
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//原型链继承
jobClick(name){
console.log("点击某个岗位:",name)
this.bus.$emit("changeJob",name)
}
}
}
</script>
<style>
</style>
我们现在 Vue 的 prototype 属性中引入 bus ,然后在 button 按钮上绑定 jobClick 点击事件,并传入点击的 job ,然后在 bus总线上注入 changeJob 方法,并将点击的 job 值传入,我们再来看一下
JobTop.vue
<template>
<p>{{jobself}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
jobself:this.job
}
},
mounted () {
//原型链继承
var that=this;
this.bus.$on("changeJob",function(name){
console.log("点击后传过来的job",name)
that.jobself=name;
})
}
}
</script>
<style>
</style>
HelloWorld页面无需更改,最终效果图如下:
3、Vuex
接下来介绍今天的主角 Vuex ,Vuex 说白了就像是开辟了一个组件共有的内存空间,组件都可以去取去用;
我们在src创建 store 文件夹,里面添加一个 index.js 来存储共享数据,在 main.js 中引入 store文件,并在 Vue 实例中注入 store 。
main.js
import Vue from 'vue'
import App from './App'
import router from './router'
import Vuex from 'vuex'
import store from './store'
Vue.use(VueRouter)
Vue.use(Vuex)
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store,
components: { App },
template: '<App/>'
})
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//vuex
jobClick(name){
this.$store.dispatch("changeJob",name)
},
}
}
</script>
<style>
</style>
在 JobList.vue 中添加 jobClick 点击方法,根据上面说的流程将一个 changJob 事件通过 dispatch 方法注入,并将所选的 job 值传入。
仓库 store 包含了应用的数据(状态)和操作过程。 Vuex 里的数据都是响应式的,任何组件使用同一 store 的数据时,只要 store 的数据变化,对应的组件也会立即更新。
mutation 里不应该异步操作数据,所以有了 actions 选项 action与mutation很像,不同的是 action 里面提交的是 mutation ,并且可以异步操作业务逻辑。action 在组件 通过$store.dispatch 触发。
store文件夹下的index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
job:"web"
},
actions: {
changeJob(ctx,name){
console.log("action",ctx,name);
ctx.commit("changeJob",name);
}
},
mutations: {
changeJob(state,name){
console.log("mutation",state,name);
state.job=name;
}
}
})
JobTop.vue
获取到job的值,渲染出来
<template>
<p>{{this.$store.state.job}}</p>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
}
</script>
<style>
</style>
HelloWorld页面不需要改动,页面效果如下:

或者将store/index.js改为:
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
job:"web",
list: [10,18,30,45]
},
getters: {
filteredList: state =>{
return state.list.filter(item => item<30);
}
},
actions: {
// changeJob(ctx,name){
// console.log("action",ctx,name);
// ctx.commit("changeJob",name);
// },
// 获取
changeJob({state, commit}, params) {
console.log("....."+state.job) //没有commit时,是上一次的值
console.log("=====>"+params);
commit("changeJob", params);
console.log("-----"+state.job)
}
},
mutations: {
changeJob(state,name){
console.log("mutation",state,name);
state.job=name;
}
}
})
结果:
注意commit 前后对 state.job 的影响:点python,点java,再点java

4、重构
以上就是vue的几种组件传参方式,大家想了解的更加官方,请阅览官方地址:https://vuex.vuejs.org/zh/
根据官方 Vuex 目录,我们再深一步讲解一下核心概念,对上面的代码进行一下重构。
JobList.vue
<template>
<div>
<button v-for="item of jobs" :key="item.id" @click="jobClick(item.name)">{{item.name}}</button>
</div>
</template>
<script>
import { mapMutations } from 'vuex'
export default {
name:'JobList',
props:{
jobs:Array,
},
data(){
return{
}
},
methods:{
//vuex
jobClick(name){
this.changeJob(name)
},
...mapMutations(['changeJob'])
}
}
</script>
<style>
</style>
尤大大为我们提供了封装好的一些方法,如上面的 ...mapMutations() ,意思是 mutations 里有一个 changeJob ,我们将其映射到该组件的一个 changeJob的方法里,这样就可以直接调用 changeJob 方法并传值了
JobTop.vue
<template>
<p>{{this.job}}</p>
</template>
<script>
import { mapState } from 'vuex'
export default {
name:'JobTop',
props:{
},
data(){
return{
}
},
computed:{
...mapState(['job'])
},
}
</script>
<style>
</style>
在 mapState 里有一个 city 属性,将该属性映射到该组件的 city 属性里,
效果图如下:

5、getters用法
Vuex 定义了某个数据 list,它是一个数组,如果只想得到小于30 的数据,最容易想到的方法可能是在组件的计算属性里进行过滤。
JobTop.vue
<template>
<div>{{list}}</div>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
computed:{
list(){
return this.$store.state.list.filter(item => item<30)
}
}
}
</script>
<style>
</style>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
job:"web",
list: [10,18,30,45]
}
})
结果:
[ 10, 18 ]
这样写完全没有问题。但如果还有其他的组件也需要过滤后的数据时,就得 computed 的代码完全复制一份,而且需要修改过滤方法时,每个用到的组件都得修改,这明显不是我们期望的结果。如果能将 computed的方法也提取出来就方便多了, getters就是来做这件事的。
使用 getters 改写上面的示例:
JobTop.vue
<template>
<div>{{list}}</div>
</template>
<script>
export default {
name:'JobTop',
props:{
job:String,
},
data(){
return{
}
},
computed:{
list(){
return this.$store.getters.filteredList;
}
}
}
</script>
<style>
</style>
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
export default new Vuex.Store({
state: {
job:"web",
list: [10,18,30,45]
},
getters: {
filteredList: state =>{
return state.list.filter(item => item<30);
}
}
})
结果:
[ 10, 18 ]
6、modules用法
它用来将 store 分割到不同模块。当你的项目足够大时, store 里的 state、getters、utations、actions 会非常多,都放在 main.js 里显得不是很友好,使用 modules 可以把它们写到不同的文件中。每个 module 拥有自己的 state、getters、mutations、actions ,而且可以 多层嵌套。
比如下面的示例:
const moduleA = {
state: { . . . } ,
mutations: { . . . } ,
actions: { . . . ) ,
getters: { . . . )
}
const moduleB= {
state: { . . . } ,
mutations: { . . . } ,
actions: { . . . ) ,
getters: { . . . )
}
const store= new Vuex.Store ( {
modules : {
a: moduleA,
b: moduleB
}
})
store.state.a // moduleA 的状态
store.state.b // moduleB 的状态
store/index.js
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex);
const moduleA = {
state: {
job:"web",
list: [10,18,30,45]
},
getters: {
filteredList: state =>{
return state.list.filter(item => item<30);
}
},
}
export default new Vuex.Store({
modules : {
a: moduleA,
}
})
JobTop.vue
这里不需要修改:
computed:{
list(){
return this.$store.getters.filteredList;
}
}
参考:https://blog.youkuaiyun.com/weixin_44811417/article/details/90766421
本文深入解析Vue.js的专用状态管理工具Vuex,包括其核心概念、何时使用、以及实战应用示例。从简单的组件间通信到利用Vuex进行状态管理,文章详细介绍了Vuex的State、Getter、Mutation、Action和Module等关键特性。
846

被折叠的 条评论
为什么被折叠?



