将脚本标签添加到 React/JSX

IT技术 javascript reactjs ecmascript-6
2021-01-17 12:49:35

我有一个相对简单的问题,即尝试向 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>
        );
    }
};

我也试过:

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

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

PS:忽略 foobar,我有一个真正在使用的真实 id,我不想分享。

6个回答

编辑:事情变化很快,这已经过时了 - 请参阅更新


你想一次又一次地获取和执行脚本,每次渲染这个组件时,还是当这个组件被挂载到 DOM 时才一次?

也许尝试这样的事情:

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

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

    document.body.appendChild(script);
}

但是,这仅在您要加载的脚本不能作为module/包使用时才真正有用。首先,我总是:

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

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


更新:

现在我们有了钩子,更好的方法可能是useEffect像这样使用

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

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

  document.body.appendChild(script);

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

这使它成为自定义钩子的绝佳候选者(例如:)hooks/useScript.js

import { useEffect } from 'react';

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

    script.src = url;
    script.async = true;

    document.body.appendChild(script);

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

export default useScript;

可以这样使用:

import useScript from 'hooks/useScript';

const MyComponent = props => {
  useScript('https://use.typekit.net/foobar.js');

  // rest of your component
}
当脚本被附加到页面时,它将正常执行。例如,如果您使用此方法从 CDN 下载 jQuery,那么在该componentDidMount函数下载并将脚本附加到页面后,您将拥有全局可用jQuery$对象(即: on window)。
2021-03-15 12:49:35
这确实有效 - 加载脚本,但如何访问脚本中的代码。例如,我想调用位于脚本内的函数,但我无法在加载脚本的组件内调用它。
2021-03-17 12:49:35
我在使用身份验证脚本时遇到了类似的问题,结果发现将它包含在根目录的 html 文件中可能会更好,位于您的 React App.js 之上。如果有人觉得这很有用。正如@loganfsmith 提到的...
2021-03-19 12:49:35
我认为 TypeKit 的“高级”实现更适合这种方法。
2021-03-21 12:49:35
为什么return () => {document.body.removeChild(script);}为什么需要返回 removeChild
2021-04-09 12:49:35

我最喜欢的方法是使用 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>
    );
  }
};

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

这是迄今为止最好的解决方案。
2021-03-14 12:49:35
试过了,对我不起作用。我不建议使用 react-helmet 的唯一原因是它向脚本中注入了无法删除的额外属性。这实际上破坏了某些脚本,维护者多年没有修复它,拒绝github.com/nfl/react-helmet/issues/79
2021-03-21 12:49:35
@Darkowic,我通过添加async="true"<script>将 jQuery添加代码中标签来使您的代码工作
2021-03-26 12:49:35
@SomaMbadiwe 为什么它可以工作async=true但没有它会失败?
2021-03-27 12:49:35
不幸的是,它不起作用......见codeandbox.io/s/l9qmrwxqzq
2021-04-09 12:49:35

除了上面的答案,您还可以执行以下操作:

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)} />;
  }
}

div 被绑定,this脚本被注入其中。

可以在codeandbox.io上找到演示

您可能没有绑定this.instance到渲染方法中的 ref。我添加了一个演示链接来显示它的工作
2021-03-15 12:49:35
this.instance 对我不起作用,但 document.body.appendChild 对 Alex McMillan 的回答有用。
2021-03-18 12:49:35
@ShubhamKushwah 你一定是在做服务器端渲染?
2021-03-30 12:49:35
s.async = true 有什么需要,我找不到任何关于它的参考,要知道它的目的,你能解释一下吗
2021-03-31 12:49:35
@ArrayKnight 是的,我后来发现在服务器上这些对象不存在:document, window所以我更喜欢使用 npmglobal
2021-04-03 12:49:35

如果您需要<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>
这与` <h1 risklySetInnerHTML={{__html: page.title}}></h1>` <div risklySetInnerHTML={{__html: renderMD(page.body)}}></div> 相同
2021-03-12 12:49:35

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

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

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

为什么

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

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

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