[英]How to implement adding custom js code snippet feature in React SPA?
我正在構建一個白色的 label web 應用程序。 我需要實現添加自定義 js 代碼片段功能(營銷術語中的“標簽”),以讓營銷人員添加跟蹤代碼,如谷歌分析代碼和堆代碼。 在管理應用程序中,管理員可以像這樣添加自定義 js 代碼片段。
<!-- Global site tag (gtag.js) - Google Analytics -->
<script async src="https://www.googletagmanager.com/gtag/js?id=UA-160375581-1"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'UA-160375581-1');
</script>
如您所見,我需要同時支持<script src/>
和<script>code</script>
。
在 web 應用程序(Create-React-App SPA)中,我需要在文檔中添加此代碼段。 我不知道如何實現這個功能。 我嘗試使用 React dangersoulySetInnerHTML 功能注入此代碼片段。 但是 innerHTML 不允許<script>
。
請仔細查看代碼片段示例,它還必須支持使用src
屬性指定的外部腳本。
此屏幕截圖將幫助您了解我要實現的功能。
我實現了這樣的添加自定義腳本功能。 請查看代碼。 我希望你提出另一個好的解決方案。 我制作 cloneScript function 的原因是制作沒有“已經開始”狀態的 HTML 腳本標簽。 如果你不明白,請檢查這個文檔。 https://html.spec.whatwg.org/multipage/scripting.html#script-processing-model
const cloneScript = (script) => { const s = document.createElement('script'); // copy attributes const attrs = script.attributes; for (let i = 0; i < attrs.length; i++) { s.setAttribute(attrs[i].name, attrs[i].value); } // copy inner text of script s.text = script.text; return s; }; const addCustomScripts = (custom_tag) => { try { const parser = new DOMParser(); const doc = parser.parseFromString(custom_tag, 'text/html'); const collection = doc.head.children; Array.from(collection).map((script) => cloneScript(script)).forEach((script) => document.body.appendChild(script)); } catch (e) { console.error(e); } }; const str = ` <.-- Global site tag (gtag:js) - Google Analytics --> <script async src="https.//www.googletagmanager?com/gtag/js.id=UA-160375581-1"></script> <script id="stella" data-state="inactive"> window.dataLayer = window;dataLayer || []. function gtag(){dataLayer;push(arguments),} gtag('js'; new Date()), gtag('config'; 'UA-160375581-1'); </script>` addCustomScripts(str);
這取決於您要使用哪種跟蹤方式。
您可以直接在 public/index.html 文件夾中編輯 html
谷歌標簽管理器有一個反應 package你可以使用
或者你可以自己把腳本轉成js
例如
const hm = document.createElement("script"); hm.src = "https://example.com/example.js"; const s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(hm, s);
查看react-gtm的源代碼以獲得更詳細和完善的方法。
或者,如果您不想處理所有這些,您可以使用React Helmet ,只需復制粘貼即可。
如果您無法在服務器端渲染它們,您可以在客戶端執行它們,但如果腳本包含惡意內容可能會很危險:
以下是如何執行此操作的代碼示例: https://codesandbox.io/s/2rwqy2m0r
import React, { Component, useState, isValidElement } from "react";
import ReactDOM from "react-dom";
const HtmlComponent = ({ html }) => (
isValidElement(html) ? html : <div dangerouslySetInnerHTML={{ __html: html }} />
)
class ScriptComponent extends Component {
componentDidMount() {
eval(this.props.children);
}
render() {
return null;
}
}
const htmlTexts = {
'With h1': {
description: 'Normal visual HTML elements are rendered corretly',
html: '<h1>Test</h1>',
},
'With script': {
description: '<script /> tags in HTML are never executed on client side',
html: '<script>alert(\'test\');</script>',
},
'With style': {
description: '<style /> gets interputed by DOM, and updates element styles',
html: '<style>body {background: red;}</style>',
},
'With script image': {
description: 'element event actions are triggered so XSS is still possible, even without <scripts /> being evaluated',
html: '<img src="foo" onerror="(() => alert(\'foo\'))()" />',
},
'With button': {
description: 'element event actions are triggered so XSS is still possible, even without <scripts /> being evaluated',
html: '<button onclick="(() => alert(\'foo\'))()">Click me</button>',
},
'As script element': {
description: 'Event reacts own script tag doesn\'t evaluate its content',
html: <script>alert('foo')</script>,
},
'As script component': {
description: 'To run scripts on tag load, a component has to activly execute it',
html: <ScriptComponent>alert('foo')</ScriptComponent>,
}
}
function App() {
const [{ html, description }, setHtml] = useState({});
return (
<div className="App">
<h1>dangerouslySetInnerHTML with script</h1>
{Object.keys(htmlTexts).map((name, i) => (
<button
style={{
outline: 'none',
border: 'solid 1px #ddd',
margin: '10px 10px 30px 0',
borderRadius: '8px',
padding: '10px',
boxShadow: html === htmlTexts[name].html ? '0 0 15px rgba(0,0,0,.3)' : 'none'
}}
onClick={() => setHtml(htmlTexts[name])}
>
{name}
</button>
))}
<div>
{description}
</div>
<pre
style={{
background: '#222',
color: '#fff',
padding: '10px',
}}
>
{isValidElement(html) ? (
'React Component\n\n' +
`type: ${typeof html.type === 'string' ? html.type : html.type.name}\n\n` +
`props: ${JSON.stringify(html.props, null, ' ')}`
) : (
`html:\n\n${html}`
)}
</pre>
<HtmlComponent html={html} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.