如何解决 - 在同一渲染树中找到重复出现的插槽“default”

时间:2018-05-08 15:24:51

标签: javascript vue.js vuejs2 vue-component

注意:首先它可能看起来像是多次回答,但我觉得我的情况有所不同。

多次使用<slot>时,我收到以下错误。

  

在同一个渲染树中找到重复出现的插槽“default” -   这可能会导致渲染错误。

有使用Scoped Slots的解决方案,但据我所知,与v-for一起使用是很好的。我不确定,我可能错了,如果我是,请告诉我:)。

我遇到需要多次在子组件中复制静态内容(带标记)的情况。

// Parent Component
<template>
  <child-comp>
    <h1>Lorem Ipusm</h1>
    <button @click="fnDoSomething">Yahoo!<button>
    // ... there will be lot more lines of markups that will go here in default slot
  <child-comp>     
<template>


// Child Component
<template>
  <div>
    <h2>Need one default slot here</h2>
    <slot><slot>
    <div>
      <h2>Need one more default slot here</h2>
      <slot><slot>
    <div>
  </div>      
<template>

如果我不能解决上述问题或vue.js的限制,那么请帮助我,让我知道如何克隆插槽(或类似的东西),它仍然是被动的。

1 个答案:

答案 0 :(得分:2)

使用render function应该能够实现您的需求,如下面的演示:

但是你可能遇到一些像这个链接的麻烦:Why are duplicated slots bad?

来自Vue.js核心团队的开发人员说:

  

Vue会重用相同的vnode对象(代表元素)   在实际创建DOM元素期间多次。

     

问题在于每个vnode都会获得对它的引用   相应的DOM元素集。

     

如果多次重复使用相同的vnode对象,这些引用   得到覆盖,你最终得到没有的DOM元素   虚拟dom中的表示,或引用错误的vnode   元件。

因此在下面的演示中,当您单击该按钮时,您会发现第一个案例的第一个插槽未同步(VNode被覆盖)。

如果您的默认插槽是完全静态内容,则不绑定到任何属性和方法,则可以在render()中使用多个默认插槽。但如果没有,你必须使用范围插槽来实现你需要的东西。

或者你可以深度克隆 this.$slots.default(检查VNode constructor),它将避免被覆盖的问题。 (查看以下演示中的第三种情况)

Vue.config.productionTip = false
Vue.component('child', {
  render: function (createElement) {
    return createElement(
      'div',
      [
      this.$slots.default, // default slot
        createElement('div', {
          attrs: {
            name: 'test'
          },
          style: {fontSize: '10px', 'color':'green'}
        }, this.$slots.default) // default slot
      ]
    )
  }
})

function deepClone(vnodes, createElement){
 let clonedProperties = ['text','isComment','componentOptions','elm','context','ns','isStatic','key']
 function cloneVNode(vnode) {
	 let clonedChildren = vnode.children && vnode.children.map(cloneVNode)
	 let cloned = createElement(vnode.tag, vnode.data, clonedChildren)
   clonedProperties.forEach(function(item){
    cloned[item] = vnode[item]
   })
	 return cloned
 }
 return vnodes.map( cloneVNode )
}

Vue.component('child2', {
  render: function (createElement) {
    return createElement(
      'div',
      [
      this.$slots.default, // default slot
        createElement('div', {
          attrs: {
            name: 'test'
          },
          style: {fontSize: '10px', 'color':'green'}
        }, deepClone(this.$slots.default, createElement) ) // default slot
      ]
    )
  }
})

Vue.component('child1', {
  render: function (createElement) {

    return createElement(
      'div',
      [
      this.$slots.default, // default slot
        createElement('div', {
          attrs: {
            name: 'test'
          },
          style: {fontSize: '10px', 'color':'green'}
        }, this.$slots.my) // default slot
      ]
    )
  }
})

new Vue({
  el: '#app',
  data() {
    return {
      test: {
        'item': 'test',
        'prop1': 'a'
      }
    }
  },
  methods:{
    changeData: function() {
      this.test.item='none'
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
<div id="app">
<button @click="changeData()">Click me!!!</button>
<h1 style="background-color:red">Use multiple default slot:</h1>
<child><h1>{{test}}</h1></child>
<h1 style="background-color:red">Use scoped slot instead:</h1>
<child1><h1>{{test}}</h1><template slot="my"><h1>{{test}}</h1></template></child1>
<h1 style="background-color:red">Use Deep Clone (Default) instead:</h1>
<child2><h1>{{test}}</h1><template slot="my"><h1>{{test}}</h1></template></child2>
</div>