Vue组件插槽不视为子级吗?

时间:2019-09-04 05:39:07

标签: vue.js vuejs2 vue-component

让我们说我有一个父组件Tabs,它把子Tab组件作为插槽。

因此设置如下:

标签(父母)

<div>
  <slot name="tabChild">
</div>

标签(儿童)

<div>
  Tab {{name}}
</div>

MyApp

<Tabs>
   <Tab slot="tabChild" name="1" ></Tab>
   <Tab slot="tabChild" name="1" ></Tab>
</Tabs>

但是,当我尝试以编程方式访问其子级时,在Tabs(父级)组件中,如下所示:

标签组件

mounted(){
  let childTabs = this.$children  //this is empty?? 
  childTabs = this.$slots //this is correctly the child Tab Components
}

此外,在Tab(子)组件中,当我尝试访问其父级(我认为它是Tabs组件)(因为它们已插入其中)时,不是:

标签组件

mounted(){
   let parentTab = this.$parent //this is MyApp (grandfather), NOT Tabs
}

为什么在较大的Tabs组件中插入的tab子组件不是其子组件?

1 个答案:

答案 0 :(得分:0)

好吧,$parent将始终引用嵌套在其中的特定组件的“直接”父级/包装器,因此当需要引用“根”级父级时,它并不十分可靠。

以下是官方文档的一些摘录:

  1. 在大多数情况下,进入父级会使您的应用程序更难以调试和理解,尤其是当您更改父级中的数据时。以后查看该组件时,将很难弄清楚该突变来自何处。 [1]

  2. 请谨慎使用$parent$children,因为它们主要用作逃生舱口。首选使用道具和事件进行亲子沟通。 [2]

  3. 隐式的亲子交流:亲子组件交流应首选道具和事件,而不是this.$parent或变异道具。
    理想的Vue应用程序是道具下降,事件发生。遵循此约定可使您的组件更容易理解。 [3]

  4. 不幸的是,使用$parent属性无法很好地扩展嵌套更深的组件。在此处使用两个新的实例选项:provideinject依赖注入就很有用。 [4]


下面是一个简单的示例,它演示了上述建议:

const Parent = Vue.extend({
  template: `
    <ul :style="{ columnCount: colCount }">
      <slot></slot>
    </ul>
  `,

  provide() {
    return {
      biologicalParent: this
    }
  },

  props: ['value', 'colCount', 'message'],
  
  methods: {
    sayIt() {
      const 
        num = this.$children.indexOf(this.value) + 1,
        
        message = [
          `I am child no. ${num}`,
          `among ${this.$children.length} of my siblings`,
          `in ${this.colCount} different lineages.`
        ]
        .join(' ');

      this.$emit('update:message', message);
    }
  },
  
  watch: {
    value: 'sayIt'
  }
});

const Child = Vue.extend({
  template: `<li v-text="name" @click="setChild"></li>`,

  props: ['name'],

  inject: ['biologicalParent'],

  methods: {
    setChild() {
      this.biologicalParent.$emit('input', this);
    }
  }
});

new Vue({
  el: '#demo',

  data: () => ({
    lineage: 3,
    childCount: 10,
    message: 'Click on a child for the associated message.',
    lastChild: undefined
  }),

  components: {
    Parent,
    Child
  }
});
input {
  width: 4em;
}

li {
  cursor: pointer;
}

.message {
  background-color: beige;
  border: 1px solid orange;
  padding: 3px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="demo">
  <table>
    <tr>
      <td>Child count:</td>
      <td><input type="number" v-model="childCount" /></td>
    </tr>

    <tr>
      <td>Lineage:</td>
      <td><input type="number" v-model="lineage" /></td>
    </tr>
  </table>

  <parent 
    v-model="lastChild" 
    :col-count="lineage"
    :message.sync="message">
    <child 
      v-for="(n, i) of Array.apply(null, {length: childCount})" 
      :key="i" 
      :name="`child #${i+1}`">
    </child>
  </parent>

  <p class="message">{{ message }}</p>
</div>

provide选项允许我们指定要提供作为后代组件的datamethods。在上面的示例中,这是Parent实例。
注意如何在运行时(单击时)而不是编译时(渲染阶段)评估Child索引/编号。