将脚本标记添加到React/JSX

Arr*_*ght 197 javascript ecmascript-6 reactjs react-jsx

我有一个相对简单的问题,试图将内联脚本添加到React组件.到目前为止我所拥有的:

'use strict';

import '../../styles/pages/people.scss';

import React, { Component } from 'react';
import DocumentTitle from 'react-document-title';

import { prefix } from '../../core/util';

export default class extends Component {
    render() {
        return (
            <DocumentTitle title="People">
                <article className={[prefix('people'), prefix('people', 'index')].join(' ')}>
                    <h1 className="tk-brandon-grotesque">People</h1>

                    <script src="https://use.typekit.net/foobar.js"></script>
                    <script dangerouslySetInnerHTML={{__html: 'try{Typekit.load({ async: true });}catch(e){}'}}></script>
                </article>
            </DocumentTitle>
        );
    }
};
Run Code Online (Sandbox Code Playgroud)

我也尝试过:

<script src="https://use.typekit.net/foobar.js"></script>
<script>try{Typekit.load({ async: true });}catch(e){}</script>
Run Code Online (Sandbox Code Playgroud)

这两种方法似乎都没有执行所需的脚本.我猜这是一件我想念的简单事.有人可以帮忙吗?

PS:忽略foobar,我有一个真正的ID实际上在使用,我不想分享.

Ale*_*lan 289

是否要在每次渲染此组件时一次又一次地获取和执行脚本,或者只是在将此组件装入DOM时只执行一次?

也许尝试这样的事情:

componentDidMount () {
    const script = document.createElement("script");

    script.src = "https://use.typekit.net/foobar.js";
    script.async = true;

    document.body.appendChild(script);
}
Run Code Online (Sandbox Code Playgroud)

但是,如果要加载的脚本不可用作模块/包,这只是非常有用.首先,我会永远:

  • npm上查找包
  • 在我的项目中下载并安装包(npm install typekit)
  • import我需要的包裹(import Typekit from 'typekit';)

这可能是您安装软件包的方式react,也可能是您react-document-title的示例,并且npm上有一个Typekit软件包.

  • 这确实有效 - 加载脚本,但我怎样才能访问脚本中的代码.例如,我想调用一个存在于脚本中的函数,但是我无法在加载脚本的组件内调用它. (7认同)
  • 进行清理是很好的做法。就我而言,NextJS 删除导航到另一个页面时的脚本意味着该脚本在返回时重新执行。 (4认同)
  • 当脚本被附加到页面时,它将正常执行。例如,如果您使用此方法从 CDN 下载 jQuery,那么在 `componentDidMount` 函数下载并将脚本附加到页面后,您将拥有全局可用的 `jQuery` 和 `$` 对象(即: `窗口`)。 (3认同)
  • 我认为 TypeKit 的“高级”实现更适合这种方法。 (2认同)
  • 我在使用身份验证脚本时遇到了类似的问题,结果发现将它包含在根目录的 html 文件中可能会更好,位于您的 React App.js 之上。如果有人觉得这很有用。正如@loganfsmith 提到的...... (2认同)
  • 为什么 `return () =&gt; {document.body.removeChild(script);}` ?为什么需要removeChild的返回 (2认同)

sid*_*son 49

继上面的答案,你可以这样做:

import React from 'react';

export default class Test extends React.Component {
  constructor(props) {
    super(props);
  }

  componentDidMount() {
    const s = document.createElement('script');
    s.type = 'text/javascript';
    s.async = true;
    s.innerHTML = "document.write('This is output by document.write()!')";
    this.instance.appendChild(s);
  }

  render() {
    return <div ref={el => (this.instance = el)} />;
  }
}
Run Code Online (Sandbox Code Playgroud)

div被绑定this并且脚本被注入其中.

可以在codesandbox.io上找到演示

  • this.instance对我不起作用,但是document.body.appendChild从Alex McMillan的回答中做到了. (5认同)
  • 您可能没有将`this.instance`绑定到render方法中的ref.我添加了一个演示链接来显示它的工作情况 (5认同)

Pat*_*rex 40

我最喜欢的方法是使用React Helmet - 它是一个允许以您可能习惯的方式轻松操作文档头的组件.

例如

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <script src="https://use.typekit.net/foobar.js"></script>
                <script>try{Typekit.load({ async: true });}catch(e){}</script>
            </Helmet>
            ...
        </div>
    );
  }
};
Run Code Online (Sandbox Code Playgroud)

https://github.com/nfl/react-helmet

  • 这是迄今为止最好的解决方案. (4认同)
  • 不幸的是,它无法正常工作...请参阅https://codesandbox.io/s/l9qmrwxqzq (3认同)
  • 试过这个,对我不起作用。我不建议使用react-helmet,其唯一原因是它将无法删除的额外属性注入脚本。这实际上破坏了某些脚本,并且维护者多年未对其进行修复,并拒绝https://github.com/nfl/react-helmet/issues/79 (3认同)
  • @Darkowic,我通过将`async =“ true”`添加到将jQuery添加到代码的`&lt;script&gt;`标签中来使您的代码正常工作。 (2认同)

Div*_*ani 13

这样的回答解释了为什么这种行为背后。

任何方式来呈现script预期标签不工作:

  1. script标签用于外部脚本
  2. 使用 dangerouslySetInnerHTML

为什么

React DOM(react on web 的渲染器)使用createElement调用将 JSX 渲染为 DOM 元素。

createElement使用innerHTMLDOM API 最终将这些添加到 DOM(参见 React 源代码中的代码)。innerHTML 不执行script作为安全考虑添加的标签。这就是为什么script在 React 中渲染标签不能按预期工作的原因。

有关如何script在 React 中使用标签,请查看此页面上的其他一些答案。

  • 谢谢你!我认为原因很重要。 (2认同)

小智 11

Alex Mcmillan提供的答案对我帮助最大,但对于更复杂的脚本标签却不太有用.

我略微调整了他的答案,想出了一个带有各种功能的长标签解决方案,另外还设置了"src".

(对于我的用例,脚本需要生活在头部,这也反映在这里):

  componentWillMount () {
      const script = document.createElement("script");

      const scriptText = document.createTextNode("complex script with functions i.e. everything that would go inside the script tags");

      script.appendChild(scriptText);
      document.head.appendChild(script);
  }
Run Code Online (Sandbox Code Playgroud)

  • 我不明白你为什么要使用React,如果你只是将内联JS转储到页面上......? (4认同)
  • 您需要在代码中添加 document.head.removeChild(script); ,否则只要用户访问此页面路由,您就会在 html 中创建无限数量的 script 标记 (3认同)

sca*_*aza 10

如果你需要<script>在SSR中进行阻塞(服务器端渲染),那么一种方法componentDidMount将无效.

您可以改用react-safe库.React中的代码将是:

import Safe from "react-safe"

// in render 
<Safe.script src="https://use.typekit.net/foobar.js"></Safe.script>
<Safe.script>{
  `try{Typekit.load({ async: true });}catch(e){}`
}
</Safe.script>
Run Code Online (Sandbox Code Playgroud)

  • 哈。“安全”意味着“危险”,这是时代的标志。 (6认同)

Muh*_*ani 8

您也可以使用反应头盔

import React from "react";
import {Helmet} from "react-helmet";

class Application extends React.Component {
  render () {
    return (
        <div className="application">
            <Helmet>
                <meta charSet="utf-8" />
                <title>My Title</title>
                <link rel="canonical" href="http://example.com/example" />
                <script src="/path/to/resource.js" type="text/javascript" />
            </Helmet>
            ...
        </div>
    );
  }
};
Run Code Online (Sandbox Code Playgroud)

Helmet 采用纯 HTML 标签并输出纯 HTML 标签。它非常简单,而且 React 初学者友好。


Cor*_*son 7

我为这个特定的案例创建了一个React组件:https://github.com/coreyleelarson/react-typekit

只需将你的Typekit Kit ID作为道具传递,你就可以了.

import React from 'react';
import Typekit from 'react-typekit';

const HtmlLayout = () => (
  <html>
    <body>
      <h1>My Example React Component</h1>
      <Typekit kitId="abc123" />
    </body>
  </html>
);

export default HtmlLayout;
Run Code Online (Sandbox Code Playgroud)


Jos*_*eph 7

以下是我最终能够在 React JS 代码中添加两个外部 JavaScript 文件的方法:

这些是我遵循的步骤。

第1步: 我在react-app文件夹路径中使用终端安装了React-Helmet 。npm i react-helmet

第 2 步: 然后我import {Helmet} from "react-helmet";在代码中添加了标头。

第 3 步:最后,在我的代码中,这就是我使用Helment 添加外部 JS 文件的方式

<Helmet>
    <script src = "path/to/my/js/file1.js" type = "text/javascript" />
    <script src = "path/to/my/js/file2.js" type = "text/javascript" />  
</Helmet>
Run Code Online (Sandbox Code Playgroud)


Max*_*ils 6

有一个非常好的解决方法,使用Range.createContextualFragment.

/**
 * Like React's dangerouslySetInnerHTML, but also with JS evaluation.
 * Usage:
 *   <div ref={setDangerousHtml.bind(null, html)}/>
 */
function setDangerousHtml(html, el) {
    if(el === null) return;
    const range = document.createRange();
    range.selectNodeContents(el);
    range.deleteContents();
    el.appendChild(range.createContextualFragment(html));
}
Run Code Online (Sandbox Code Playgroud)

这适用于任意 HTML 并保留上下文信息,例如document.currentScript.


ray*_*pps 6

我试图编辑@Alex McMillan 接受的答案,但它不会让我如此这里有一个单独的答案,您可以在其中获得您加载的库的价值。人们要求的一个非常重要的区别,我需要我的使用 stripe.js 实现。

使用脚本.js

import { useState, useEffect } from 'react'

export const useScript = (url, name) => {

  const [lib, setLib] = useState({})

  useEffect(() => {
    const script = document.createElement('script')

    script.src = url
    script.async = true
    script.onload = () => setLib({ [name]: window[name] })

    document.body.appendChild(script)

    return () => {
      document.body.removeChild(script)
    }
  }, [url])

  return lib

}
Run Code Online (Sandbox Code Playgroud)

用法看起来像

const PaymentCard = (props) => {
  const { Stripe } = useScript('https://js.stripe.com/v2/', 'Stripe')
}
Run Code Online (Sandbox Code Playgroud)

注意:将库保存在对象中,因为通常库是一个函数,而 React 将在存储状态时执行该函数以检查更改——这将破坏期望使用特定参数调用的库(如 Stripe)——所以我们将它存储在一个对象中以对 React 隐藏它并保护库函数不被调用。


Ash*_*shh 5

您可以使用npm postscribe在反应组件中加载脚本

postscribe('#mydiv', '<script src="https://use.typekit.net/foobar.js"></script>')
Run Code Online (Sandbox Code Playgroud)


小智 5

您可以通过以下链接找到最佳答案:

https://cleverbeagle.com/blog/articles/tutorial-how-to-load-third-party-scripts-dynamically-in-javascript

const loadDynamicScript = (callback) => {
const existingScript = document.getElementById('scriptId');

if (!existingScript) {
    const script = document.createElement('script');
    script.src = 'url'; // URL for the third-party library being loaded.
    script.id = 'libraryName'; // e.g., googleMaps or stripe
    document.body.appendChild(script);

    script.onload = () => {
      if (callback) callback();
    };
  }

  if (existingScript && callback) callback();
};
Run Code Online (Sandbox Code Playgroud)

  • `document.getElementById('scriptId'); ` 这不应该是 `document.getElementById('libraryName');` (4认同)

sms*_*ash 5

要在 head 标签中添加脚本标签或代码<head>,请使用react-helmet包。它很轻并且有很好的文档。

要在body内的script标签中添加Js代码,

    function htmlDecode(html) {
      return html.replace(/&([a-z]+);/ig, (match, entity) => {
        const entities = { amp: '&', apos: '\'', gt: '>', lt: '<', nbsp: '\xa0', quot: '"' };
        entity = entity.toLowerCase();
        if (entities.hasOwnProperty(entity)) {
          return entities[entity];
        }
        return match;
      });
    }
  render() {
    const scriptCode = `<script type="text/javascript">
          {(function() {
          window.hello={
            FIRST_NAME: 'firstName',
            LAST_NAME: 'lastName',
          };
          })()}
          </script>`
    return(
      <div dangerouslySetInnerHTML={{ __html: this.htmlDecode(scriptCode) }} />;
    );
  }

Run Code Online (Sandbox Code Playgroud)

这段代码可以通过以下方式测试console.log(windows.hello)


wic*_*nja 5

与其他答案非常相似,只是使用默认值来清理未定义的检查

import { useEffect } from 'react'

const useScript = (url, selector = 'body', async = true) => {
  useEffect(() => {
    const element = document.querySelector(selector)
    const script = document.createElement('script')
    script.src = url
    script.async = async
    element.appendChild(script)
    return () => {
      element.removeChild(script)
    }
  }, [url])
}

export default useScript

Run Code Online (Sandbox Code Playgroud)

用法

useScript('/path/to/local/script.js') // async on body
useScript('https://path/to/remote/script.js', 'html') // async on html 
useScript('/path/to/local/script.js', 'html', false) // not async on html.. e.g. this will block
Run Code Online (Sandbox Code Playgroud)