将脚本标记添加到React / JSX

时间:2015-12-22 21:38:29

标签: javascript reactjs ecmascript-6 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>
        );
    }
};

我也尝试过:

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

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

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

24 个答案:

答案 0 :(得分:230)

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

也许尝试这样的事情:

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

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

    document.body.appendChild(script);
}

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

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

这可能是您在示例中安装软件包reactreact-document-title的方式,并且有一个Typekit package available on npm

答案 1 :(得分:36)

除上述答案外,您还可以这样做:

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并将脚本注入其中。

可以在codesandbox.io

上找到演示

答案 2 :(得分:25)

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

e.g。

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

答案 3 :(得分:9)

如果您需要在SSR中进行<script>阻止(服务器端呈现),则使用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>

答案 4 :(得分: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;

答案 5 :(得分:7)

您也可以使用React头盔

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

头盔采用纯HTML标记并输出纯HTML标记。简直太简单了,而且对React初学者也很友好。

答案 6 :(得分:6)

提供的答案Alex Mcmillan对我帮助最大,但对于更复杂的脚本标记却没有起作用。

我略微调整了他的答案,想出了一个带有各种功能的长标签的解决方案,这些功能还在设置&#34; src&#34;。

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

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

答案 7 :(得分:2)

使用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));
}

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

答案 8 :(得分:2)

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

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

答案 9 :(得分:0)

您可以在调用 react 之前将脚本放入 Html 文件中。

答案 10 :(得分:0)

只需在 html 文件中添加正文

<script src="https://unpkg.com/react-dom@17/umd/react-dom.development.js" crossorigin></script>

答案 11 :(得分:0)

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

这些是我遵循的步骤。

第 1 步: 我在 react-app 文件夹路径中使用 npm i react-helmet终端 安装了 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>

答案 12 :(得分:0)

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

useScript.js

@client.command()
async def leaderboard(ctx, x=10):
  with open('level.json', 'r') as f:
    
    users = json.load(f)
    
  leaderboard = {}
  total=[]
  
  for user in list(users[str(ctx.guild.id)]):
    name = int(user)
    total_amt = users[str(ctx.guild.id)][str(user)]['experience']
    leaderboard[total_amt] = name
    total.append(total_amt)
    

  total = sorted(total,reverse=True)
  

  em = discord.Embed(
    title = f'Top {x} highest leveled members in {ctx.guild.name}',
    description = 'The highest leveled people in this server'
  )
  
  index = 1
  for amt in total:
    id_ = leaderboard[amt]
    member = client.get_user(id_)
    
    
    em.add_field(name = f'{index}: {member}', value = f'{amt}', inline=False)
    
    
    if index == x:
      break
    else:
      index += 1
      
  await ctx.send(embed = em)

用法看起来

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

}

注意: 将库保存在对象中,因为通常库是一个函数,React会在存储状态以检查更改时执行该函数-会破坏期望通过特定args调用的库(如Stripe)-因此我们将其存储在对象中以将其隐藏在React中,并防止库函数被调用。

答案 13 :(得分:0)

此答案解释了此行为背后的原因

任何呈现标记script的方法均无法按预期工作:

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

为什么

React DOM(用于在Web上进行反应的渲染器)使用createElement调用将JSX渲染为DOM元素。

createElement使用innerHTML DOM API最终将它们添加到DOM(see code in React source)中。添加innerHTML does not execute script标签是出于安全考虑。这就是为什么依次在React中呈现script标签无法按预期工作的原因。

要了解如何在React中使用script标签,请在此页面上找到其他答案。

答案 14 :(得分:0)

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

要在主体内的脚本标签中添加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) }} />;
    );
  }

此代码可以由console.log(windows.hello)

进行测试

答案 15 :(得分:0)

参加聚会有点晚,但是我决定在查看@Alex Macmillan答案后创建自己的答案,那就是通过传递两个额外的参数;放置脚本(例如或)并将异步设置为true / false的位置,这里是:

import { useEffect } from 'react';

const useScript = (url, position, async) => {
  useEffect(() => {
    const placement = document.querySelector(position);
    const script = document.createElement('script');

    script.src = url;
    script.async = typeof async === 'undefined' ? true : async;

    placement.appendChild(script);

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

export default useScript;

调用它的方式与本文所接受的答案完全相同,但具有两个额外的(再次)参数:

// First string is your URL
// Second string can be head or body
// Third parameter is true or false.
useScript("string", "string", bool);

答案 16 :(得分:0)

如果您的脚本具有npm软件包,请使用npm add进行安装,只需导入即可。例如“ chart.js”:

do_test

然后在您的组件中使用:

npm install chart.js --save

import Chart from "chart.js/dist/Chart.min.js"

答案 17 :(得分:0)

根据 Alex McMillan 的解决方案,我进行了以下修改。
我自己的环境:React 16.8+,下一个v9 +

///添加一个名为Script的自定义组件
// hooks / Script.js

import { useEffect } from 'react'

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

    script.src = url
    script.async = (typeof async === 'undefined' ? true : async )

    document.body.appendChild(script)

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

export default function Script({ src, async=true}) {

  useScript(src, async)

  return null  // Return null is necessary for the moment.
}

//使用自定义组件,只需将其导入并用自定义驼峰大小写 <script> 标记替换旧的小写 <Script> 标记就足够了。
// index.js

import Script from "../hooks/Script";

<Fragment>
  {/* Google Map */}
  <div ref={el => this.el = el} className="gmap"></div>

  {/* Old html script */}
  {/*<script type="text/javascript" src="http://maps.google.com/maps/api/js"></script>*/}

  {/* new custom Script component */}
  <Script src='http://maps.google.com/maps/api/js' async={false} />
</Fragment>

答案 18 :(得分:0)

您可以在以下链接中找到最佳答案:

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

答案 19 :(得分:0)

componentDidMount() {
  const head = document.querySelector("head");
  const script = document.createElement("script");
  script.setAttribute(
    "src",
    "https://assets.calendly.com/assets/external/widget.js"
  );
  head.appendChild(script);
}

答案 20 :(得分:0)

尝试了所有建议的解决方案,但没有任何效果。我试图将其嵌入到组件中,但未加载。这是来自bannersnack.com的横幅。

<script type="text/javascript">
var bannersnack_embed = {"hash":"bxpien9pr","width":1190,"height":300,"t":1559889975,"userId":39254375,"responsive":true,"type":"html5"};
</script>
<script type="text/javascript" src="//cdn.bannersnack.com/iframe/embed.js"></script>

答案 21 :(得分:0)

对于多个脚本,请使用此

var loadScript = function(src) {
  var tag = document.createElement('script');
  tag.async = false;
  tag.src = src;
  document.getElementsByTagName('body').appendChild(tag);
}
loadScript('//cdnjs.com/some/library.js')
loadScript('//cdnjs.com/some/other/library.js')

答案 22 :(得分:0)

刷新时我得到了document "undefined"。所以我尝试了这种方式

const MyComponent = () => {
   let script = React.createElement("script", {
    src: "https://.....js path",
    async: true,
    defer: true,
    crossorigin: "anonymous"
})

return (

        <div>
            {script}
        </div>)
}

答案 23 :(得分:-2)

解决方案取决于方案。就像我的情况一样,我不得不在反应组件中加载一个calendly嵌入。

Calendly查找div并从其data-url属性中读取并在所述div中加载iframe。

首次加载页面时一切都很好:首先,呈现带有data-url的div。然后将calendly脚本添加到正文中。浏览器下载并评估它,我们都快乐回家。

当您离开然后返回页面时出现问题。这次脚本仍在正文中,浏览器不会重新下载&amp;重新评估它。

修正:

  1. componentWillUnmount上找到并删除脚本元素。然后重新安装,重复上述步骤。
  2. 输入$.getScript。它是一个漂亮的jquery助手,它接受一个脚本URI和一个成功的回调。加载完脚本后,它会对其进行评估并触发您的成功回调。我所要做的只是在componentDidMount $.getScript(url)。我的render方法已经有了calendly div。它运作顺畅。