簡體   English   中英

如何在反應中加載自定義腳本?

[英]How to load custom script in react?

這有點類似於這個問題:

將腳本標簽添加到 React/JSX

但就我而言,我正在加載這樣的腳本:

<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
})(window,document,'script','dataLayer','ID');</script>
<!-- End Google Tag Manager -->

現在我知道谷歌標簽管理器有一個 npm package 但我很好奇如果我想以自定義方式執行此操作我將如何 go 呢?

在上面的問題中,我看到了很多:

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

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

document.body.appendChild(script);

這很好,但是如果我在加載的腳本中有一個 function 我將如何正確執行這個 go?

要添加這樣的隨機腳本,您可以:

  1. 將腳本添加到您的 index.html
  2. 將代碼粘貼到文件中並使用導入語句。
  3. 一旦用戶做某事,使用代碼拆分動態加載腳本。

1. 將腳本添加到您的 HTML

只需將腳本標簽粘貼在 index.html 文件中,最好放在正文標簽的末尾。 如果使用 create-react-app,則 index.html 文件位於公共目錄中:

<body>
  <div id="root"></div>
  <script>/* your script here */</script>
</body>

2.從文件導入

或者,您可以將腳本粘貼到 .js 文件中,然后從代碼中的任何位置導入它。 導入通用腳本的好地方是您的 index.js 入口點文件。 這種方法的好處是將腳本包含在 js 包的 rest 中,從而實現縮小和搖樹。

// index.js
import "../path/to/your/script-file";

3. 代碼拆分最后,如果您想在某個時間點動態加載一段 js 代碼,同時確保它不是您的起始包的一部分,您可以使用動態導入進行代碼拆分。 https://create-react-app.dev/docs/code-splitting

function App() {
  function handleLoadScript() {
    import('./your-script')
      .then(({ functionFromModule }) => {
        // Use functionFromModule 
      })
      .catch(err => {
        // Handle failure
      });
  };

  return <button onClick={handleLoadScript}>Load</button>;
}

通常,可以使用dangerouslySetInnerHTML屬性更新 HTML 元素。 但是對於要執行的腳本,這將不起作用,正如其他SO question中所討論的那樣。

您必須實現此目的的一個選項是將元素附加到新文檔上下文中,使用文檔 Range API, createContextualFragment

下面的工作示例。 請注意,我已經稍微調整了您的腳本,以展示一些自定義它的方法。

const { useState, useRef, useEffect, memo } = React;

const MyCustomScriptComponent = () => {
  const [includeScript, setIncludeScript] = useState(false)

  // just some examples of customizing the literal script definition
  const labelName = 'dataLayer'
  const gtmId = 'ID' // your GTM id

  // declare the custom <script> literal string
  const scriptToInject = `
    <script>
(function(w,d,s,l,i){
const gtmStart = new Date().getTime();
w[l]=w[l]||[];w[l].push({'gtm.start':
gtmStart,event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
console.log("loaded at gtmStart:", gtmStart);
})(window,document,'script','${labelName}','${gtmId}');
console.log("fetching GTM using id '${gtmId}'");
     </script>`

  const InjectScript = memo(({ script }) => {
    const divRef = useRef(null);

    useEffect(() => {
      if (divRef.current === null) {
        return;
      }
      // create a contextual fragment that will execute the script
      // beware of security concerns!!
      const doc = document
          .createRange()
          .createContextualFragment(script)
      
      // clear the div HTML, and append the doc fragment with the script 
      divRef.current.innerHTML = ''
      divRef.current.appendChild(doc)
    })

    return <div ref={divRef} />
  })

  const toggleIncludeScript = () => setIncludeScript((include) => !include)

  return (
    <div>
      {includeScript && <InjectScript script={scriptToInject} />}
      <p>Custom script {includeScript ? 'loaded!' : 'not loaded.'}</p>
      <button onClick={toggleIncludeScript}>Click to load</button>
    </div>
  )
}

ReactDOM.render(<MyCustomScriptComponent />, document.getElementById('app'))

codepen上實時試用。

如需更多參考,您可以在此中型帖子中找到更多注入腳本的替代方法。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

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