1

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

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

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

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>

4

1 回答 1

0

至于为什么您会看到无法修改根对象但可以修改其下的属性的这种行为,暴露的插槽变量是浅克隆,因此顶级引用与子对象中的对象引用不同零件。我添加了一个 console.log 并包装.string在一个对象中以显示它。单击childmodify按钮。

所以看起来你正试图将子组件的状态暴露给父组件,这样你就可以从父组件进入并改变子组件的状态。这通常不是您应该使用 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>

于 2020-04-20T11:18:18.990 回答