如何在React中渲染一个<template />标签?

11
有时候您可能需要从React应用程序中呈现Web组件。

Web组件通常使用特殊的<template> ... </template>标签。 但是如果我像这样使用React渲染这种标记:

render() {
  return (
    <template>
      <div>some content</div>
    </template>
  )
}

那么我的 Web 组件就无法正常工作。

3个回答

13

原因是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>
  )
}

不要忘记将内部内容放在引号中,因为它应该是一个字符串。


如果模板包含脚本(这恰好是我的用例),那么dangerouslySetInnerHTML将无法工作。 - Christophe
我遇到了相同的问题!@Christophe 你找到任何解决方案了吗? - Carl V.
我认为我放弃了,但最有前途的路径是Range.createContextualFragment(),它确实可以让脚本运行。可能与Reactjs ref一起使用。 - Christophe
我通过将子元素包装在 ReactDomServer.renderToString 中解决了这个问题。 - Carl V.

0

这实际上是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。


0

我知道这个问题已经有了答案,但是我想还有另一种更简单的解决方法,那就是创建一个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>
  )
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接