简体   繁体   English

如何在Vue.JS中更新插槽

[英]How to update a slot in Vue.JS

I have a Vue component simplified below. 我在下面简化了Vue组件。

Here is the template 这是模板

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

The slot may contain HTML, which is why I decided to use a slot rather than a prop which I would simply bind to. 插槽可能包含HTML,这就是为什么我决定使用插槽而不是我会简单绑定到的道具的原因。 I'd like to keep it that way. 我想保持这种方式。

I have a method that gets new HTML from the server. 我有一种从服务器获取新HTML的方法。 I'd like to use this new HTML to update the slot. 我想使用这个新的HTML来更新广告位。 I'm not sure if slots are reactive and how I can accomplish this. 我不确定插槽是否具有反应性以及如何实现此目的。

I can view the default slot using this.$slots.default[0] , but I don't know how to update it with a string of HTML content. 我可以使用this.$slots.default[0]查看默认插槽,但是我不知道如何使用字符串HTML内容更新默认插槽。 Simply assigning the string to the element is obviously incorrect, to .innerHtml does not work because it isn't an available function, and to .text doesn't work. 简单地将字符串分配给元素显然是不正确的,因为.innerHtml不是可用的函数,因此对.innerHtml不起作用,而对.text不起作用。 I assume that even though the text element exists on the slot object, the element properties take precedence. 我假定即使插槽对象上存在文本元素,但元素属性仍然优先。

Per suggestion in comments, I've tried this along with a computer property. 根据评论中的建议,我已经尝试过将其与计算机属性一起使用。

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

But now the problem is that it overwrites the slot passed to me. 但是现在的问题是,它覆盖了传递给我的广告位。

How can I reactively update a slot with new HTML in Vue.JS? 如何在Vue.JS中使用新的HTML反应式更新广告位?

I think your issue comes from a misunderstanding of how <slot> inherently works in VueJS. 我认为您的问题源于对<slot>在VueJS中固有工作方式的误解。 Slots are used to interweave content from a consuming parent component into a child component. 插槽用于将内容从使用中的父组件交织到子组件中。 See it as a HTML equivalent of v-bind:prop . 将其视为等同于v-bind:prop的HTML。 When you use v-bind:prop on a component, you are effectively passing data into a child component. 在组件上使用v-bind:prop时,实际上是在将数据传递到子组件中。 This is the same as slots. 这与插槽相同。

Without any concrete example or code from your end, this answer is at best just guess-work. 如果没有最终的具体示例或代码,此答案充其量只是猜测。 I assume that your parent component is a VueJS app itself, and the child component is the one that holds the <slot> element. 我假设您的父组件本身就是VueJS应用程序,子组件就是保存<slot>元素的组件。

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

<!-- Custom component template -->
<template>
    <slot></slot>
</template>

In this case, the app has a default ground state where it passes static HTML to the child component: 在这种情况下,应用程序具有默认的基本状态,在该状态下,它将静态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>

Then, when an event is fired, you want to replace that ground-state markup with new incoming markup. 然后,在触发事件时,您想用新的传入标记替换该基态标记。 This can be done by storing the incoming HTML in the data attribute, and simply using v-html to conditionally render it. 这可以通过将传入的HTML存储在data属性中,并简单地使用v-html有条件地呈现它来完成。 Let's say we want to store the incoming markup in app's vm.$data.customHTML : 假设我们要将传入的标记存储在应用程序的vm.$data.customHTML

data: {
    customHTML: null
}

Then your template will look like this: 然后您的模板将如下所示:

<!-- 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>

Note that in contrast to the code you have tried, the differences are that: 请注意,与您尝试的代码相比,不同之处在于:

  • It is the parent component (ie the consuming component ) that is responsible for dictating what kind of markup to pass to the child 父组件 (即消费组件 )负责决定将哪种标记传递给子组件
  • The child component is as dumb as it gets: it simply receives markup and renders it in the <slot> element 子组件非常笨拙 :仅接收标记并将其呈现在<slot>元素中

See proof-of-concept below: 请参阅下面的概念验证:

 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> 

Below is my solution though I don't like this opinion (load html into slot directly in current component level) because it breaks the rules for the slot. 以下是我的解决方案,尽管我不喜欢这种观点(将html直接加载到当前组件级别的插槽中),因为它违反了插槽的规则。 And I think you should do like this way ( <component><template v-html="yourHtml"></template></component> ), it will be better because Slot will focus on its job as Vue designed. 而且我认为您应该喜欢这种方式( <component><template v-html="yourHtml"></template></component> ),这样会更好,因为Slot将专注于Vue设计的工作。

The key is this.$slots.default must be one VNode, so I used extend() and $mount() to get the _vnode. 关键是this.$slots.default必须是一个VNode,因此我使用extend()$mount()来获取_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> 

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM