如何在 JSX 中渲染脚本标签而不手动创建它

Posted

技术标签:

【中文标题】如何在 JSX 中渲染脚本标签而不手动创建它【英文标题】:How to render script tag inside JSX without manually create it 【发布时间】:2020-07-25 15:03:45 【问题描述】:

我有从后端获取的 html 文档。这些文档由 2 种标签组成,我想动态呈现它们。这是我的源代码

const Content = ( slug ) => 
  const [data, setData] = useState()
  useScript("https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js");

  useEffect(() => 

    api.posts.read( slug: slug ,  formats: ["html", "plaintext"] ).then((resp) => 
      setData(resp)
    )
  , [slug])

  if (data) 
    return (
      <>
        <SEO
          title=data.title
          description=data.meta_description
          canonical_url=data.canonical_url/>
          <section className='content'>
            <h4 className='content-title'>data.title</h4>
            <div dangerouslySetInnerHTML= __html: data.html />
          </section>
      </>
      )
  
  return <Section>
    <CircularProgress style=color: '#004D80'/>
    <p>Loading</p>
  </Section>

我尝试了这 2 个,但它们都不适合我的用例。

1 &lt;script&gt; 已渲染但未执行

<div dangerouslySetInnerHTML= __html: data.html />

2

React: Script tag not working when inserted using dangerouslySetInnerHTML

这不适用于我的情况。如果我有类似的标签怎么办 &lt;script src="https://gist.github.com/ogyalcin/66d0785998588ab50cf1908f8d43bb7b.js"&gt;&lt;/script&gt; 为了在两个段落之间呈现代码块?此外,如果标签内的属性较多,也很难处理。

【问题讨论】:

好吧,这是特意设置的not to be executed 是的,有什么解决方法吗? 参考:***.com/questions/6432984/… 【参考方案1】:

不是答案,只是一些反馈仅供参考。我在浏览器中做了一个实验。好像脚本标签被渲染了。但是里面的代码没有执行,不知道为什么。

// @jsx

function App() 
  return <div><h1>React</h1><div dangerouslySetInnerHTML= __html: window.thatScript  /><h2>JS</h2></div>


ReactDOM.render(<App />, document.getElementById('root'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id='root'></div>
<script>
window.thatScript = '<script type="text/javascript">function foo() console.log("yolo") setTimeout(() => foo();<\script>'
</script>

【讨论】:

这是因为出于安全原因,通过 innerHTML 注入的脚本标签不会执行:developer.mozilla.org/en-US/docs/Web/API/Element/innerHTML 参考:github.com/facebook/react/issues/8838【参考方案2】:

可能会有所帮助:

来自 create-react-app 项目的示例代码。

import React,  useEffect  from 'react';
import './App.css';

function App() 
  const danger = '<script>alert("Hello world!")</script>';

  useEffect(() => 
    const el = document.querySelector('.danger');
    el.appendChild(document.createRange().createContextualFragment(danger));
  , []);

  return (
    <div className='App'>
      <h1>Render and execute script tag</h1>
      <div className='danger'></div>
    </div>
  );


export default App;

也可能有一个库:

dangerously-set-html-content

import React from 'react'
import InnerHTML from 'dangerously-set-html-content'

function Example 

  const html = `
    <div>This wil be rendered</div>
    <script>
      alert('testing')
    </script>
  `

  return (
    <InnerHTML html=html />
  )

这也适用于设置了 src 属性的脚本

分享好文章以获取更多信息: Render dangerous content with React

制造危险

现在,当您想使用 dangerouslySetInnerHTML 但还需要执行 html 中的任何脚本标记时会发生什么?这违反了 HTML5 规范,但是如果我们深入研究一下 innerHTML 是如何注入 html 的,我们会发现一些有趣的东西:

指定的值被解析为 HTML 或 XML(基于文档类型),生成一个 DocumentFragment 对象,表示新元素的新 DOM 节点集。

这个 DocumentFragment 是一个轻量级版本的文档,它可以有子节点,主要区别是因为是一个片段,实际上并不是活动/主文档的一部分。

我们可以使用 document.Range API 创建一个 DocumentFragment。

import React,  useEffect, useRef  from 'react'

// InnerHTML component
function InnerHTML(props) 
  const  html  = props
  const divRef = useRef(null)

  useEffect(() => 
    const parsedHTML = document.createRange().createContextualFragment> (html)
    divRef.current.appendChild(parsedHTML)
  , [])


  return (
    <div ref=divRef></div>
  )


// Usage
function App() 
  const html = `
    <h1>Fragment</h1>
  `

  return (
    <InnerHTML html=html />
  )

Reference on innerHTML

【讨论】:

以上是关于如何在 JSX 中渲染脚本标签而不手动创建它的主要内容,如果未能解决你的问题,请参考以下文章

如何在 GitHub 中创建标签而不创建发布?

在 React 中,如何将 JSX 与返回更多 JSX 的 JS 函数一起返回?

你如何在颤振飞镖中渲染脚本标签

如何迭代嵌套对象并在 jsx 中渲染?

如何在反应中从渲染函数返回一个空的jsx元素?

如何在 TypeScript 中键入这个“as”JSX 属性?