对于许多相同组件之间的兄弟通信,我应该如何将数据存储在最低公共祖先中?

For sibling communication between many identical components, how should I store the data in the lowest-common ancestor?

背景:

问题:

我的问题:

处理这种情况的最佳做法是什么?我应该将成分数据移动到 Vuex 存储中还是(以同样的方式)最小共同祖先 WeeklyMenu.vue 组件?如果是这样,它究竟应该如何工作?每顿饭都应该有一个单独的变量吗?或者我应该有一个包含所有不同膳食数据的对象吗?如果我使用单个对象,我是否需要担心 Meal.vue 组件中该对象的观察者即使在对不同膳食的数据进行更改时也会被触发?

如果我将所有膳食成分存储在单独的变量中,我将需要将所有这些传递给每顿饭(因此每顿饭都需要接收其他每顿饭的成分作为单独的道具)。所以这似乎不是正确的方法。

如果用户对特定餐点进行特定更改,我将如何只让其他同名餐点做出反应?

相关链接:

我要处理的情况的简化示例:

  1. 是的,问题领域似乎足够复杂,足以证明使用 Vuex 是合理的。我不会将数据保存在组件中并通过 props 共享——这不能很好地扩展
  2. 将每个 Recipe 作为一个对象保存在单个对象中 recipes - 您无需担心观察者。如果一个特定的 Recipe 对象发生变化,Vue 将 re-render 仅使用相同 Recipe 对象的组件(如果做得好,你甚至不需要观察者)
  3. 在商店内创建一个“每周菜单”对象
  4. 在该对象的叶节点(膳食)中,只需使用某种参考(通过名称或唯一 ID,如果有的话)到食谱中。因此,菜单上的多个 Meal.vue 组件将使用商店中的相同对象并自动更新

我最终在 CodePen 中的一个简单示例中让它工作,当我试图让它在实际站点上工作时,我将使用它作为指南。

我对这个解决方案的总结是,“当 Vuex 状态对象的嵌套条目更新时,Vue 实际上更新;你不需要担心它没有检测到这些变化。所以当你有许多重复的兄弟组件需要相互反应时,将所有数据保存在一个大的 Vuex 存储对象中是可以的。"

这是 CodePen:https://codepen.io/NathanWailes/pen/NWRNgNz

截图

CodePen 示例的作用总结

  • 用于填充菜单的数据全部保存在 Vuex 存储中的单个 weeklyMenu 对象中,该对象具有子对象以将数据分解为不同的日期/膳食。
  • 单餐已使用 getset 函数计算属性,因此它既可以从商店获取更改,也可以更新商店。
  • DailyMenu 和 WeeklyMenu 组件通过简单地迭代 Vuex weeklyMenu 对象的计算属性来获取它们的聚合数据,并且它“正常工作”。
  • 我通过迭代 Vuex 突变中的膳食并查找具有相同“成分名称”的膳食来更新同名膳食以相互匹配。

密码

HTML

<html>
  <body>
    <div id='weekly-menu'></div>
    <h3>Requirements:</h3>
    <ul>
      <li>Each row should have all the numbers in it summed and displayed ('total daily calories').</li>
      <li>The week as a whole should have all the numbers summed and displayed ('total weekly calories').</li>
      <li>If two or more input boxes have the same text, a change in one numerical input should propagate to the other same-named numerical inputs.</li>
      <li>Ideally the data (ingredient names and calories) should be stored in one place (the top-level component or a Vuex store) to make it more straightforward to populate it from the database with a single HTTP call (which is not simulated in this example).</li>
    </ul>
  </body>
</html>

JavaScript

const store = new Vuex.Store(
  {
    state: {
      weeklyMenu: {
        Sunday: {
          Breakfast: {
            name: 'aaa',
            calories: 1
          },
          Lunch: {
            name: 'bbb',
            calories: 2
          },
        },
        Monday: {
          Breakfast: {
            name: 'ccc',
            calories: 3
          },
          Lunch: {
            name: 'ddd',
            calories: 4
          },
        }
      }
    },
    mutations: {
      updateIngredientCalories (state, {dayOfTheWeekName, mealName, newCalorieValue}) {
        state.weeklyMenu[dayOfTheWeekName][mealName]['calories'] = newCalorieValue
        
        const ingredientNameBeingUpdated = state.weeklyMenu[dayOfTheWeekName][mealName]['name']
        for (const dayOfTheWeekName of Object.keys(state.weeklyMenu)) {
          for (const mealName of Object.keys(state.weeklyMenu[dayOfTheWeekName])) {
            const mealToCheck = state.weeklyMenu[dayOfTheWeekName][mealName]
            const ingredientNameToCheck = mealToCheck['name']
            if (ingredientNameToCheck === ingredientNameBeingUpdated) {
              mealToCheck['calories'] = newCalorieValue
            }
          }
        }
      },
      updateIngredientName (state, {dayOfTheWeekName, mealName, newValue}) {
        state.weeklyMenu[dayOfTheWeekName][mealName]['name'] = newValue
      }
    }
  }
)

var Meal = {
  template: `
    <td>
      <h4>{{ mealName }}</h4>
      Ingredient Name: <input v-model="ingredientName" /><br/>
      Calories: <input v-model.number="ingredientCalories" />
    </td>
  `,
  props:    [
    'dayOfTheWeekName',
    'mealName'
  ],
  computed: {
    ingredientCalories: {
      get () {
        return this.$store.state.weeklyMenu[this.dayOfTheWeekName][this.mealName]['calories']
      },
      set (value) {
        if (value === '' || value === undefined || value === null) {
          value = 0
        }
        this.$store.commit('updateIngredientCalories', {
          dayOfTheWeekName: this.dayOfTheWeekName,
          mealName: this.mealName,
          newCalorieValue: value
        })
      }
    },
    ingredientName: {
      get () {
        return this.$store.state.weeklyMenu[this.dayOfTheWeekName][this.mealName]['name']
      },
      set (value) {
        this.$store.commit('updateIngredientName', {
          dayOfTheWeekName: this.dayOfTheWeekName,
          mealName: this.mealName,
          newValue: value
        })
      }
    }
  }
};

var DailyMenu = {
  template: `
    <tr>
      <td>
        <h4>{{ dayOfTheWeekName }}</h4>
        Total Daily Calories: {{ totalDailyCalories }}
      </td>
      <meal :day-of-the-week-name="dayOfTheWeekName" meal-name="Breakfast" />
      <meal :day-of-the-week-name="dayOfTheWeekName" meal-name="Lunch" />
    </tr>
  `,
  props:    [
    'dayOfTheWeekName'
  ],
  data: function () {
    return {
    }
  },
  components: {
           meal: Meal
  },
  computed: {
    totalDailyCalories () {
      let totalDailyCalories = 0
      for (const mealName of Object.keys(this.$store.state.weeklyMenu[this.dayOfTheWeekName])) {
        totalDailyCalories += this.$store.state.weeklyMenu[this.dayOfTheWeekName][mealName]['calories']
      }
      return totalDailyCalories
    }
  }
};

var app = new Vue({ 
    el: '#weekly-menu',
  template: `<div id="weekly-menu" class="container">
    <div class="jumbotron">
    <h2>Weekly Menu</h2>
    Total Weekly Calories: {{ totalWeeklyCalories }}
    <table class="table">
        <tbody>
          <daily_menu day-of-the-week-name="Sunday" />
          <daily_menu day-of-the-week-name="Monday" />
        </tbody>
    </table>
    </div>
</div>
`,
  data: function () {
    return {
    }
  },
  computed: {
    totalWeeklyCalories () {
      let totalWeeklyCalories = 0
      for (const dayOfTheWeekName of Object.keys(this.$store.state.weeklyMenu)) {
        let totalDailyCalories = 0
        for (const mealName of Object.keys(this.$store.state.weeklyMenu[dayOfTheWeekName])) {
          totalDailyCalories += this.$store.state.weeklyMenu[dayOfTheWeekName][mealName]['calories']
        }
        totalWeeklyCalories += totalDailyCalories
      }
      return totalWeeklyCalories
    }
  },
  components: {
           daily_menu: DailyMenu
  },
  store: store
});