
在 react 应用中,使用 usescript 等自定义 hook 动态注入外部脚本时,若脚本路径采用相对形式(如 ./tagging.js),在标准路由下可能正常工作,但在动态路由(如 /brand/:brandname)下会因浏览器解析基准 url 变化而导致脚本加载失败,并抛出 "unexpected token '
在现代 React 应用中,我们有时需要动态加载第三方脚本,例如分析工具、广告标签或自定义功能库。为了更好地管理脚本的生命周期和状态,通常会封装一个自定义 Hook,如 useScript。这个 Hook 负责创建、插入 <script> 标签到文档头部,并监听其加载或错误事件,以返回脚本的当前状态(loading、ready 或 error)。
以下是一个典型的 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 配置了动态路由时,例如:
<Routes>
<Route path='/offer' element={<Offer />} />
<Route path='/hearing-agency' element={<HearingAgency />} />
<Route
path='/hearing-agency/brand/:brandname' // 动态路由
element={<HearingAgency />}
/>
<Route path='*' element={<Offer />} />
</Routes>如果我们在 /offer 这样的标准路由下调用 useScript('./tagging.js'),脚本通常能正常加载。然而,当导航到 /hearing-agency/brand/phonak 这样的动态路由时,尽管 useScript Hook 可能报告脚本状态为 ready,但实际上脚本并未正确执行,并且在控制台会抛出 SyntaxError: Unexpected token '<' 错误。
检查文档的 <head> 部分,会发现 <script> 标签确实被插入了,但其 src 属性在动态路由下似乎未能正确解析。
问题的根本原因在于,当使用相对路径(如 ./tagging.js)来指定脚本源时,浏览器会根据当前页面的 基准 URL (Base URL) 来解析这个相对路径。
如果你的 tagging.js 文件实际存放在应用的 public 目录(或根目录),那么在动态路由下,浏览器会尝试从 https://yourdomain.com/hearing-agency/brand/tagging.js 加载脚本。由于这个路径通常不存在实际的 tagging.js 文件,服务器可能会返回应用的 index.html 内容(这是单页应用路由的常见配置),浏览器在尝试将 HTML 内容解析为 JavaScript 时,就会遇到 < 符号,从而抛出 SyntaxError: Unexpected token '<' 错误。
解决这个问题的关键是确保脚本的 URL 始终相对于应用的根目录解析,无论当前路由如何。这可以通过将相对路径改为绝对路径来实现。
将 useScript 的调用从:
useScript('./tagging.js'); // 相对路径修改为:
useScript('/tagging.js'); // 绝对路径或者,如果你的脚本文件名为 dummyscript.js:
useScript('/dummyscript.js'); // 绝对路径通过在路径前添加 /,我们指示浏览器从当前域名的根目录开始解析路径。这样,无论当前页面是 /offer 还是 /hearing-agency/brand/phonak,/tagging.js 都会被正确解析为 https://yourdomain.com/tagging.js,从而确保脚本能够从正确的位置加载。
<BrowserRouter basename="/my-app">
{/* ... 你的路由配置 */}
</BrowserRouter>在这种情况下,你的绝对路径 '/tagging.js' 仍会相对于 basename 解析,即 https://yourdomain.com/my-app/tagging.js。
在 React 应用中利用 useScript 等自定义 Hook 动态加载外部脚本时,面对动态路由导致的脚本加载失败问题,其核心原因在于相对路径在不同基准 URL 下的解析差异。通过将脚本路径从相对路径(如 ./tagging.js)修改为绝对路径(如 /tagging.js),可以确保脚本始终从应用根目录正确解析,从而彻底解决 SyntaxError: Unexpected token '<' 的问题。采用绝对路径是处理 React 应用中静态资源路径的推荐做法,能够提升应用的健壮性和可维护性。
以上就是React 应用中动态路由下脚本注入失败的解决方案的详细内容,更多请关注php中文网其它相关文章!
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号