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

问题描述

背景:

问题:

我的问题:

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

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

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

相关链接:

我试图处理的情况的简化示例:

标签: javascriptvue.jsvuejs2vuexstate-management

解决方案


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

我对这个解决方案的发现总结是,“当 Vuex 状态对象的嵌套条目更新时,Vue实际上更新;您不必担心它不会检测到这些更改。所以只保留所有数据就可以了当您有许多需要相互响应的重复同级组件时,在单个大型 Vuex 存储对象中。”

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

截屏

在此处输入图像描述

CodePen 示例的功能总结

  • 用于填充菜单的数据都存在于 Vuex 存储中的单个weeklyMenu对象中,该对象具有子对象以将数据分解为不同的日期/餐点。
  • 各个餐点具有计算属性getset函数,因此它既可以从商店获取更改,也可以更新商店。
  • DailyMenu 和 WeeklyMenu 组件通过简单地具有迭代 VuexweeklyMenu对象的计算属性来获取它们的聚合数据,并且它“正常工作”。
  • 我通过迭代 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
});

推荐阅读