vuex是vue中的状态管理器。可以使我们轻松解决在开发中遇到的多组件状态同步等问题, 同时通过store对vue的数据统一管理,可以使数据流向变得清晰、可追踪、可预测。对于一些中大型的应用,vuex的使用可以大大提升项目的稳定性和扩展性!
vuex的构成
vuex本质是一个对象,其中分别定义了state getters mutations actions 四个属性来管理vue中的数据。其中store和getters用于定义状态。使用 mutation 和 action对状态进行变更;引入module对状态进行模块化分割;引入插件对状态进行快照、记录、以及追踪等;提供了mapState、mapGetters、 mapActions、 mapMutations 辅助函数方便开发者在vm中处理store。
vuex的核心原理
- vuex暴露一个Store类和一个install方法。
- store注入 vue的实例组件的方式,是通过vue的 mixin机制,借助vue组件的生命周期 钩子 beforeCreate 完成的。即 每个vue组件实例化过程中,会在 beforeCreate 钩子前调用 vuexInit 方法
- 通过new Vue({ data: data }), 将store中的state包装成一个data。实现state中数据的响应式。
vuex简单实现
1.首先通过vue-cli 创建一个vue项目,不选择引入vuex。在项目的根目录创建文件夹myVuex。 新建store.js 里边包含两个方法,分别是State类和install方法。
// store.js
export let Vue;
import applyMixin from './mixin.js';
export class Store {
}
export const install = () => {
}
install方法中通过Vue.mixin() 方法将store全局混入。新建mixin.js,并且修改store.js如下
// mixin.js
export default function applyMixin (Vue) {
Vue.mixin({
beforeCreate: VuexInit
})
}
function VuexInit () {
const options = this.$options;
if (options.store) { // 只有根组件实例上绑定了store属性
this.$store = options.store;
} else if (options.parent && options.parent.$store) { // 子组件
this.$store = options.parent.$store;
}
}
// store.js
export const install = (_vue) => {
console.log('--------------------'); // 使用Vue.use会调用install方法
Vue = _vue;
applyMixin(Vue); // 通过mixin绑定
}
2.完成全局注入变量我们就可以确保在每一个vue的组件实例上都能拿到this.$store属性。然后我们来分别实现state、getters、mutations、actions四个属性。 首先我们先看下使用vuex时怎样定义store的
import Vue from 'vue';
import Vuex from '../vuex';
Vue.use(Vuex);
export default new Vuex.Store({
state: {
name: 'aaaaa',
age: 1,
},
getters: {
myName: state => state.name + 'sasdsds',
myAge: state => state.age,
},
mutations: {
add(state, payload) {
state.age += payload;
},
setName(state, payload) {
state.name = payload;
}
},
actions: {
add({ commit }, payload) {
console.log('commit', commit);
commit('add', payload);
}
}
})
也就是整个store中定义的状态都作为参数传入Store类中。Store类中接受一个参数options。所以接下来我们分别实现。
- state
定义一个state属性接收options中的state参数。然后通过初始化实例new Vue({ data: this.state}),将state传入vue实例的data中实现state中变量的响应式。同时我们给Store类添加一个state属性。这个属性自动触发get接口,类似Object.defineProperty({get()})。这样我们就可以通过this.$state.store拿到对应的store了。
// store.js
export class Store {
constructor(options) {
console.log(options);
// --------------------state--------------------------------------
// stare默认值是一个对象, 通过new Vue({ data: data }) 将数据绑定在
this.state = options.state || {};
this._vm = new Vue({
data: this.state,
})
}
get State() { // 属性访问器 new Store().state Object.defineProperty({get()})
console.log('get state');
return this._vm.data;
}
}
使用引入vuex的方式引入我们自己的Myvuex,在组件中通过this.$store.state打印可以看到结果:
这样,我们就成功实现了vuex中state。
- getters
我们通过遍历获取所有的getters方法,当我们获取getters的时候会通过Object.defineProperty() 方法获取get接口。这样我们才可以在使用getters的时候不加括号。
// store.js
export class Store {
constructor(options) {
// --------------------state--------------------------------------
// stare默认值是一个对象, 通过new Vue({ data: data }) 将数据绑定在
this.state = options.state || {};
this._vm = new Vue({
data: this.state,
})
// --------------------=getters--------------------------------------
let getters = options.getters || {}; // getters中可能会有多个方法
this.getters = {};
// 遍历整个对象,拿到每一个getter
Object.keys(getters).forEach((getter) => {
Object.defineProperty(this.getters, getter, {
// 当你要获取getter的时候,会自动调用get这个方法
//一定要用箭头函数,要不然this指向会出现问题
get: () => {
return getters[getter](this.state);
}
});
});
}
get State() { // 属性访问器 new Store().state Object.defineProperty({get()})
console.log('get state');
return this._vm.data;
}
}
- mutations mutations跟getters一样,还是用mutations对象将用户传入的mutations存储起来。但是mutations必须通过commit()方法去提交状态。所以,在Store中还需要增加一个comit方法。
// store.js
export class Store {
constructor(options) {
// --------------------state--------------------------------------
// stare默认值是一个对象, 通过new Vue({ data: data }) 将数据绑定在
this.state = options.state || {};
this._vm = new Vue({
data: this.state,
})
// --------------------=getters--------------------------------------
let getters = options.getters || {}; // getters中可能会有多个方法
this.getters = {};
// 遍历整个对象,拿到每一个getter
Object.keys(getters).forEach((getter) => {
Object.defineProperty(this.getters, getter, {
// 当你要获取getter的时候,会自动调用get这个方法
//一定要用箭头函数,要不然this指向会出现问题
get: () => {
return getters[getter](this.state);
}
});
});
// --------------------mutations--------------------------------------
let mutations = options.mutations || {};
this.mutations = {}
Object.keys(mutations).forEach((mutation) => {
this.mutations[mutation] = (payload) => {
// 传递上下文和负载
mutations[mutation](this.state, payload);
}
});
}
commit = (type, payload) => {
//把方法名和参数传给mutations
// 使用箭头函数,防止this指向拿不到。
this.mutations[type](payload);
}
get State() { // 属性访问器 new Store().state Object.defineProperty({get()})
console.log('get state');
return this._vm.data;
}
}
- actions 实现了mutations,那么actions就比较简单了。同样是保存传递进来的actions,同时添加一个dispatch()方法来提交action。
// store.js
export class Store {
constructor(options) {
// --------------------state--------------------------------------
// stare默认值是一个对象, 通过new Vue({ data: data }) 将数据绑定在
this.state = options.state || {};
this._vm = new Vue({
data: this.state,
})
// --------------------=getters--------------------------------------
let getters = options.getters || {}; // getters中可能会有多个方法
this.getters = {};
// 遍历整个对象,拿到每一个getter
Object.keys(getters).forEach((getter) => {
Object.defineProperty(this.getters, getter, {
// 当你要获取getter的时候,会自动调用get这个方法
//一定要用箭头函数,要不然this指向会出现问题
get: () => {
return getters[getter](this.state);
}
});
});
// --------------------mutations--------------------------------------
let mutations = options.mutations || {};
this.mutations = {}
Object.keys(mutations).forEach((mutation) => {
this.mutations[mutation] = (payload) => {
// 传递上下文和负载
mutations[mutation](this.state, payload);
}
});
// --------------------action--------------------------------------
let actions = options.actions || {};
console.log(actions, 'actions');
this.actions = {}
// 这里传递this到actions中,所以在actions中可以拿到commit() 方法和 state。
// 使用action的时候 ,其中的{ commit } 就是对this的解构
// add({ commit }, payload) {
// console.log('commit', commit);
// commit('add', payload);
// }
Object.keys(actions).forEach((action) => {
this.actions[action] = (payload) => {
actions[action](this, payload);
}
})
}
dispatch = (type, payload) => {
this.actions[type](payload);
}
commit = (type, payload) => {
//把方法名和参数传给mutations
// 使用箭头函数,防止this指向拿不到。
this.mutations[type](payload);
}
get State() { // 属性访问器 new Store().state Object.defineProperty({get()})
console.log('get state');
return this._vm.data;
}
}
结语
本文参考了网上一些大佬的博文,加上自己的理解。仅实现了vuex中基本的状态。但是通过自己手动实践,也对vuex的使用有了全新的了解。