我有一个相对简单的尝试向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正在使用中,我并不想分享。答案 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 install typekit
)import
我需要的包裹(import Typekit from 'typekit';
)这可能是您在示例中安装软件包react
和react-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
并将脚本注入其中。
答案 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>
);
}
};
答案 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实施时需要。
@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
的方法均无法按预期工作:
script
标签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)
您可以在以下链接中找到最佳答案:
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;重新评估它。
componentWillUnmount
上找到并删除脚本元素。然后重新安装,重复上述步骤。$.getScript
。它是一个漂亮的jquery助手,它接受一个脚本URI和一个成功的回调。加载完脚本后,它会对其进行评估并触发您的成功回调。我所要做的只是在componentDidMount
$.getScript(url)
。我的render
方法已经有了calendly div。它运作顺畅。