平时开发业务模块时候,经常遇见需要维护一些模块级别的全局状态等数据。这时候使用 Vuex 等全局状态管理工具,又显得太重( Vuex 管理需要经常变化的数据太麻烦)。这个时候创建一个模块级别的 Store 显得比较重要。可以避免组件间 props 多层传递造成变量重复定义等问题,又使得需要全局管理的状态数据代码从组件中分离出来,可读性更强。
代码实现
使用 Vue.extend 创建一个 Store
// store.js
const Store = Vue.extend({
data() {
return {
states: {
// 共享数据
}
};
},
methods: {
setData(){
// ...
}
}
});
export function createStore(instance, initialState = {}) {
if (!instance) {
throw new Error('instance is required.');
}
const store = new Store();
store.instance = instance;
Object.keys(initialState).forEach(key => {
store.states[key] = initialState[key];
});
return store;
}
// 利用 ES2015 Symbols、函数 `provide` 和对象 `inject`
export const STORE_INJECTION_KEY = Symbol('Store')
父组件中通过 createStore 创建 store 实例,并通过 provide 依赖注入到子/孙组件中
// parent.vue
<template>
<div></div>
</template>
<script>
import { createStore, STORE_INJECTION_KEY } from './store.js'
export default {
provide() {
return {
[STORE_INJECTION_KEY]: this.store,
}
},
data() {
const store = createStore(this, {
// 初始数据
});
return {
store
};
}
};
</script>
子/孙组件通过 inject 注入 store
// child.vue
<template>
<div></div>
</template>
<script>
import { STORE_INJECTION_KEY } from './store.js'
export default {
inject: {
store: STORE_INJECTION_KEY
},
data() {
return {};
},
created(){
console.log(this.store.states)
}
};
</script>
小技巧
一、组件间事件派发
// 组件A emit 一个change事件
this.store.$emit('change', data)
// 组件B listen A组件发出的change事件
this.store.$on('change', data=>{ ... })
二、双向数据绑定
因为注入的 store 是一个响应式对象,所以可以使用 v-model 实现双向绑定。
子组件应当避免直接给全局状态赋值,建议通过
setData函数修改全局状态
<input v-model="store.states.xxx" />