簡體   English   中英

如何在 React SPA 中實現添加自定義 js 代碼片段功能?

[英]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屬性指定的外部腳本。

此屏幕截圖將幫助您了解我要實現的功能。

instapage 構建器的屏幕截圖

我實現了這樣的添加自定義腳本功能。 請查看代碼。 我希望你提出另一個好的解決方案。 我制作 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);

這取決於您要使用哪種跟蹤方式。

  1. 您可以直接在 public/index.html 文件夾中編輯 html

  2. 谷歌標簽管理器有一個反應 package你可以使用

  3. 或者你可以自己把腳本轉成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的源代碼以獲得更詳細和完善的方法。

  4. 或者,如果您不想處理所有這些,您可以使用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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM