Web组件通常使用特殊的<template> ... </template>
标签。
但是如果我像这样使用React渲染这种标记:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
那么我的 Web 组件就无法正常工作。
Web组件通常使用特殊的<template> ... </template>
标签。
但是如果我像这样使用React渲染这种标记:
render() {
return (
<template>
<div>some content</div>
</template>
)
}
那么我的 Web 组件就无法正常工作。
原因是JSX执行的工作与<template />
标签存在的不同。模板标签的想法是不渲染它的子元素,并且基本上像未解析的文本一样处理它(浏览器实际上会解析它以确保其有效的html格式,但不做更多的事情)
但是当您在JSX中编写以下内容时:
return (
<template>
<div>some content</div>
</template>
)
你基本上是在指示React创建一个'template'
元素,然后 创建一个'div'
元素,再把这个div
作为子元素添加到template
中。
因此,在幕后发生了以下情况:
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创建所有这些子标签作为节点元素,但让浏览器决定如何处理它们。
你可能不想一直使用dangerouslySetInnerHTML
。那么我们可以创建一个帮助组件:
function Template({ children, ...attrs }) {
return (
<template
{...attrs}
dangerouslySetInnerHTML={{ __html: children }}
/>
);
}
现在,每当您需要使用模板时,可以像这样使用:
render() {
return (
<Template>
{'<div>some content</div>'}
</Template>
)
}
不要忘记将内部内容放在引号中,因为它应该是一个字符串。
这实际上是React中的一个漏洞!https://github.com/facebook/react/issues/19932
当本地浏览器解析<template>
标签时,它将所有子元素附加到模板的content
片段中,而不是将它们添加为children
。因此,模板实际上不应该有任何children
。
但是,如果以编程方式构建DOM(就像react-dom
一样),那么由于所有子元素都被添加为子元素,所以模板将具有一个空的content
片段。这就是为什么使用这些模板的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。
我知道这个问题已经有了答案,但是我想还有另一种更简单的解决方法,那就是创建一个hoc(Higher Order Component)。
只需要像这样创建一个新的“组件”:
// 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>
)
}
ReactDomServer.renderToString
中解决了这个问题。 - Carl V.