【问题标题】:Can't modify top level data of slot object in VueVue 无法修改 slot 对象的顶层数据
【发布时间】:2020-04-20 11:13:51
【问题描述】:

如果您运行此演示并单击“在子项中修改”,则文本会更新。但是如果你点击“通过插槽修改顶层”,那么它不会得到更新,并且点击它后点击其他按钮不再起作用。

如何更新插槽的顶级属性?例如布尔值或字符串。直接在孩子身上做是可行的,但我不能通过插槽来做。

如果子数据包含一个对象,我可以通过槽修改该数据对象的子属性(请参阅编辑前此问题的原始版本以获取演示),但我无法修改顶部级别属性。

const Child = {
  template: `<div>
          {{ object }}
          <slot name="named" v-bind="object">
          </slot>
          <button @click="click">child</button>
        </div>`,
  data() {
    return {
      object: {
        string: "initial"
      }
    }
  },
  methods: {
    click() {
      this.object.string = "modify in child"
    }
  }
}
new Vue({
  components: {
    Child,
  },
  template: `
          <div class="page1">
            <Child>
              <template v-slot:named="slot">
                <button @click="click(slot)">modify top level through slot</button>
              </template>
            </Child>
          </div>`,
  methods: {
    click(slot) {
      slot.string = "updated top level through slot"
    }
  }
}).$mount('#app')
<div id="app"></div>
<script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>

【问题讨论】:

    标签: javascript vue.js vuejs2


    【解决方案1】:

    至于为什么你会看到这种你不能修改根对象,但可以修改它下面的属性的行为,暴露的槽变量是一个浅克隆,所以顶级引用与对象引用不同在子组件中。我添加了一个 console.log 并将 .string 包装在一个对象中以显示它。单击child,然后单击modify 按钮。

    所以看起来您正在尝试将子组件的状态公开给父组件,因此您可以从父组件进入并改变子组件的状态。这通常不是您应该使用 Vue 的方式。这个想法是状态应该在树中向上移动,并且确定性道具通过组件树向下传播。

    在子组件上直接引用和改变状态是一种反模式。这是为了鼓励设计具有确定性行为的组件并保持组件的解耦(以保持它们独立和可重用)。还有一些性能优势。

    这家伙解释得很好:https://stackoverflow.com/a/31756470/120242
    Vue 和 React 基于相似的概念。

    const Child = {
      template: `<div>
              {{ object }}
              <slot name="named" v-bind="object">
              </slot>
              <button @click="click">child</button>
            </div>`,
      data() {
        return {
          object: {
            string: {x: "initial"}
          }
        }
      },
      methods: {
        click() {
          window.ChildObjectReference = this.object;
          this.object.string.x = "modify in child"
        }
      }
    }
    new Vue({
      components: {
        Child,
      },
      template: `
              <div class="page1">
                <Child>
                  <template v-slot:named="slot">
                    <button @click="click(slot)">modify top level through slot</button>
                  </template>
                </Child>
              </div>`,
      methods: {
        click(slot) {
          console.log( `slot: `,slot, `\nobject = `, window.ChildObjectReference,
             `\nslot !== ref as object: `, slot === window.ChildObjectReference,
             `\nslot.string === ref object.string: `, slot.string === window.ChildObjectReference.string)
        }
      }
    }).$mount('#app')
    <div id="app"></div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>

    显示槽与父组件的交互:

    const Child = {
      template: `<div>
              {{ object }}
              <slot name="named" v-bind="object">
              </slot>
              <button @click="click">child</button>
            </div>`,
      data() {
        return {
          object: {
            string: "initial"
          }
        }
      },
      methods: {
        click() {
          this.object.string = "modify in child"
        }
      }
    }
    new Vue({
      data: {
        topLevelObject: { property: "top level initial" }
      },
      components: {
        Child,
      },
      template: `
              <div class="page1">
                <Child>
                  <template v-slot:named="slot">
                    <div>this is v-bind:'object' on slot 'named' put into variable slot: {{ slot }}</div>
                    <button @click="click(slot)">modify top level through slot</button>
                  </template>
                </Child>
                Top Level state: {{ topLevelObject }}
              </div>`,
      methods: {
        click(slot) {
          this.topLevelObject.property = "slot.string pushed to top level: " + slot.string
        }
      }
    }).$mount('#app')
    <div id="app"></div>
    <script src="https://cdn.jsdelivr.net/npm/vue@2.6.11/dist/vue.min.js"></script>

    【讨论】:

    • 这是故意的。我的问题是问如何修改插槽对象的 TOP LEVEL 数据。
    • 我不确定您所说的“插槽对象的顶级数据”是什么意思。我可能仍然误解您要做什么。 slot 本质上是对子组件中 prop 的绑定,通常以允许从模板绑定到子组件中的 slot 的方式关联。
    • 顶级是指slot 对象下的属性。例如slot.property 而不是slot.sub.property。第一个不起作用,但第二个在更新时起作用。
    • 好的,这里有两个问题。一是 Vue 的默认观察者不会跟踪新的属性添加。 vuejs.org/v2/guide/reactivity.html#For-Objects 你指的是.property 但它不存在。看来您正试图改变从状态派生的插槽对象,因此试图直接从父级进入并改变子级的状态。这是故意的吗?
    • 对于第一个问题,它也不适用于Vue.$set。第二个.property 只是一个例子。我刚刚从示例代码中删除了不必要的slot.property
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-10-31
    • 1970-01-01
    • 2022-01-05
    • 2020-04-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多