干嘛的?
用于解决组件之间数据共享,数据的集中处理,是一种状态管理的设计模式。
什么是vuex ?单项数据流
- Vuex 是一个专为 Vue.js 应用程序开发的状态管理器,采用 集中式存储 管理所有组件的状态。
vuex的原理其实非常简单,它为什么能实现所有的组件共享同一份数据?
- 因为vuex生成了一个store实例,并且把这个实例挂在了所有的组件上,所有的组件引用的都是同一个store实例。
- store实例上有数据,有方法,方法改变的都是store实例上的数据。由于其他组件引用的是同样的实例,所以一个组件改变了store上的数据,导致另一个组件上的数据也会改变,就像是一个对象的引用。
基本使用
#安装
yarn add vuex
#引入 一般我们会在src根目录下创建一个store的文件夹,
#下面写index.js来实现相关的存储方法(组件共用数据)
import Vuex from 'vuex'
import Vue from 'vue'
Vue.use(Vuex)
vuex的组成部分(应用结构)
vuex的工作流程
1.通过this.$store.dispatch()向vuex中的actions派发一个事件同时传递一个参数
methods:{
handleClick(city){
/* this.$store.dispatch 向vuex派发一个事件,同时传递一个参数 */
this.$store.dispatch("changeCity",city)
}
}
2.vuex-actions中接收dispatch派发过来的事件和参数
export default new Vuex.Store({
...
actions: {
changeCity(ctx,city){
/* ctx表示上下文 this.$store
city是自定义事件传递过来的参数 */
3.在actions中使用commit方法向mutations提交一个事件,同时传递一个参数
ctx.commit("toggleCity",city)
}
}
})
4.在mutations中接收commit方法提交过来的事件,同时改变state中的状态
export default new Vuex.Store({
state: {
city:"武汉"
},
mutations: {
toggleCity(state,city){
state.city = city;
}
}
})
数据持久化 =>设置缓存机制
function getCity(){
let defaultCity = "天门";
if(localStorage.getItem("city")){
defaultCity = localStorage.getItem("city")
}
return defaultCity;
}
export default new Vuex.Store({
state: {
city:getCity()
},
mutations: {
changeCity(state,city){
state.city = city;
}
},
actions: {
changeCity(ctx,city){
ctx.commit("changeCity",city)
//1.设置缓存
localStorage.setItem("city",city)
}
}
})
State
State负责存储整个应用的状态数据,一般需要在使用的时候在跟节点注入store对象,后期就可以使用this.$store.state直接获取状态。一般是在main.js文件中引入store的文件,从而使用。(vue spa应用中)
//store为实例化生成的
import store from './store'
new Vue({
el: '#app',
store,
render: h => h(App)
})
这个store可以理解为一个容器,包含着应用中的state等。实例化生成store的过程是:
const mutations = {...};
const actions = {...};
const state = {...};
Vuex.Store({
state,
actions,
mutation
});
后续在组件中使用的过程中,如果想要获取对应的状态你就可以直接使用this.$store.state获取,当然,也可以利用vuex提供的mapState辅助函数将state映射到计算属性中去,如
mapState
state: {
count:0,
city:"武汉",
name:"港啊",
age:20,
sex:"男"
},
<template>
<div class="home">
<h2 >{{city}}</h2>
<h2 >{{age}}</h2>
</div>
</template>
<script>
import {mapState} from 'vuex';
export default {
name: 'home',
computed:{
...mapState(['city','name','age','sex'])
}
}
</script>
- 映射
Mutations
Mutations的中文意思是“变化”,利用它可以更改状态,本质就是用来处理数据的函数,其接收唯一参数值state。store.commit(mutationName)是用来触发一个mutation的方法。需要记住的是,定义的mutation必须是同步函数,否则devtool中的数据将可能出现问题,使状态改变变得难以跟踪。
| 直接派发一个事件给mutations
export default {
methods: {
handleClick() {
this.$store.commit('changeCity')
}
}
}
mutations:{
changeCity(state){
state.city="成都"
}
}
或者使用辅助函数mapMutations直接将触发函数映射到methods上,这样就能在元素事件绑定上直接使用了。如:
mapMutations
<template>
<div class="home">
<h2>{{this.$store.state.count}}</h2>
<button @click="myIncrease">增加</button>
<button @click="myDecrease">减少</button>
</div>
</template>
<script>
import {mapMutations} from 'vuex';
export default {
name: 'home',
methods:{
...mapMutations(['myIncrease','myDecrease'])
}
}
</script>
mutations: {
myIncrease(state){
state.count+=1
},
myDecrease(state){
state.count-=1
}
},
- 映射
Actions
Actions也可以用于改变状态,不过是通过触发mutation实现的,重要的是可以包含异步操作。其辅助函数是mapActions与mapMutations类似,也是绑定在组件的methods上的。如果选择直接触发的话,使用this.$store.dispatch(actionName)方法。action内部支持异步方法,这是很重要的一点区别。
mapActions
export default new Vuex.Store({
state: {
count:0
},
/* 异步操作,处理一些复杂的业务逻辑 */
actions: {
increase(ctx){
ctx.commit("myIncrease")
},
decrease(ctx){
ctx.commit("myDecrease")
}
},
/* 简单的更改状态(state)的逻辑*/
mutations: {
myIncrease(state){
state.count+=1;
},
myDecrease(state){
state.count-=1;
}
},
})
- 映射
```css
{{this.$store.state.count}}
作用:当store对象非常复杂的时候,我们可以根据将其拆分成不同的模块 ### 1. 新建modules文件夹,拆分module ![module.png](https://cdn.nlark.com/yuque/0/2019/png/402644/1574323726476-dd6e883b-6aff-479c-93cc-452955ffab4d.png#align=left&display=inline&height=380&name=module.png&originHeight=380&originWidth=563&size=22125&status=done&style=none&width=563) ```javascript //user.js const user = { state: { name:"程超" }, mutations:{}, actions: { }, getters: { } } export default user; ``` ```javascript //info.js const info = { state: { tips:12, news:15 }, mutations: { add(state){ state.tips++; }, reduce(state){ state.tips--; } }, actions: { }, getters: { } } export default info; ``` ```javascript //index.js export default new Vuex.Store({ modules:{ user, info } }) ``` ### 2. 使用数据 ```javascript
首页
{{tips}}
{{news}}
{{name}}
to explicit replace store state."
Error: [vuex] use store.replaceState() to
explicit replace store state. 报错如上,是因为修改state的时候,写成了this.state = data ,正确的写法应该是 this.state.xxx =data ; ### vuex与持久存储localStorage的关系 **场景** :发现部分开发在引入持久存储之后,mutation的时候没有改变state,而是直接修改的localStorage,这样会导致没有页面刷新时,拿到的state都是不对的。因为state不会实时监听localStorage的更改。
**解决方案** :
1 更改state的时候,除了更改localStorage,也要更改state,而且更改state才是必须要做的,而更改localStorage只是为了解决刷新页面后数据丢失的问题。(推荐方式,毕竟使用vuex的全套才是正宗)
2 如果你觉得上面的方案每次都写很麻烦,也可以尝试增加locastorage的监听机制,当发生变化的时候,寻找更改对应的state. ### 拓展符写法 在mapState,mapActions,mapMutations使用的时候,为了不影响正常方法的使用,我们可以使用对象以及数组的拓展方法进行拓展。 ``` methods:{ ...mapMutations([ 'add','reduce' ]), ...mapActions(['addAction','reduceAction']) }, // 获取state属性,你也可以采用这种写法 import {mapState} from 'vuex'; computed:mapState({ count:state=>state.count }) ``` ### 单页中直接定义使用(特别说明) ``` // 如果在模块化构建系统中,请确保在开头调用了 Vue.use(Vuex) const store = new Vuex.Store({ state: { count: 0 }, mutations: { increment (state) { state.count++ } } }) //使用的时候 如果想改变数值,必须用事件触发,不是直接改变。 store.commit('increment') console.log(store.state.count) // -> 1 ``` ## 个人理解 自我实践下来,如果要用好vuex,需要掌握以下的几点。
1 组件数据共享,跨页面数据共享,可以统一管理数据的存储,操作,分发。比如用户数据,比如固定的某些数据是固定某些api获取的且不止用于一个组件或者一个场景。
2 不要形式主义,每个页面都用vuex;每个模块都写getters,actions,常量方法,当你的工程量、数据量达到使用某技术场景的时候,采用某方案会觉得恰如其分
3 处理基于数据的业务逻辑,一般是跨页面跨组件的,比如购买流程对用户余额,购物车,订单的联动影响
4 vuex核心的index做一些模块公用的存储工具,可以配置一些需要的插件或者工具类
5 拓展:数据通讯不止vuex,简单的也可以用event bus,甚至页面内的已经能符合你的需求了
6 actions,支持异步是指更加建议在action中写异步,写复杂的逻辑,而所有的更改state建议写在mutations ## 使用vuex的核心概念 每一个 Vuex 应用的核心就是 store(仓库)。“store”基本上就是一个容器,它包含着你的应用中大部分的状态 (state)。 ### vuex的核心概念和核心概念图: 1、state - Vuex store实例的根状态对象,用于定义共享的状态变量。
2、Action -动作,向store发出调用通知,执行本地或者远端的某一个操作(可以理解为store的methods)
3、Mutations -修改器,它只用于修改state中定义的状态变量。
4、getter -读取器,外部程序通过它获取变量的具体值,或者在取值前做一些计算(可以认为是store的计算属性) ### Vuex的应用场景: Vuex主要用于:
1、多层嵌套的组件之间进行状态传递
2、兄弟组件间进行状态传递时(当然也可以使用中央事件总线BUS)
3、多组件共享状态时
更为具体的场景:组件之间的状态、音乐播放、登录状态、加入购物车...![288a0dc913bab3fe765baf18fb4bac27_701x551.png](https://cdn.nlark.com/yuque/0/2020/png/402644/1583220449909-28a05844-6698-470a-96fa-ac240fe36d0f.png#align=left&display=inline&height=551&name=288a0dc913bab3fe765baf18fb4bac27_701x551.png&originHeight=551&originWidth=701&size=8091&status=done&style=none&width=701) ### 代码: ```javascript //vuex的使用 #1.安装vuex npm i vuex -S #2.main.js import Vue from 'vue' import Vuex from 'vuex' // 注册vuex到vue中 Vue.use(Vuex) // new Vuex.Store()实例,得到一个数据仓储对象 // 可以在组件中通过this.$store.state.xx 来访问store中的数据 var store = new Vuex.Store({ //state相当于组件中的data state: { count: 0 }, //如果要修改store中state的值,需要调用 mutations提供的方法,可以通过this.$store.commit('方法名')来调用 mutations: { increment(state) { state.count++ }, //mutations函数参数列表中最多支持两个参数,其中参数1是state; 参数2是通过commit提交过来的参数; subtract(state, obj) { console.log(obj) state.count -= obj.step; } }, getters: { //这里的getters只负责对外提供数据,不负责修改数据,如果想要修改 state 中的数据需要在mutations中修改 optCount: function (state) { return '当前最新的count值是:' + state.count } } }) // 总结: // 1. state中的数据,不能直接修改,如果想要修改,必须通过 mutations // 2. 如果组件想要直接 从 state 上获取数据: 需要 this.$store.state.*** // 3. 如果组件想要修改数据,必须使用 mutations 提供的方法,需要通过 this.$store.commit('方法的名称', 唯一的一个参数) // 4. store中state上的数据在对外提供的时候建议做一层包装,推荐使用 getters。调用的时候则用this.$store.getters.*** import App from './App.vue' const vm = new Vue({ el: '#app', render: c => c(App), //将vuex创建的store挂载到VM实例上,只要挂载到了 vm 上,任何组件都能使用store来存取数据 store }) #3.index.html #4.App.vue
这是 App 组件
5.components/amount.vue
{{ $store.getters.optCount }}
6.components/counter.vue
```