在另一个模块中实现抽象、实时可解决的Vuex存储,如何设置?

11

我有两个模块,让我们称它们为coreimplementation。 我如何设置存储以使core中的内容依赖于由implementation提供的假设的存储?

implementation的存储中,我正在做这样的事情:

import Core from 'core'

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

这将注册默认的状态,变化,操作和获取器(设置允许implementation的用户扩展/修改由core提供的默认存储)。

问题出现在core内部的一个操作中,当它试图在非Vue JS文件中访问Getter时。

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

有没有一种方法可以实现“运行时”解析“主”存储?我在想是否可以使用 window 来访问由 implementation 存储库创建的 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
}

这个方法可以用,但我对此解决方案并不完全满意,一定有比依赖窗口更好的方法吧?我能使用其他设计模式吗?


3
你有没有考虑将商店传递给 exampleMethod(例如,从一个带有 this.$store 的 Vue 组件)? - tony19
2
假设 implementation 可以知道 core,但反之不行,让 core 导出一个静态回调方法,用于传递对在 implementation 实例化的 store 的引用 - 类似于 Core.setStore(storeInstance) - Richard Matsen
1个回答

3

我不确定这是否符合你寻求解决方案的方向(因为它不依赖于vuex),如果不是,我希望它至少能成为一个有趣的阅读材料 =)...

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

您可以尝试使用一种非常简单但尚未被广泛知晓的方式来处理状态管理,即基于Meiosis模式的方法(主页)。我最近已在react和riot.js应用程序中实现了此模式,并且与redux和co相比,它仅需要很少的样板代码(加上除提供流之外没有任何依赖项,但您也可以自己实现!)。我还没有机会在vue中尝试它(在我的待办事项清单上),因此以下代码只是从repo中的“setup”示例中快速(并缩减)摘录的...

Meiosis模式的基本思想是依靠流来实现响应式循环,并将状态管理代码与视图代码分离。以下是该模式的简要总结(摘自主页):

  • 从一个初始状态开始。
  • 创建一个补丁更新流。
  • 补丁可以是Patchinko补丁、函数补丁或您自己的补丁。
  • 通过在更新流上使用“scan”和初始状态以及(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()flyd提供的getState流的响应函数。 PS:还可以查看上面链接的示例存储库,以获取更复杂的示例,包括todo-mvcrealworld(以及更多)。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接