定义
Vuex为Vue.js应用程序开发的状态管理模式,采用集中式存储管理所有组件的公共状态,并以相应的规则保证状态以一种可预测的方法发生变化。
原理
Vue组件接收交互行为,调用dispatch方法触发action相关处理,若页面状态需要改变,则调用commit方法提交mutation修改state,通过getters获取到state新值,响应数据或状态给Vue 组件,界面随之更新。
使用场景:
- 组件会被销毁
- 组件基于数据而创建
- 多对多事件--多处触发,影响多处
- 涉及非父子组件之间跨组件共享数据
- 多个组件依赖于同一状态时。
- 来自不同组件的行为需要变更同一状态。
Vuex 核心功能:
State(Vuex的数据仓库)
Mutations(只能在mutations中修改state的数据状态)使用store.commit方法更改state存储的状态。
Actions(异步改变共享数据)
Getters(获取state二次处理的数据)
getters是store的计算属性,对state的加工,是派生出来的数据。就像computed计算属性一样,getter返回的值会根据它的依赖被缓存起来,且只有当它的依赖值发生改变才会被重新计算。
Module Module是store分割的模块,每个模块拥有自己的state、getters、mutations、actions。
Vuex框架的运行流程图:
- Vue Components:Vue组件。HTML页面上,负责接收用户操作等交互行为,执行dispatch方法触发对应action进行回应。
- dispatch:操作行为触发方法,是唯一能执行action的方法。
- actions:操作行为处理模块。负责处理Vue - Components接收到的所有交互行为。包含同步/异步操作,支持多个同名方法,按照注册的顺序依次触发。向后台API请求的操作就在这个模块中进行,包括触发其他action以及提交mutation的操作。该模块提供了Promise的封装,以支持action的链式触发。
- commit:状态改变提交操作方法。对mutation进行提交,是唯一能执行mutation的方法。
- mutations:状态改变操作方法。是Vuex修改state的唯一推荐方法,其他修改方式在严格模式下将会报错。该方法只能进行同步操作,且方法名只能全局唯一。操作之中会有一些hook暴露出来,以进行state的监控等。
- state:页面状态管理容器对象。集中存储Vue components中data对象的零散数据,全局唯一,以进行统一的状态管理。页面显示所需的数据从该对象中进行读取,利用Vue的细粒度数据响应机制来进行高效的状态更新。
- getters:state对象读取方法。图中没有单独列出该模块,应该被包含在了render中,Vue Components通过该方法读取全局state对象。
Vuex中状态储存在哪里,怎么改变它?
存储在state中,改变Vuex中的状态的唯一途径就是显式地提交 (commit) mutation。
如何对mutation中的state改变状态
this.$store.dispatch()
dispatch: 含有异步操作,数据提交至 actions ,可用于向后台提交数据
- 存储 this.$store.dispatch('getlists',name)
- 取值 this.$store.getters.getlists
this.$store.commit()
commit: 同步操作,数据提交至 mutations ,可用于读取用户信息写到缓存里
- 存储 this.$store.commit('changeValue',name)
- 取值 this.$store.state.changeValue
Vuex中状态是对象时,使用时要注意什么?
因为对象是引用类型,复制后改变属性还是会影响原始数据,这样会改变state里面的状态,是不允许,所以先用深度克隆复制对象,再修改。
怎么在组件中批量使用Vuex的state状态?
使用mapState辅助函数, 利用对象展开运算符将state混入computed对象中
import {mapState} from 'vuex'
export default{
computed:{
...mapState(['price','number'])
}
}
vuex的store是如何挂载注入到组件中呢?
- 利用vue的插件机制,使用Vue.use(vuex)时,会调用vuex的install方法,装载vuex
- applyMixin方法使用vue混入机制,vue的生命周期beforeCreate钩子函数前混入vuexInit方法
vuex与全局变量的区别
-
vuex的状态存储是响应式的,当Vue组件从store中读取状态的时候,若store中的状态发生变化,那么相应的组件也会得到高效更新。
-
不能直接改变store的变化,改变store中状态的唯一途径是commit mutation。方便于跟踪每一个状态的变化。
如何使用vuex
- 开始使用vuex,新建一个 sotre文件夹,分开维护 actions mutations getters
- 在store/index.js文件中新建vuex 的store实例
import Vue from 'vue'
import Vuex from 'vuex'
import * as getters from './getters' // 导入响应的模块,*相当于引入了这个组件下所有导出的事例
import * as actions from './actions'
import * as mutations from './mutations'
Vue.use(Vuex)
// 首先声明一个需要全局维护的状态 state,比如 我这里举例的resturantName
const state = {
resturantName: '飞歌餐馆' // 默认值
// id: xxx 如果还有全局状态也可以在这里添加
// name:xxx
}
// 注册上面引入的各大模块
const store = new Vuex.Store({
state, // 共同维护的一个状态,state里面可以是很多个全局状态
getters, // 获取数据并渲染
actions, // 数据的异步操作
mutations // 处理数据的唯一途径,state的改变或赋值只能在这里
})
export default store // 导出store并在 main.js中引用注册。
- actions
// 给action注册事件处理函数。当这个函数被触发时候,将状态提交到mutations中处理
export function modifyAName({commit}, name) { // commit 提交;name即为点击后传递过来的参数,此时是 'A餐馆'
return commit ('modifyAName', name)
}
export function modifyBName({commit}, name) {
return commit ('modifyBName', name)
}
// ES6精简写法
// export const modifyAName = ({commit},name) => commit('modifyAName', name)
- mutations
// 提交 mutations是更改Vuex状态的唯一合法方法
export const modifyAName = (state, name) => { // A组件点击更改餐馆名称为 A餐馆
state.resturantName = name // 把方法传递过来的参数,赋值给state中的resturantName
}
export const modifyBName = (state, name) => { // B组件点击更改餐馆名称为 B餐馆
state.resturantName = name
}
- getters
// 获取最终的状态信息
export const resturantName = state => state.resturantName
- 在main.js中导入 store实例
import Vue from 'vue'
import App from './App'
import router from './router'
import store from './store'
Vue.config.productionTip = false
/* eslint-disable no-new */
new Vue({
el: '#app',
router,
store, // 这样就能全局使用vuex了
components: { App },
template: '<App/>'
})
额外补充
如何实现vuex与组件data之间的数据同步更新
- 使用computed属性去获取state中的数据
用 computed 去返回 state 中的数据,当 state 中的数据发生改变后,computed 会感知到,并重新获取 state 中的数据,并返回新的值。
<template>
<div>{{userInfo}}</div>
</template>
<script>
export default {
computed: {
userInfo(){
return this.$store.state.userInfo;
}
}
};
</script>
- 使用watch监听state中的数据
通过组件的 watch 属性,为 state 中的某一项数据添加一个监听,当数据发生改变的时候触发监听事件,在监听事件内部中去更改 data 中对应的数据,即可变相的让 data 中的数据去根据 state 中的数据发生改变而改变。
<template>
<div>{{userInfo}}</div>
</template>
<script>
export default {
data() {
return {
userInfo: this.$store.state.userInfo;
};
},
watch: {
"this.$store.state.userInfo"() {
this.userInfo = this.$store.getters.getUserInfo; // 使用getters来获取数据
}
}
};
</script>
Vuex的严格模式是什么,有什么作用,怎么开启?
在严格模式下,无论何时发生了状态变更且不是由mutation函数引起的,将会抛出错误。这能保证所有的状态变更都能被调试工具跟踪到。
在Vuex.Store 构造器选项中开启,如下
const store = new Vuex.Store({
strict:true,
})
手写vuex的原理
前提解析:
vue项目怎么引入vuex:
- 安装vuex,通过import vuex from 'vuex'
- 先 var store = new Vuex.Store({...}),再把store作为参数的一个属性值,new Vue({store})
- 通过Vue.use(vuex)使得每个组件都可以拥有store实例
class Store {
constructor(options){
this.vm = new Vue ({
data:{
state:optiosn.state
}
})
let getters = options.getter || {}
this.getters = {}
Object.keys(getters).forEach(getterName=>{
Object.defineProperty(this.getters,getterName,{
get:()=>{
return getters[getterName](this.state)
}
})
})
let mutations = options.mutations || {}
let mutations = {}
Object.keys(mutations).forEach(mutationName=>{
this.mutations[mutationName] = (arg)=>{
mutations[mutationName](this.state,arg)
}
})
let actions = options.actions
this.actions = {}
Object.keys(actions).forEach(actionName=>{
this.actions[actionName] = (arg)=>{
actions[actionName](this,arg)
}
})
dispatch(method,arg){
this.actions[method](arg)
}
// 修改代码
commit=(method,arg)=>{
console.log(method);
console.log(this.mutations);
this.mutations[method](arg)
}
get state(){
return this.vm.state
}
}
}
let install = function (){
Vue.mixin({
beforeCreate(){
if(this.$options && this.$options.store){
this.$store = this.$options.store
}else {
this.$store = this.$parent && this.$parent.$store
}
}
})
}
let Vuex = {
Store,
install
}
export default Vuex