为什么需要引入vuex
当我们构建简单的应用的时候,没有很多很复杂的页面通信的需求,通过data以及bus可以处理绝大多数的信息通信,这样就可以完成对中小型项目的状态管理,但如果我们需要管理一个大的项目(例如商城),我们需要考虑如何更好的进行状态管理,所以vuex就成为了自然而然的选择。
vuex几个要素
state
const CommonState: any = {
leftNodeTrees: [],
userInfo: {
img: '',
name: '',
misId: '',
},
};
getters
const G = {
leftNodeTrees(state) {
return state.leftNodeTrees;
},
userInfo(state) {
return state.userInfo;
},
};
actions
const A = {
// /** 普通调用的例子 */
// async testApi() {
// await api.getSchool()
// },
setLeftNodeTreeData({ commit }, params) {
commit('SET_TREE_DATA', params);
},
setUserInfo({ commit }: any, params) {
commit('SET_USER_INFO', params);
},
};
mutations
export const M = {
SET_TREE_DATA(localState, params) {
localState.leftNodeTrees = params;
},
SET_USER_INFO(localState, params) {
localState.userInfo = params;
},
};
高级用法 modules
const user = {
// 模块化并设置命名空间
namespaced:true,
state:{
userInfo:{
id:110,
username:'laoxie'
}
},
getters:{
isLogin(state){
return !!state.userInfo.id
}
},
mutations:{
login(state,payload){
state.userInfo = payload
},
logout(state){
state.userInfo = {}
}
},
actions:{
login(ctx,payload){
ajax('/login',payload).then(data=>{
ctx.commit('login',data)
})
}
}
}
const store = new Vuex({
modules:{
user
}
})
mapActions mapGetters mapState mapMutations
{
computed:{
// 把同名属性userInfo映射到computed中,以下两行代码等效
// userInfo(){return this.$store.state.userInfo}
...mapState(['userInfo']),
// 如需更改名字,则可使用对象形式(字符串或函数形式)
...mapState({user:'userInfo',currentUser:state=>state.userInfo})
// mapGetters采用对象参数时不支持函数写法
...mapGetters(['isLogin']),
...mapGetters({logined:'isLogin'}),
},
methods:{
// 以下两种写法等效于:logout(){this.$store.commit('logout')}
...mapMutations(['logout']),
...mapActions({
logout(commit,payload){
// commit固定为第一个参数
// payload为调用logout时传入的参数,不传则为undefined
commit('logout')
}
}),
// 以下两种写法等效于:login(user){this.$store.dispatch('login',user)}
...mapActions(['login']),
...mapActions({
login(dispatch,payload){
// dispatch固定为第一个参数
// payload为调用login时传入的参数,不传则为undefined
dispatch('login',payload)
}
})
}
}
实现一个简单的vuex
// pvuex.js
let Vue;
function install(Vue_){
Vue = Vue_;
// 混入一个beforeCreate生命周期函数,在Vue原型里注册$store属性
Vue.mixin({
beforeCreate() {
if(this.$options.store){
Vue.prototype.$store = this.$options.store;
}
},
})
}
class Store {
constructor(options){
// 作为在commit执行中的标识符
this._committing = false
// 保存用户的mutations、actions、getters的配置
this._mutations = options.mutations;
this._actions = options.actions;
this._getters = options.getters;
// 代表是严格模式下,不能直接给state里的状态值赋值,只可通过commit的方式提交修改
this.strict = options.strict;
// 定义配置在Vue里的computed,用Vue的computed特性实现getters机制
let computed = {};
// 暴露给外面使用的getters
this.getters = {};
// 把this保存一份
const store = this;
// 遍历每个_getters里的方法,封装一个高阶函数,目的是往遍历的方法里传递一个state
Object.keys(this._getters).forEach(key => {
const fn = this._getters[key];
computed[key] = function(){
return fn(store.state);
}
// 给store.getters的每个属性做一层访问代理,并封印getters里属性的setter
Object.defineProperty(store.getters, key, {
get(){
return store._vm[key];
},
set(val){
console.error('getters not set');
return;
}
});
});
// 借鸡生蛋,借助new Vue完成数据options.state的响应式
this._vm = new Vue({
data: {
$$state: options.state
},
computed
});
// 如果是严格模式,则定义个用户watch,在每个state里的值改变的时候调用assert方法
if(this.strict){
this._vm.$watch(function () { return this._data.$$state }, (v) => {
assert(store._committing, `do not mutate vuex store state outside mutation handlers.`)
}, { deep: true, sync: true });
}
// 绑定commit和dispatch函数中的this指向
this.commit = this.commit.bind(this);
this.dispatch = this.dispatch.bind(this);
}
// 通过存取器获取响应后的state
get state(){
return this._vm._data.$$state;
}
// 直接覆盖state,将抛出错误
set state(val){
throw new Error();
}
commit(type, payload){
const entry = this._mutations[type];
if(!entry){
console.error('unkown mutation type');
}
this._wrappedCommit(() => entry(this.state,payload))
}
dispatch(type, payload){
const entry = this._actions[type];
if(!entry){
console.error('unkown action type');
}
entry(this,payload);
}
// 封装commit执行
_wrappedCommit (fn) {
const committing = this._committing;
this._committing = true;
fn();
this._committing = committing;
}
}
// 不合法抛出错误
function assert (condition, msg) {
if (!condition) throw new Error(`[vuex] ${msg}`);
}
export default { Store , install}