You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在React单路由中加载Botpress v12第三方聊天脚本?

解决Botpress v12聊天机器人在React多路由中全局显示的问题

我在React项目里集成了Botpress v12的第三方聊天机器人,封装成BotpressChatbot组件后只想让它在单个路由页面显示,但现在所有路由都能看到——估计是因为脚本直接挂到了body上。

先贴我的原组件代码:

const BotpressChatbot = () => {
    useEffect(() => {
      const script = document.createElement('script');
      const user = {
        user_profile: getUserFromSession()?.profile.name,
        oidc_User: sessionStorage.getItem(
          `${SESSION_OIDC_TOKEN_KEY}:${AUTHORITY}:${CLIENT}`,
        ),
      };
      var my_data = JSON.stringify(user);
      script.src =
        'https://dev.bot.ask.speciphic.ai/assets/modules/channel-web/inject.js';
      script.addEventListener('load', () => {
        try {
          let config = {
            host: 'https://dev.bot.ask.speciphic.ai',
            botId: 'jllask',
            exposeStore: false,
            startNewConvoOnTimeout: true,
            useSessionStorage: true,
            enableTranscriptDownload: false,
            composerPlaceholder: 'Reply to JLL Ask',
            enableConversationDeletion: true,
            disableNotificationSound: false,
            extraStylesheet:
              'https://dev.bot.ask.speciphic.ai/assets/modules/channel-web/jllAsk.css',
          };

          window.botpressWebChat.init(config);
          window.addEventListener('message', function (event) {
            if (event.data.name && event.data.name == 'webchatReady') {
              window.botpressWebChat.sendEvent({
                type: 'proactive-trigger',
                channel: 'web',
                payload: { text: my_data },
              });
            } else if (
              event.data.payload &&
              event.data.payload.type == 'session_reset'
            ) {
              window.botpressWebChat.sendEvent({
                type: 'proactive-trigger',
                channel: 'web',
                payload: { text: my_data },
              });
            }
          });
        } catch (e) {
          console.log('error in Botpress.jsx', e);
        }
      });
      document.body.appendChild(script);
      return () => {
        document.body.removeChild(script);
      };
    }, []);
    return <>></>;
};

export default BotpressChatbot;

原useEffect的清理函数只删了自己创建的脚本标签,但Botpress初始化后会在body里生成一个ID为bp-web-widget的独立DOM容器,所以切换路由时聊天窗口根本没被删掉,导致全局都显示。

后来我把清理函数改成这样:

return () => {
    const widget = document.getElementById('bp-web-widget');
    if (widget) document.body.removeChild(widget);
};

这下切换路由能把聊天窗口删掉,回到目标页面也能重新渲染,但新问题来了——父页面因为有很多useEffect频繁触发重渲染,导致聊天机器人的脚本被重复加到body里。


解决办法:控制脚本仅加载一次+完善清理逻辑

核心思路是:

  1. 给脚本加全局标记,避免重复注入
  2. 清理时不仅删DOM,还要移除全局事件监听,避免内存泄漏

修改后的完整组件代码:

const BotpressChatbot = () => {
  useEffect(() => {
    // 先检查脚本是否已经加载过,避免重复注入
    if (window.botpressWebChatLoaded) {
      // 如果已加载,检查聊天窗口是否存在,不存在就重新初始化
      const widget = document.getElementById('bp-web-widget');
      if (!widget) {
        initBotpressChat();
      }
      return;
    }

    const script = document.createElement('script');
    const user = {
      user_profile: getUserFromSession()?.profile.name,
      oidc_User: sessionStorage.getItem(
        `${SESSION_OIDC_TOKEN_KEY}:${AUTHORITY}:${CLIENT}`,
      ),
    };
    const my_data = JSON.stringify(user);

    // 把初始化逻辑抽成函数,方便复用
    function initBotpressChat() {
      try {
        const config = {
          host: 'https://dev.bot.ask.speciphic.ai',
          botId: 'jllask',
          exposeStore: false,
          startNewConvoOnTimeout: true,
          useSessionStorage: true,
          enableTranscriptDownload: false,
          composerPlaceholder: 'Reply to JLL Ask',
          enableConversationDeletion: true,
          disableNotificationSound: false,
          extraStylesheet:
            'https://dev.bot.ask.speciphic.ai/assets/modules/channel-web/jllAsk.css',
        };

        window.botpressWebChat.init(config);
        // 把消息监听函数存起来,方便后续清理
        const messageListener = function (event) {
          if (event.data.name && event.data.name === 'webchatReady') {
            window.botpressWebChat.sendEvent({
              type: 'proactive-trigger',
              channel: 'web',
              payload: { text: my_data },
            });
          } else if (
            event.data.payload &&
            event.data.payload.type === 'session_reset'
          ) {
            window.botpressWebChat.sendEvent({
              type: 'proactive-trigger',
              channel: 'web',
              payload: { text: my_data },
            });
          }
        };
        window.addEventListener('message', messageListener);
        window.botpressMessageListener = messageListener;
      } catch (e) {
        console.log('error in Botpress.jsx', e);
      }
    }

    script.src = 'https://dev.bot.ask.speciphic.ai/assets/modules/channel-web/inject.js';
    script.addEventListener('load', () => {
      // 标记脚本已加载
      window.botpressWebChatLoaded = true;
      initBotpressChat();
    });
    document.body.appendChild(script);

    // 完善清理函数
    return () => {
      // 删除聊天窗口DOM
      const widget = document.getElementById('bp-web-widget');
      if (widget) {
        document.body.removeChild(widget);
      }
      // 移除全局消息监听
      if (window.botpressMessageListener) {
        window.removeEventListener('message', window.botpressMessageListener);
        delete window.botpressMessageListener;
      }
      // 移除脚本标签(可选,脚本加载后其实就没用了,想彻底清理就加上)
      if (script && script.parentNode) {
        script.parentNode.removeChild(script);
      }
      // 如果希望离开页面后再次进入重新加载脚本,可以删掉下面的注释
      // delete window.botpressWebChatLoaded;
    };
  }, []);

  return <>></>;
};

export default BotpressChatbot;

额外优化:绑定路由变化(可选)

如果父页面重渲染频繁但路由没变化,可以用react-router的useLocation监听路由,只在目标路由时初始化聊天机器人:

import { useLocation } from 'react-router-dom';

const BotpressChatbot = () => {
  const location = useLocation();

  useEffect(() => {
    // 替换成你的目标路由路径
    const isTargetRoute = location.pathname === '/your-target-page';
    
    if (!isTargetRoute) {
      // 非目标路由时直接清理聊天窗口和监听事件
      const widget = document.getElementById('bp-web-widget');
      if (widget) {
        document.body.removeChild(widget);
      }
      if (window.botpressMessageListener) {
        window.removeEventListener('message', window.botpressMessageListener);
        delete window.botpressMessageListener;
      }
      return;
    }

    // 下面放之前的初始化逻辑...

  }, [location.pathname]);

  return <>></>;
};

内容的提问来源于stack exchange,提问作者Jayanta Basumatary

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 12:22:02