如何使用 React 渲染 <template /> 标签?
How can I render a <template /> tag with react?
有时您可能需要从您的 React 应用程序呈现 Web 组件。
Web 组件通常使用特殊的 <template> ... </template>
标记。
但是如果我尝试用这样的反应来呈现这样的标记:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
然后我的网络组件无法正常工作。
原因是 JSX 的作用与 <template />
标签的作用不同。模板标签的想法是 不呈现它的 children 并且几乎像处理未解析的文本一样处理它(浏览器实际上解析它只是为了确保它有效 html, 但仅此而已)
但是当你在 JSX 中写这个时:
return (
<template>
<div>some content</div>
</template>
)
你基本上是在指示 react 创建一个 'template'
元素,然后 创建一个 'div'
元素 然后附加这个 div
template
作为 child.
所以在幕后发生了这种情况:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
但是你想要的是将<template />
的内容设置为一个字符串。您可以为此使用 innerHTML
。
解决方案
一个解决方案是:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
现在你要求 React 创建所有这些 children 标签作为节点元素,但让浏览器决定如何处理它们。
更好的解决方案
您可能不想一直使用 dangerouslySetInnerHTML
。那么让我们创建一个辅助组件:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
现在,任何时候您需要使用模板时,您都可以像这样使用它:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
不要忘记将内部内容放在引号中,因为它应该是一个字符串。
我知道这个问题已经有了答案,但我想还有一个更简单的解决方案可以创建 hoc(高阶组件)。
只需像这样创建新的“组件”:
// hoc/Template.js
const template = props => props.children
export default template
然后你可以这样在你的项目中使用它:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
较新版本的react已经构建了这样的组件,所以你不用创建组件也能达到同样的目的。
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
这实际上是 React 中的一个错误! https://github.com/facebook/react/issues/19932
当本机浏览器解析 <template>
标签时,它会将所有 children 附加到模板的 content
片段,而不是将它们添加为 children
。所以一个模板实际上不应该有任何 children
.
但是,如果您以编程方式构建 DOM(react-dom
的方式),则模板将具有 empty content
片段,因为所有 children 都添加为 children。这就是 Web 组件无法与这些模板一起正常运行的原因。
这里也有一个问题示例:https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
最简单的解决方法是使用 dangerouslySetInnerHTML
,如下所示:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
这受限于您只能提供原始 HTML(无自定义 React 元素)这一事实。但是,由于您首先使用的是 <template>
标签,因此您似乎不太可能在模板中使用 React。
有时您可能需要从您的 React 应用程序呈现 Web 组件。
Web 组件通常使用特殊的 <template> ... </template>
标记。
但是如果我尝试用这样的反应来呈现这样的标记:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
然后我的网络组件无法正常工作。
原因是 JSX 的作用与 <template />
标签的作用不同。模板标签的想法是 不呈现它的 children 并且几乎像处理未解析的文本一样处理它(浏览器实际上解析它只是为了确保它有效 html, 但仅此而已)
但是当你在 JSX 中写这个时:
return (
<template>
<div>some content</div>
</template>
)
你基本上是在指示 react 创建一个 'template'
元素,然后 创建一个 'div'
元素 然后附加这个 div
template
作为 child.
所以在幕后发生了这种情况:
const template = document.createElement('template')
const div = document.createElement('div')
const text = document.createTextNode('some text')
div.appendChild(text)
template.appendChild(div)
但是你想要的是将<template />
的内容设置为一个字符串。您可以为此使用 innerHTML
。
解决方案
一个解决方案是:
render() {
return (
<template
dangerouslySetInnerHTML={{
__html: '<div>some content</div>'
}}
/>
)
}
现在你要求 React 创建所有这些 children 标签作为节点元素,但让浏览器决定如何处理它们。
更好的解决方案
您可能不想一直使用 dangerouslySetInnerHTML
。那么让我们创建一个辅助组件:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
现在,任何时候您需要使用模板时,您都可以像这样使用它:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
不要忘记将内部内容放在引号中,因为它应该是一个字符串。
我知道这个问题已经有了答案,但我想还有一个更简单的解决方案可以创建 hoc(高阶组件)。
只需像这样创建新的“组件”:
// hoc/Template.js
const template = props => props.children
export default template
然后你可以这样在你的项目中使用它:
import './hoc/Template.js'
...
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
较新版本的react已经构建了这样的组件,所以你不用创建组件也能达到同样的目的。
import { Fragment } from 'react'
...
render() {
return (
<Fragment>
{'<div>some content</div>'}
</Fragment>
)
}
这实际上是 React 中的一个错误! https://github.com/facebook/react/issues/19932
当本机浏览器解析 <template>
标签时,它会将所有 children 附加到模板的 content
片段,而不是将它们添加为 children
。所以一个模板实际上不应该有任何 children
.
但是,如果您以编程方式构建 DOM(react-dom
的方式),则模板将具有 empty content
片段,因为所有 children 都添加为 children。这就是 Web 组件无法与这些模板一起正常运行的原因。
这里也有一个问题示例:https://codesandbox.io/s/new-sun-8l62o?file=/src/App.tsx:1056-1118
最简单的解决方法是使用 dangerouslySetInnerHTML
,如下所示:
<template dangerouslySetInnerHTML={{ __html: `
<p> Some text </p>
` }} />
这受限于您只能提供原始 HTML(无自定义 React 元素)这一事实。但是,由于您首先使用的是 <template>
标签,因此您似乎不太可能在模板中使用 React。