代码之家  ›  专栏  ›  技术社区  ›  Goose

如何更新Vue中的插槽。JS公司

  •  6
  • Goose  · 技术社区  · 6 年前

    我在下面简化了一个Vue组件。

    这是模板

    <template>
        <slot></slot>
    </template>
    

    插槽可能包含HTML,这就是为什么我决定使用插槽而不是简单绑定到的道具。我想保持这样。

    我有一个从服务器获取新HTML的方法。我想用这个新的HTML来更新插槽。我不确定插槽是否是被动的,我如何才能做到这一点。

    我可以使用查看默认插槽 this.$slots.default[0] ,但我不知道如何用一串HTML内容更新它。简单地将字符串赋给元素显然是不正确的 .innerHtml 无法工作,因为它不是可用的函数 .text 不起作用。我假设,即使slot对象上存在text元素,元素属性也优先。

    根据评论中的建议,我已经将此与计算机属性一起尝试。

    <span v-html="messageContent"><slot></slot></span>
    

    但现在的问题是,它覆盖了传递给我的插槽。

    如何在Vue中使用新HTML反应性地更新插槽。JS?

    2 回复  |  直到 6 年前
        1
  •  10
  •   Terry    6 年前

    我认为你的问题源于对 <slot> 在VueJS中固有的工作方式。插槽用于将内容从消费父组件交织到子组件中。将其视为 v-bind:prop .当您使用 v形绑定:道具 在组件上,您可以有效地将数据传递到子组件中。这与插槽相同。

    如果你没有任何具体的例子或代码,这个答案是 充其量 猜猜看吧。我假设父组件是VueJS应用程序本身,子组件是保存 <插槽(>); 要素

    <!-- Parent template -->
    <div id="app">
        <custom-component>
            <!-- content here -->   
        </custom-component>
    </div>
    
    <!-- Custom component template -->
    <template>
        <slot></slot>
    </template>
    

    在这种情况下,应用程序有一个默认基态,将静态HTML传递给子组件:

    <!-- Parent template -->
    <div id="app">
        <custom-component>
            <!-- Markup to be interweaved into custom component -->
            <p>Lorem ipsum dolor sit amet.</p>
        </custom-component>
    </div>
    
    <!-- Custom component template -->
    <template>
        <slot></slot>
    </template>
    

    然后,当触发事件时,您希望用新的传入标记替换该基态标记。这可以通过将传入的HTML存储在 data 属性,并简单地使用 v-html 有条件地渲染它。假设我们要将传入的标记存储在应用程序的 vm.$data.customHTML :

    data: {
        customHTML: null
    }
    

    然后,您的模板将如下所示:

    <!-- Parent template -->
    <div id="app">
        <custom-component>
            <div v-if="customHTML" v-html="customHTML"></div>
            <div v-else>
                <p>Lorem ipsum dolor sit amet.</p>
            </div>
        </custom-component>
    </div>
    
    <!-- Custom component template -->
    <template>
        <slot></slot>
    </template>
    

    请注意,与您尝试的代码相比,区别在于:

    • 它是 父组件 (即 消费组件 )负责指定要传递给孩子的标记类型
    • 子组件如下所示 哑的 它只接收标记并在 <插槽(>); 要素

    见以下概念证明:

    var customComponent = Vue.component('custom-component', {
      template: '#custom-component-template'
    });
    
    new Vue({
      el: '#app',
      data: {
        customHTML: null
      },
      components: {
        customComponent: customComponent
      },
      methods: {
        updateSlot: function() {
          this.customHTML = '<p>Foo bar baz</p>';
        }
      }
    });
    .custom-component {
      background-color: yellow;
      border: 1px solid #000;
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.min.js"></script>
    
    <div id="app">
      <h1>I am the app</h1>
      <button type="button" @click="updateSlot">Click me to update slot content</button>
      <custom-component>
        <div v-if="customHTML" v-html="customHTML">
        </div>
        <div v-else>
          <p>Lorem ipsum dolor sit amet.</p>
        </div>
      </custom-component>
    </div>
    
    <!-- custom-component template -->
    <script type="text/template" id="custom-component-template">
      <div class="custom-component">
        <h2>I am a custom component</h2>
        <!-- slot receives markup set in <custom-component> -->
        <slot></slot>
      </div>
    </script>
        2
  •  5
  •   Sphinx    6 年前

    下面是我的解决方案,尽管我不喜欢这种观点(在当前组件级别将html直接加载到插槽中),因为它打破了插槽的规则。我想你应该这样做( <component><template v-html="yourHtml"></template></component> ),它会更好,因为Slot将专注于Vue设计的工作。

    关键是 this.$slots.default 必须是一个VNode,所以我使用 extend() $mount() 获取\u vnode。

    Vue.config.productionTip = false
    
    Vue.component('child', {
      template: '<div><slot></slot><a style="color:green">Child</a></div>',
      mounted: function(){
        setTimeout(()=>{
          let slotBuilder = Vue.extend({
            // use your html instead
            template: '<div><a style="color:red">slot in child</a></div>',
          })
          let slotInstance = new slotBuilder()
          this.$slots.default = slotInstance.$mount()._vnode
          this.$forceUpdate()
        }, 2000)
      }
    })
    
    new Vue({
      el: '#app',
      data() {
        return {
          test: ''
        }
      }
    })
    <script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.16/vue.js"></script>
    <div id="app">
    <child><h1>Test</h1></child>
    </div>