Skip to content

React 与 Vue 接入示例 ​

合从不提供框架适配包,React、Vue 项目用的是同一段 script 标签,在组件里调 window.hecong(cb) 即可。

script 标签放在 index.html ​

不要在组件里动态插入 script。聊天窗是页面级的,跟着页面走,不随组件的创建销毁。Nuxt 这类没有静态入口 HTML 的框架是例外,在应用级入口动态插入一次,写法见接入代码与配置项。

html
<!-- index.html -->
<body>
  <div id="root"></div>
  <script async
    src="https://assets.aihecong.com/sdk/hecong.js"
    data-channel-id="0190a1b2-c3d4-7e5f-8a9b-0c1d2e3f4a5b">
  </script>
  <script>
    window.hecong = window.hecong || function (c) { (window.hecong.q = window.hecong.q || []).push(c) }
  </script>
</body>

第二段里那行占位函数照原样复制,不要改写:组件里的调用可能跑在接入脚本加载完成之前,有了它 window.hecong 从一开始就存在,早调的回调排队等脚本就绪。详见接入代码与配置项。

Next.js、Nuxt 这类框架的写法见接入代码与配置项。

传客户身份 ​

window.hecong(cb) 早调晚调都能拿到命令对象,所以你只要在业务上知道客户是谁的那一刻调用即可。

jsx
import { useEffect } from 'react'

function useHecongIdentity(user) {
  useEffect(() => {
    if (!user) return
    window.hecong((hc) => {
      hc.identify({ id: user.id, profile: { name: user.name } })
    })
  }, [user])
}

// 客户退出登录时
function onLogout() {
  window.hecong((hc) => hc.reset())
}
js
import { watchEffect } from 'vue'

watchEffect(() => {
  if (!user.value) return
  window.hecong((hc) => {
    hc.identify({ id: user.value.id, profile: { name: user.value.name } })
  })
})

// 客户退出登录时
function onLogout() {
  window.hecong((hc) => hc.reset())
}

哪些要清理,哪些不用 ​

聊天窗本身不需要任何清理,组件卸载了它照样在页面上。

但你注册进去的东西要收回,否则组件重新挂载时会重复注册:

你调了什么卸载时要做什么
hc.identify()什么都不用做
hc.on(...)调它返回的退订函数
hc.registerComposerAction(...) / hc.registerQuickReply(...)调它返回的注销函数
jsx
useEffect(() => {
  let off
  let cancelled = false
  window.hecong((hc) => {
    if (cancelled) return
    off = hc.on('message:incoming', handleIncoming)
  })
  return () => {
    cancelled = true
    off?.()
  }
}, [])

组件可能在聊天窗脚本就绪前就卸载 —— React 开发模式下 StrictMode 会挂载、卸载、再挂载一次,必然踩到。这时 off 还没拿到,cancelled 的作用就是让迟到的回调不再注册。

hc.on() 返回的退订函数在任何阶段调用都有效,聊天窗还没加载时它会把这次订阅从待执行队列里撤掉。

另外,window.hecong(cb) 的返回值不是退订函数,不要这么用。

Vue 里对应写在 onBeforeUnmount 中。

用自己的按钮打开聊天窗 ​

jsx
function AskButton() {
  return (
    <button onClick={() => window.hecong((hc) => hc.open())}>
      咨询客服
    </button>
  )
}

TypeScript 项目 ​

合从没有发布类型声明包,自己声明一下全局即可:

ts
// hecong.d.ts
type HecongProfile = { name?: string; phone?: string; email?: string; avatar?: string }
type HecongData = Record<string, string | number | boolean>
type HecongPickerType = 'product' | 'order' | 'article'
type HecongAction = { id: string; label: string; icon?: string; onClick(): void }

interface HecongApi {
  identify(args: { id: string; profile?: HecongProfile; data?: HecongData }): Promise<unknown>
  reset(): Promise<unknown>
  open(): void
  close(): void
  toggle(): void
  setLocale(locale: string): void
  setColorScheme(scheme: 'light' | 'dark' | 'auto'): void
  registerComposerAction(action: HecongAction): () => void
  registerQuickReply(action: HecongAction): () => void
  setPickerData(type: HecongPickerType, items: unknown[]): void
  openPicker(type: HecongPickerType): void
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  on(event: string, handler: (arg: any) => void): () => void
  readonly isOpen: boolean
  readonly state: 'idle' | 'connecting' | 'open' | 'reconnecting' | 'closed'
}

declare global {
  interface Window {
    hecong: (cb: (hc: HecongApi) => void) => void
  }
}

export {}

完整的字段与类型见接口速查。

下一步 ​