在 Vue.js 中使用自定义渲染函数创建文本节点
Create text node with custom render function in Vue.js
我正在使用 my-link
组件根据需要在各种项目周围包装锚标记。为此,使用了自定义 render
方法 - 然而 createElement
方法只能创建 HTML 节点,创建纯文本节点似乎是不可能的。
当前场景
my-link
组件的用法
<template v-for="item in items">
<h4>
<my-link :url="item.url">{{ item.text }}</my-link>
</h4>
</template>
my-link
组件的实现为 Link.vue
<script>
export default {
name: 'my-link',
props: { url: String },
render(createElement) {
if (this.url) {
return createElement(
'a', {
attrs: { href: this.url }
}, this.$slots.default
);
}
return createElement(
'span',
this.$slots.default
);
}
};
</script>
结果HTML
<h4>
<a url="/some-link">This item is linked</a>
</h4>
<h4>
<span>Plain text item</span>
</h4>
期望的场景
在这种特定情况下,span
标记是多余的,可以避免 - 但是,我不清楚 Vue.js 如何以及是否完全可以做到这一点。一般来说,我想知道如何使用自定义 render
方法创建纯文本节点。
<h4>
<a url="/some-link">This item is linked</a>
</h4>
<h4>
Plain text item
</h4>
旁注:
- 问题最初是针对 VueJS v2.1 和 v2.2-beta(2017 年 2 月)提出的
- 重点是正确的语义节点(文本节点与元素节点)
创建纯文本节点的 Vue exposes an internal method on it's prototype called _v。您可以 return 从 render 函数调用此方法以呈现纯文本字符串的结果:
render(h){
return this._v("my string value");
}
以这种方式公开它,并带有下划线前缀,可能表明它是作为私有 API 方法使用的,因此请谨慎使用。
如果你使用功能组件,"this"也是不可用的。在这种情况下,你应该调用context._v(),例如:
functional: true,
render(h, context){
return context._v("my string value")
}
这与从插槽中提取文本(如在您的评论中,使用有用的 getChildrenTextContent)相结合将产生所需的结果。
您需要一个组件的根元素。在您的情况下,也许您可以使用 'h4' 作为根元素,因为我看到您无论如何都将其包含在 v-for 循环中。一旦你这样做了,你就可以像这样创建一个文本节点。
return createElement(
'h3',
{},
['Plain text item']
);
根据Vue文档,createElement的第三个参数可以是字符串也可以是数组,如果是字符串会转为文本节点。
https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
在 Vue 2.5.x 中,您可以使用它来呈现文本节点:
render(createElement) {
return createElement(() => {
return document.createTextNode('Text');
});
}
实际上,您可以通过将 MyLink 组件的实现更改为功能组件来避免使用上面答案中提到的 _v 方法(并可能避免使用稍后可能重命名的内部 Vue 方法)。功能组件不需要根元素,因此它可以避免在非 link 元素周围放置一个跨度。
MyLink 可以定义如下:
const MyLink = {
functional: true,
name: 'my-link',
props: { url: String },
render(createElement, context) {
let { url } = context.props
let slots = context.slots()
if (url) {
return createElement(
'a', {
attrs: { href: url }
}, slots.default
);
}
else {
return slots.default
}
}
};
然后要使用它,您可以在不同的组件中执行类似的操作:
<div>
<h4 v-for="item in items">
<my-link :url="item.url">{{ item.text }}</my-link>
</h4>
</div>
参见:https://codepen.io/hunterae/pen/MZrVEK?editors=1010
此外,作为旁注,您的原始代码片段似乎将文件命名为 Link.vue。由于您正在定义自己的渲染函数而不是使用 Vue 的模板系统,理论上您可以将文件重命名为 Link.js 并删除开始和结束脚本标签,并且只拥有一个完整的 JS 组件文件。但是,如果您的组件包含自定义系统(您的代码片段没有),则此方法将不起作用。希望这有帮助。
不需要使用任何私有方法,其实很简单。
仅呈现文本的 VNode
具有未定义的所有属性,除了文本。
所以你可以
return { text: 'my text' };
如果您使用的是 TypeScript,您可能需要断言它是一个 VNode。
万一有人遇到这个问题并且当前正在使用 Vue 3:API 已更改。您目前有 2 个选项:
- 安装
@vue/compat
以像在 Vue 2 中一样访问 _v()
- 否则,请选择
createTextVNode()
:
import { createTextVNode, VNode } from 'vue';
export const renderMoney = (data: number): VNode =>
createTextVNode(data.toLocaleString('en'));
在此示例 (Typescript) 中,您可以传入一个数字并返回纯文本 VNode
。
我正在使用 my-link
组件根据需要在各种项目周围包装锚标记。为此,使用了自定义 render
方法 - 然而 createElement
方法只能创建 HTML 节点,创建纯文本节点似乎是不可能的。
当前场景
my-link
组件的用法
<template v-for="item in items">
<h4>
<my-link :url="item.url">{{ item.text }}</my-link>
</h4>
</template>
my-link
组件的实现为 Link.vue
<script>
export default {
name: 'my-link',
props: { url: String },
render(createElement) {
if (this.url) {
return createElement(
'a', {
attrs: { href: this.url }
}, this.$slots.default
);
}
return createElement(
'span',
this.$slots.default
);
}
};
</script>
结果HTML
<h4>
<a url="/some-link">This item is linked</a>
</h4>
<h4>
<span>Plain text item</span>
</h4>
期望的场景
在这种特定情况下,span
标记是多余的,可以避免 - 但是,我不清楚 Vue.js 如何以及是否完全可以做到这一点。一般来说,我想知道如何使用自定义 render
方法创建纯文本节点。
<h4>
<a url="/some-link">This item is linked</a>
</h4>
<h4>
Plain text item
</h4>
旁注:
- 问题最初是针对 VueJS v2.1 和 v2.2-beta(2017 年 2 月)提出的
- 重点是正确的语义节点(文本节点与元素节点)
创建纯文本节点的 Vue exposes an internal method on it's prototype called _v。您可以 return 从 render 函数调用此方法以呈现纯文本字符串的结果:
render(h){
return this._v("my string value");
}
以这种方式公开它,并带有下划线前缀,可能表明它是作为私有 API 方法使用的,因此请谨慎使用。
如果你使用功能组件,"this"也是不可用的。在这种情况下,你应该调用context._v(),例如:
functional: true,
render(h, context){
return context._v("my string value")
}
这与从插槽中提取文本(如在您的评论中,使用有用的 getChildrenTextContent)相结合将产生所需的结果。
您需要一个组件的根元素。在您的情况下,也许您可以使用 'h4' 作为根元素,因为我看到您无论如何都将其包含在 v-for 循环中。一旦你这样做了,你就可以像这样创建一个文本节点。
return createElement(
'h3',
{},
['Plain text item']
);
根据Vue文档,createElement的第三个参数可以是字符串也可以是数组,如果是字符串会转为文本节点。
https://vuejs.org/v2/guide/render-function.html#createElement-Arguments
在 Vue 2.5.x 中,您可以使用它来呈现文本节点:
render(createElement) {
return createElement(() => {
return document.createTextNode('Text');
});
}
实际上,您可以通过将 MyLink 组件的实现更改为功能组件来避免使用上面答案中提到的 _v 方法(并可能避免使用稍后可能重命名的内部 Vue 方法)。功能组件不需要根元素,因此它可以避免在非 link 元素周围放置一个跨度。
MyLink 可以定义如下:
const MyLink = {
functional: true,
name: 'my-link',
props: { url: String },
render(createElement, context) {
let { url } = context.props
let slots = context.slots()
if (url) {
return createElement(
'a', {
attrs: { href: url }
}, slots.default
);
}
else {
return slots.default
}
}
};
然后要使用它,您可以在不同的组件中执行类似的操作:
<div>
<h4 v-for="item in items">
<my-link :url="item.url">{{ item.text }}</my-link>
</h4>
</div>
参见:https://codepen.io/hunterae/pen/MZrVEK?editors=1010
此外,作为旁注,您的原始代码片段似乎将文件命名为 Link.vue。由于您正在定义自己的渲染函数而不是使用 Vue 的模板系统,理论上您可以将文件重命名为 Link.js 并删除开始和结束脚本标签,并且只拥有一个完整的 JS 组件文件。但是,如果您的组件包含自定义系统(您的代码片段没有),则此方法将不起作用。希望这有帮助。
不需要使用任何私有方法,其实很简单。
仅呈现文本的 VNode
具有未定义的所有属性,除了文本。
所以你可以
return { text: 'my text' };
如果您使用的是 TypeScript,您可能需要断言它是一个 VNode。
万一有人遇到这个问题并且当前正在使用 Vue 3:API 已更改。您目前有 2 个选项:
- 安装
@vue/compat
以像在 Vue 2 中一样访问_v()
- 否则,请选择
createTextVNode()
:
import { createTextVNode, VNode } from 'vue';
export const renderMoney = (data: number): VNode =>
createTextVNode(data.toLocaleString('en'));
在此示例 (Typescript) 中,您可以传入一个数字并返回纯文本 VNode
。