更新时间:2021-09-20 04:27:02
<textarea>
组件被Vue渲染器视为静态组件,因此将它们放入DOM后,它们根本不会发生变化(因此,这就是为什么如果您检查DOM,则会在<textarea>
的内部看到<slot></slot>
.
<textarea>
components are treated as static by the Vue renderer, thus after they are put into the DOM, they don't change at all (so that's why if you inspect the DOM you'll see <slot></slot>
inside your <textarea>
).
但是即使改变了,也无济于事.只是因为<textarea>
中的HTML元素没有成为它们的值.您必须设置 TextArea元素的value
属性使它正常工作.
But even it if they did change, that wouldn't help much. Just because HTML elements inside <textarea>
s don't become their value. You have to set the value
property of the TextArea element to make it work.
无论如何,请不要绝望.这是可行的,您需要克服的上述问题就是发挥一个小的辅助组件的作用.
Anyway, don't despair. It is doable, all you need to overcome the issues above is to bring a small helper component into play.
有很多方法可以实现这一目标,如下所示.它们在本质上有不同,您希望原始组件的模板是这样.
There are many possible ways to achieve this, two shown below. They differ basically in how you would want your original component's template to be.
您组件的模板现在将变为:
Your component's template would now become:
<template>
<div>
<textarea-slot
v-model="myContent"
:name="name"
:id="id">
<slot></slot>
</textarea-slot>
</div>
</template>
如您所见,除了用<textarea-slot>
替换<textarea>
外,什么都没有改变.这足以克服Vue对<textarea>
进行的静态处理. <textarea-slot>
的完整实现在下面的演示中.
As you can see, nothing but replacing <textarea>
with <textarea-slot>
changed. This is enough to overcome the static treatment Vue gives to <textarea>
. The full implementation of <textarea-slot>
is in the demo below.
解决方案是创建一个助手组件(在下面命名为vnode-to-html
),该组件将转换插槽的
The solution is to create a helper component (named vnode-to-html
below) that would convert your slot's VNodes into HTML strings. You could then set such HTML strings as the value
of your <textarea>
. Your component's template would now become:
<template>
<div>
<vnode-to-html :vnode="$slots.default" @html="valForMyTextArea = $event" />
<textarea
:value="valForMyTextArea"
:name="name"
:id="id">
</textarea>
</div>
</template>
my-component
的用法保持不变:
The usage of the my-component
stays the same:
<my-component>
<p class="textbox">hello world</p>
</my-component>
Vue.component('my-component', {
props: ["content", "name", "id"],
template: `
<div>
<textarea-slot
v-model="myContent"
:name="name"
:id="id">
<slot></slot>
</textarea-slot>
<vnode-to-html :vnode="$slots.default" @html="valueForMyTextArea = $event" />
<textarea
:value="valueForMyTextArea"
:name="name"
:id="id">
</textarea>
</div>
`,
data() { return {valueForMyTextArea: '', myContent: null} }
});
Vue.component('textarea-slot', {
props: ["value", "name", "id"],
render: function(createElement) {
return createElement("textarea",
{attrs: {id: this.$props.id, name: this.$props.name}, on: {...this.$listeners, input: (e) => this.$emit('input', e.target.value)}, domProps: {"value": this.$props.value}},
[createElement("template", {ref: "slotHtmlRef"}, this.$slots.default)]
);
},
data() { return {defaultSlotHtml: null} },
mounted() {
this.$emit('input', [...this.$refs.slotHtmlRef.childNodes].map(n => n.outerHTML).join('\n'))
}
});
Vue.component('vnode-to-html', {
props: ['vnode'],
render(createElement) {
return createElement("template", [this.vnode]);
},
mounted() {
this.$emit('html', [...this.$el.childNodes].map(n => n.outerHTML).join('\n'));
}
});
new Vue({
el: '#app'
})
<script src="https://unpkg.com/vue"></script>
<div id="app">
<my-component>
<p class="textbox">hell
o world1</p>
<p class="textbox">hello world2</p>
</my-component>
</div>
故障:
<slot>
解析为 this.$slots.SLOTNAME
属性中可用.自然,默认插槽位于this.$slots.default
.<slot>
传递的内容(作为this.$slots.default
中的VNode).现在的挑战变成了如何将这些VNode转换为HTML字符串?.这是一个复杂的这很可能需要一段时间..template-slot
和vnode-to-html
)都使用Vue的render函数将VNode渲染到DOM,然后拾取渲染的HTML.vnode-to-html
作为事件返回,应由父级(my-component
)拾取,该父级使用传递的值来设置data
属性,该属性将被设置为textarea
的:value
. textarea-slot
向父级声明自己为<textarea>
.这是一种更清洁的解决方案,但需要更多注意,因为您必须指定要传递给在textarea-slot
内部创建的<textarea>
的属性.<slot>
s into VNode
s and makes them available in the this.$slots.SLOTNAME
property. The default slot, naturally, goes in this.$slots.default
.<slot>
(as VNodes in this.$slots.default
). The challenge now becomes how to convert those VNodes to HTML String? This is a complicated, still open, issue, which may get a different solution in the future, but, even if it ever does, it will most likely take a while.template-slot
and vnode-to-html
) use Vue's render function to render the VNodes to the DOM, then picks up the rendered HTML.<script>
tags.vnode-to-html
returns as an event that should be picked up by the parent (my-component
) which uses the passed value to set a data
property that will be set as :value
of the textarea
.textarea-slot
declares itself a <textarea>
, to the parent doesn't have to. It is a cleaner solution, but requires more care because you have to specify which properties you want to pass down to the <textarea>
created inside textarea-slot
.
但是,重要的是要知道Vue在将声明的<template>
解析为<slot>
时,会剥离一些格式信息,例如***组件之间的空格.同样,它会剥离<script>
标记(因为它们不安全).这些是使用<slot>
的任何解决方案所固有的警告(此处未显示).所以要注意.
However possible, it is important to know that Vue, when parsing the declared <template>
into <slot>
s, will strip some formatting information, like whitespaces between top-level components. Similarly, it strips <script>
tags (because they are unsafe). These are caveats inherent to any solutions using <slot>
s (presented here or not). So be aware.
Vue的典型富文本编辑器通过使用v-model
(或value
)属性将代码传递到组件中,从而完全解决了此问题.
Typical rich text editors for Vue, work around this problem altogether by using v-model
(or value
) attributes to pass the code into the components.
众所周知的例子包括:
他们在他们的网站上都有非常好的文档(如上链接),所以在这里重复它们对我没有多大用处,但仅作为示例,请参见codemirror如何使用value
道具来传递代码:
They all have very good documentation in their websites (linked above), so it would be of little use for me to repeat them here, but just as an example, see how codemirror uses the value
prop to pass the code:
<codemirror ref="myCm"
:value="code"
:options="cmOptions"
@ready="onCmReady"
@focus="onCmFocus"
@input="onCmCodeChange">
</codemirror>
所以他们就是这样做的.当然,如果<slot>
s(带有警告)适合您的用例,则也可以使用它们.
So that's how they do it. Of course, if <slot>
s - with its caveats - fit your use case, they can be used as well.