首页 > 解决方案 > 设置在另一个模块中实现的抽象、实时可解析的 Vuex 存储?

问题描述

我有两个模块,我们称它们coreimplementation. 我如何设置商店以使事物core依赖于提供的假定商店implementation

implementation's store 我正在做这样的事情:

import Core from 'core'

export default new Vuex.Store(
    new Core().defaultStore
)

这将注册默认状态、突变、动作和 getter(设置允许用户implementation扩展/修改由 提供的默认存储core)。

core当尝试访问非 vue JS 文件中的 getter 时,内部操作会出现问题。

export default class SomeClassInCore {
    exampleMethod() {
        // The getters will not be accessible here!
        return store.getters.someKey
    }
}

有没有办法实现“主”商店的“运行时”解析?我在考虑是否可以以某种方式window访问​​由implementationrepo 创建的 Vue 实例?

我试着这样做

import store from './store.js'
import Vue from 'vue'
import { CoreVueTools } from 'core'

window.Vue = Vue
window.store = store

Vue.use(CoreVueTools)

new Vue({
    el: '#app',
    store
})

然后像这样访问它

exampleMethod() {
    return window.store.getters.someKey
}

那行得通,但是我对这个解决方案并不满意,必须有比依赖窗口更好的方法吗?我可以使用其他设计模式吗?

标签: javascriptvue.jsvuexes6-modules

解决方案


我不确定这是否朝着您正在寻找解决方案的方向发展(因为它不依赖于 vuex),如果不是,我希望它至少会有点有趣=)...

我可以使用其他设计模式吗?

您可以使用基于减数分裂模式( Homepage ) 的方法尝试一种非常简单(但还不太为人所知)的方式来处理状态管理。我最近用一个 react 和一个 riot.js 应用程序实现了这个模式,与 redux 和 co 相比,它用如此少的样板代码运行得非常好(加上除了作为流提供的东西之外没有依赖项,但你也可以自己实现它! )。我还没有机会用 vue 尝试它(在我的待办事项列表上),所以下面的代码只是从repo中的“设置”示例中快速(并且减少)盗版......

减数分裂模式的基本思想是依靠流来实现反应循环并将状态管理代码与视图代码分离。模式的快速总结(取自主页):

  • 从初始状态开始。
  • 创建补丁更新流。
  • 补丁可以是 Patchinko 补丁、功能补丁或您自己的补丁。
  • 创建向更新流发布补丁的函数的操作对象。
  • (x, f) => f(x)通过对具有初始状态的更新流和函数补丁使用扫描来创建状态流。
  • 将状态和动作传递给视图。

甚至可以在 SO 片段中使用的东西=):

(关键部分跳到这个答案的最后)

.row {
  padding-bottom: 0.5rem;
}

button {
  display: inline-block;
  padding: 0 1rem;
  color: #555;
  text-align: center;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.5rem;
  letter-spacing: 1px;
  text-transform: uppercase;
  text-decoration: none;
  white-space: nowrap;
  background-color: transparent;
  border-radius: 0.25rem;
  border: 1px solid #bbb;
  cursor: pointer;
  box-sizing: border-box;
}

button:hover,
button:focus{
  color: #333;
  border-color: #888;
  outline: 0;
}
<div id="app">
	<temp />
</div>

<script src="https://cdn.jsdelivr.net/npm/flyd/flyd.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.min.js"></script>
<script name="store">
	// for convenience, let's define an initial state
	const initialState = {
		temperature: {
			value: 22,
			unit: "C"
		}
	};
	
	// $update is a stream of patch functions
	// the patch function passed here is doing
	// nothing, it takes state and returns is,
	// just for consistency...
	const update = flyd.stream(state => state);
	
	// define another stream to accumulate patches
	// starting from the initial state
	const getState = flyd.scan(
		(state, patchFn) => patchFn(state),
		initialState,
		update,
	);
	
	// actions are functions that take an input
	// and pass an appropriate patch function
	// to the update stream
	const actions = {
		changeTemp: amount => update(state => ({
			...state,
			temperature: {
				...state.temperature,
				value: state.temperature.value + amount
			}
		})),
		changeTempUnit: () => update(state => ({
			...state,
			temperature: {
				...state.temperature,
				unit: state.temperature.unit === "C" ? "F" : "C",
				value: state.temperature.unit === "C"
					? Math.round((state.temperature.value * 9) / 5 + 32)
					: Math.round(((state.temperature.value - 32) / 9) * 5)
			}
		}))
	};
	
	// define global store by combining actions and getState
	const store = {
		getState,
		actions
	};
</script>

<script name="app">
	new Vue({
		el: "#app",
		components: {
			'temp': {
				data() {
					const initial = store.getState();
					return {
						temperature: initial.temperature.value,
						unit: initial.temperature.unit
					};
				},
				mounted() {
					store.getState.map(s => {
						this.temperature = s.temperature.value;
						this.unit = s.temperature.unit;
					});
				},
				methods: {
					incTemp() {
						store.actions.changeTemp(+1);
					},
					decTemp() {
						store.actions.changeTemp(-1);
					},
					changeUnit() {
						store.actions.changeTempUnit();
					}
				},
				props: [],
				template: `
					<div class="temp-component">
						<div class="row">
							<button @click="incTemp">+</button>&nbsp;
							<button @click="decTemp">&minus;</button>&nbsp;
							<span>The temperature is {{temperature}} &deg;{{unit}}</span>
						</div>
						<div class="row">
							<button @click="changeUnit">Change temperature unit</button>
						</div>
					</div>
				`
			}
		}
	});
</script>

该模式绝对独立于框架,因此无论您使用什么框架,商店等的设置都可以保持不变。vue.js 的关键部分是我们如何将 store 与 vue/vue-components 连接起来,例如:

data() {
    const initial = store.getState();
    return {
        temperature: initial.temperature.value,
        unit: initial.temperature.unit
    };
},
mounted() {
    store.getState.map(s => {
        this.temperature = s.temperature.value;
        this.unit = s.temperature.unit;
    });
},

首先,我们将部分全局状态数据(或整个全局状态)设置为data()组件函数的返回值。然后我们简单地使用getState流的 map 函数来更新组件状态。.map()是 getState 流的反应函数(此处由 flyd 提供)。

PS:还可以查看上面链接的示例仓库以获取更复杂的示例,包括todo-mvcrealworld(以及更多)...


推荐阅读