React 应用中动态路由下脚本注入失败的解决方案

在 react 应用中,使用 usescript 等自定义 hook 动态注入外部脚本时,若脚本路径采用相对形式(如 ./tagging.js),在标准路由下可能正常工作,但在动态路由(如 /brand/:brandname)下会因浏览器解析基准 url 变化而导致脚本加载失败,并抛出 "unexpected token '

理解 React 应用中的脚本动态注入

在现代 React 应用中,我们有时需要动态加载第三方脚本,例如分析工具、广告标签或自定义功能库。为了更好地管理脚本的生命周期和状态,通常会封装一个自定义 Hook,如 useScript。这个 Hook 负责创建、插入

以下是一个典型的 useScript Hook 实现:

import { useEffect, useState } from 'react';
import { useLocation } from 'react-router-dom';

const useScript = (url) => {
  const [status, setStatus] = useState(url ? 'loading' : 'idle');
  const location = useLocation(); // 引入 useLocation 以监听路由变化

  useEffect(() => {
    if (!url) {
      setStatus('idle');
      return;
    }

    // 尝试查找已存在的脚本,避免重复加载
    let script = document.querySelector(`script[src="${url}"]`);

    if (!script) {
      // 如果脚本不存在,则创建并插入
      script = document.createElement('script');
      script.src = url;
      script.type = 'text/javascript';
      script.async = true;
      document.head.appendChild(script);

      // 首次设置 data-status 属性
      const setAttributeFromEvent = (event) => {
        script.setAttribute(
          'data-status',
          event.type === 'load' ? 'ready' : 'error'
        );
      };
      script.addEventListener('load', setAttributeFromEvent);
      script.addEventListener('error', setAttributeFromEvent);
    } else {
      // 如果脚本已存在,则读取其状态
      setStatus(script.getAttribute('data-status'));
    }

    // 监听脚本加载状态,更新组件的 state
    const setStateFromEvent = (event) => {
      setStatus(event.type === 'load' ? 'ready' : 'error');
    };
    script.addEventListener('load', setStateFromEvent);
    script.addEventListener('error', setStateFromEvent);

    // 清理函数:组件卸载或 URL 变化时移除事件监听器
    return () => {
      if (script) {
        script.removeEventListener('load', setStateFromEvent);
        script.removeEventListener('error', setStateFromEvent);
      }
    };
  }, [url, location]); // 依赖项包括 url 和 location

  return status;
};

export default useScript;

在组件中使用时,通常会这样调用:

const status = useScript('./tagging.js'); // 使用相对路径
console.log(status);

动态路由下的脚本加载问题

当 React 应用使用 react-router-dom 配置了动态路由时,例如:


   } />
   } />
   }
   />
   } />

如果我们在 /offer 这样的标准路由下调用 useScript('./tagging.js'),脚本通常能正常加载。然而,当导航到 /hearing-agency/brand/phonak 这样的动态路由时,尽管 useScript Hook 可能报告脚本状态为 ready,但实际上脚本并未正确执行,并且在控制台会抛出 SyntaxError: Unexpected token '

检查文档的

部分,会发现