AI 智能管家 JSBridge 深度问答(面试 + 实战)
用途:把 JSBridge 双向通信机制讲透,含 6 个深度问题 + 完整开门案例
适合:被追问「getToken 是步骤几」「method 命名规则」「原生→H5 怎么回传数据」时脱口而出
核心思想:method 名字串起 H5→RN→H5 三步,requestId 串起一次会话
一、JSBridge 双向通信机制(10 步)
先记住这张图,后面所有问题都围绕它展开。

步骤速查表
| 步骤 | 谁 | 调什么 | 代码位置 |
|---|---|---|---|
| step1 | H5 | window.ReactNativeWebView.postMessage |
H5 端 SDK |
| step2 | WebView | onMessage 回调 自动触发 |
WebView.js prop |
| step3 | webViewHandlers.js | handleWebViewMessage 函数 |
utils/webViewHandlers.js |
| step4 | webViewHandlers.js | handlers[method] 按 method 查表 |
同上 |
| step5 | 原生 handler | 调业务 API(fetch) | utils/agentToolHandlers/doorHandlers.js |
| step6 | 业务后端 | 返回数据 | --- |
| step7 | 原生 handler | ctx.sendMessage(method, data, requestId) |
doorHandlers.js |
| step8 | webViewHandlers.js 到 WebView.js | ctx.sendMessage 到 webViewRef.injectJavaScript |
WebView.js |
| step9 | H5 | window.dispatchEvent nativeMessage 注入的 JS 触发 |
H5 监听 |
| step10 | H5 | 拿到数据 渲染聊天界面 | H5 端 SDK |
二、6 个深度问题速答
Q1:智能体云服务(nexa.newbest.com.cn)是哪个厂商?
A :公司域名 newbest.com.cn 的子域名,大概率是采购的第三方 SaaS (思必驰/涂鸦/大厂之一)。LLM 包含在云服务里,前端不需要单独买。
判断依据:
| 证据 | 推断 |
|---|---|
域名 <YOUR_AGENT_DOMAIN> |
公司自有域名,不是第三方云厂商 |
tenantId + agentId 多租户配置 |
SaaS 服务典型特征 |
SDK AgentChatSDK |
第三方厂商提供的标准 SaaS SDK 命名 |
LLM 处理 :智能体云服务内部封装,前端零感知。买的套餐里包含 LLM 调用次数。
Q2:前端需要理解这么详细吗?RN 项目能区分「智能体云服务」和「LLM」吗?
A :前端不用理解这么深 。RN 代码完全看不见 LLM ,把云服务当黑盒。
前端只需要理解 3 句话:
markdown
1. RN 端把用户说的话发给智能体云服务
2. 等结果
3. 把结果显示出来
中间的「云服务怎么调 LLM、怎么过滤权限 」完全在云服务内部,前端零感知。
| 层级 | RN 项目能看到 | 看不到 |
|---|---|---|
| 智能体云服务 | 能看到(URL <YOUR_AGENT_DOMAIN>) | --- |
| LLM | 完全看不到 | 模型名称 调用次数 prompt |
| 工具注册表 | 完全看不到 | 哪些工具注册了 |
| 权限过滤 | 完全看不到 | 用户能用哪些工具 |
Q3:智能体云服务怎么处理工具权限?
A :大致流程是用户身份 → 权限过滤 → 工具列表 + 用户消息 → LLM → 二次校验 → 返回。前端不可见。
推测的流程:
markdown
用户发请求「帮我开一号楼大门」
智能体云服务 收到请求
1. 识别用户身份 拿到 userId/token
2. 查该用户的权限(A 能调哪些工具)
3. 过滤工具注册表(A 只能用 [查天气, 开门])
4. 把用户身份 + 过滤后的工具列表 + 用户消息 一起发给 LLM
5. LLM 返回 要调开门
6. 智能体云服务再次校验权限(防止 LLM 幻觉)
7. 返回结果给前端
关键 :LLM 不可信 ,可能幻觉瞎说或被 prompt 注入越权,云服务永远要做最终权限校验。
Q4:onMessage / injectedJavaScript / injectJavaScript() 是啥?
答:3 个相似但完全不同的东西
| 名称 | 类型 | 何时调用 | 用途 |
|---|---|---|---|
injectedJavaScript |
WebView 的 prop | 初始化时 跑一次 | 注入静态配置 BASE_URL 禁用缩放 |
injectJavaScript() |
WebView 实例的 方法 | 任何时候手动调用 | 动态注入 JS 业务数据回传 |
onMessage |
WebView 的 prop 回调 | H5 发消息时触发 | 接收 H5 发来的数据 |
对照代码:
javascript
// WebView.js 第 421 440 行
<WebView
onMessage={this.handleWebViewMessage} // Q4-A 监听 H5 发来的消息
injectedJavaScript={injectedJavaScript} // Q4-B 初始化时跑一次
injectedJavaScriptBeforeContentLoaded={...} // Q4-C DOM 加载前注入
/>
javascript
// WebView.js sendMessageToWebView 方法
sendMessageToWebView = (method, data, requestId) => {
const jsCode = `
window.dispatchEvent(new CustomEvent('nativeMessage', {
detail: { method: '${method}', requestId: '${requestId}', data: ${JSON.stringify(data)} }
}));
true;
`;
// 这里调用的是 webViewRef 的实例方法 不是 prop
this.webViewRef.current?.injectJavaScript(jsCode);
};
对照表:
| 业务数据回调用哪个 | 用 prop 还是方法 |
|---|---|
| 初始化静态配置 | injectedJavaScript prop |
| 业务数据回传 | webViewRef.current.injectJavaScript() 方法 |
三个 prop / 方法分别注入什么?
| 名称 | 注入时机 | 注入内容 |
|---|---|---|
injectedJavaScriptBeforeContentLoaded |
DOM 加载前 | 宿主 App 信息 安全区域 console 转发 |
injectedJavaScript |
DOM 加载后 | BASE_URL isInXingzhijia 标记 viewport |
onLoad 回调里 injectJavaScript() |
加载完成后 | 最新的 __SAFE_AREA_INSETS__ |
sendMessageToWebView() 里的 injectJavaScript() |
任何时候 | 业务数据回传(dispatchEvent nativeMessage) |
业务 API 数据回传走的是 sendMessageToWebView 里的 injectJavaScript(),不是 injectedJavaScript prop。
Q5:onShouldStartLoadWithRequest 是 WebView 自带的吗?
A :是 react-native-webview 提供的 prop,业务自定义 URL 拦截逻辑。
项目代码:
javascript
handleShouldStartLoadWithRequest = (request) => {
const currentUri = this.props.route?.params?.uri;
const requestUrl = request?.url;
// 规则 1: 空 URL 放行
if (!requestUrl) return true;
// 规则 2: 非顶层 frame 放行
if (request?.isTopFrame === false) return true;
// 规则 3: 不是 xingzhijia-agent 场景放行
if (!currentUri.includes('xingzhijia-agent')) return true;
// 规则 4: javascript: 或 about:blank 放行
if (requestUrl === 'about:blank' || requestUrl.startsWith('javascript:')) return true;
// 规则 5: 相对路径放行
if (!requestUrl.startsWith('//') && !/^[a-z][a-z0-9+.-]*:/i.test(requestUrl)) return true;
// 规则 6: 相同 URL 放行
if (currentUri.split('#')[0] === requestUrl.split('#')[0]) return true;
// 规则 7: 外部链接 用 Linking.openURL 在外部浏览器打开
Linking.openURL(requestUrl).catch(console.warn);
return false; // 阻止 WebView 内导航
};
用途 :安全防护 ,防止用户在 H5 里点恶意链接导致 WebView 跳到钓鱼网站。拦截后用外部浏览器打开,用户能看到地址栏。
⚠️ 项目里这个 prop 目前被注释了 (前面有 //),目前没有启用。
Q6:流程图对不对?injectedJavaScript 是不是传业务 API 数据?
A :你的发现完全正确!
injectedJavaScript 这个 prop ≠ 业务 API 数据回调。
业务 API 数据回调用的是:
sendMessageToWebView(method, data, requestId)(自定义函数)- 内部调用
webViewRef.current.injectJavaScript(jsCode)(实例方法) - 注入的 JS 里
window.dispatchEvent('nativeMessage', {detail}) - H5 监听
nativeMessage事件拿到数据
injectedJavaScript prop 注入的是 :BASE_URL isInXingzhijia 标记 viewport 禁用缩放(静态配置)。
修正文档时的核心澄清:
| 步骤 | 用什么 | 文档之前描述 | 修正后 |
|---|---|---|---|
| step8 | 原生→H5 注入 JS | injectJavaScript(歧义) |
webViewRef.current.injectJavaScript() 实例方法 |
| 初始化 | 静态配置 | injectedJavaScript prop |
明确为 prop 加实例方法 |
三、method 命名规则 + 完整开门案例
你提的核心问题
H5 给 RN 通信定义的方法是咋样的?RN 返回消息给 H5 又是怎样的方法命名?
getToken/getUserInfo是步骤 step4 的方法,还是步骤 step7 的 sendMessage 方法?
核心答案:method 名 = handler 名 = sendMessage 参数,三者必须一致
getToken 既是:
- 步骤 step4 的 handler 名(
handlers['getToken']) - 步骤 step7 的 sendMessage 第一参数(
sendMessage('getToken', ...)) - 步骤 step9 的 H5 响应 method 字段(
{method: 'getToken', ...})
三者必须同名,因为:
- 步骤 step4:
handlers['getToken']表示「H5 想调 getToken 这个能力」 - 步骤 step7:
sendMessage('getToken', ...)表示「回传的数据 method 是 getToken」 - 步骤 step9:H5 用 method 名字区分是哪个请求的响应
method 命名规范
| 类型 | 命名规则 | 例子 |
|---|---|---|
| 拉数据 查 | getXxx |
getToken / getUserInfo / getAgentContext / getServicePhone |
| 查列表 | queryXxx / getUserXxx |
queryBuildings / getUserAllDoor |
| 执行动作 | verbXxx |
doorOpen / verifyDoorHealth / callPhone |
| 跳转 | goXxx |
goBack |
H5 发起:
javascript
{
method: '方法名', // 驼峰命名
params: {...}, // 参数对象
requestId: '唯一ID' // 关联响应
}
RN 响应:
javascript
ctx.sendMessage('方法名', result, requestId);
// ↑ 同一个方法名
四、完整案例:「帮我开一号楼大门」(3 轮 H5↔RN 通信)
这是 AI 智能管家最经典的场景,包含 3 个 handler 调用 + 4 次 H5↔RN 通信。
全流程时序图

关键代码对照
step1 H5 发请求(H5 端 SDK 调用)
javascript
// 第一轮 拿楼宇
window.ReactNativeWebView.postMessage(JSON.stringify({
method: 'getBuildingInfo',
params: {},
requestId: 'req-001'
}));
// 第二轮 拿门禁列表
window.ReactNativeWebView.postMessage(JSON.stringify({
method: 'getUserAllDoor',
params: {buildingId: 1, employeeNo: 'xxx'},
requestId: 'req-002'
}));
// 第三轮 开门
window.ReactNativeWebView.postMessage(JSON.stringify({
method: 'doorOpen',
params: {
deviceSerial: 'abc',
buildingId: 1,
employeeNo: 'xxx'
},
requestId: 'req-003'
}));
step4 + step7 原生 handler 实现(RN 项目代码)
javascript
// src/utils/agentToolHandlers/doorHandlers.js
// handler 名 = method 名 = sendMessage 名(必须一致)
export async function getBuildingInfoHandler(params, requestId, context) {
const {sendMessage} = context;
// 从 redux 拿 不调 API
const buildingId = context.building?.curId;
sendMessage('getBuildingInfo', {buildingId}, requestId);
// ↑ 步骤 step7 调用 sendMessage
}
export async function getUserAllDoorHandler(params, requestId, context) {
const {sendMessage} = context;
const {buildingId, employeeNo} = params;
// 调业务 API
const result = await getUserAllDoorNew(buildingId, employeeNo);
sendMessage('getUserAllDoor', result, requestId);
// ↑ 步骤 step7
}
export async function doorOpenHandler(params, requestId, context) {
const {sendMessage} = context;
const {deviceSerial, buildingId, employeeNo} = params;
// 调开门 API
const result = await doorOpen({deviceSerial, buildingId, employeeNo});
sendMessage('doorOpen', result, requestId);
// ↑ 步骤 step7
}
step9 H5 接收响应(H5 端 SDK 内部)
javascript
window.addEventListener('nativeMessage', (e) => {
const {method, requestId, data} = e.detail;
// method 区分是哪个请求的响应
if (method === 'getBuildingInfo') {
// 拿到 buildingId 继续下一步
} else if (method === 'getUserAllDoor') {
// 拿到门禁列表
} else if (method === 'doorOpen') {
// 开门成功 显示结果
}
});
4 个观察点(最容易混的地方)
| 观察点 | 谁 | 啥时候调 |
|---|---|---|
| method 名字一致性 | H5 method = handler 名 = sendMessage 参数 | 全程保持一致 |
| requestId 唯一性 | 每次新请求生成 | H5 发起时 |
| ctx.sendMessage 是步骤 step7 | 原生 handler 完成后调 | handler 内部 |
| sendMessageToWebView 是步骤 step8 | ctx.sendMessage 内部调 | WebView.js 内 |
核心口诀 :「method 名字串起 H5 RN H5 三步,requestId 串起一次会话,handler 名 = sendMessage 的 method 参数」
五、11 个工具速查表
| 工具名 | 作用 | 内部调什么 |
|---|---|---|
getCloudTalkUserInfo |
拿云对讲用户信息 | api.getCloudTalkUserInfo() |
queryBuildings |
拿楼栋列表 | api.queryBuildingsNew() |
getUserAllDoor |
拿门禁列表 | api.getUserAllDoorNew() |
verifyDoorHealth |
验证开门权限 | api.verifyDoorHealth() |
doorOpen |
执行开门 | api.doorOpen() |
getBuildingInfo |
拿当前楼宇 | 直接从 redux 取 |
getBindingStatus |
拿房屋绑定状态 | 直接从 redux 取 |
getToken |
拿登录 token | storage.getToken() |
getUserInfo |
拿用户信息 | 直接从 redux 取 |
getAgentContext |
拿 agent 上下文 | 从 redux 拼 |
getServicePhone |
拿服务中心电话 | 直接从 redux 取 |
callPhone |
拨打电话 | Linking.openURL tel ... |
六、5 个观察口诀
口诀 1:JSBridge 三要素
onMessage 接收 + injectJavaScript 发送 + requestId 关联
口诀 2:method 三名一致
ini
handler 名 = H5 method = sendMessage 第一参数
口诀 3:业务数据回传路径
sendMessageToWebView → webViewRef.injectJavaScript → dispatchEvent nativeMessage
口诀 4:3 个相似但不同
scss
injectedJavaScript 是 prop 初始化用
injectJavaScript() 是方法 动态调用
sendMessageToWebView 是自定义函数 封装了方法调用
口诀 5:method 命名规范
erlang
查数据用 getXxx 查列表用 queryXxx 执行动作用 verbXxx 跳转用 goXxx
七、面试常见 5 问速答
Q1:什么是 JSBridge?
A :JSBridge 是 JavaScript 与原生代码之间的通信桥梁。在 RN WebView 里:
- H5 → 原生:
window.ReactNativeWebView.postMessage→ WebViewonMessage - 原生 → H5:
webViewRef.injectJavaScript→ 派发CustomEvent nativeMessage
Q2:onMessage / injectedJavaScript / injectJavaScript() 三者关系?
| 名称 | 性质 | 用途 |
|---|---|---|
onMessage |
WebView prop | 接收 H5 发来的消息 |
injectedJavaScript |
WebView prop | 初始化时注入静态配置 |
injectJavaScript() |
WebView 实例方法 | 任何时候动态注入 JS 业务数据回传 |
Q3:getToken 是步骤几?
A :同时是 step4 和 step7:
- step4:
handlers['getToken'](handler 名) - step7:
ctx.sendMessage('getToken', ...)(sendMessage 第一参数) - step9:H5 收到的响应 method 字段是
'getToken'
三者必须同名,这就是 JSBridge 的 method 约定。
Q4:method 命名有什么规范?
A:4 类命名:
- 查数据:
getXxx(getToken getUserInfo getAgentContext) - 查列表:
queryXxx或getUserXxx(queryBuildings getUserAllDoor) - 执行动作:
verbXxx(doorOpen callPhone) - 跳转:
goXxx(goBack)
Q5:为什么需要 requestId?
A :关联异步请求和响应 。H5 可能同时发多个 postMessage,原生处理完不一定按顺序返回,用 requestId 让 H5 知道这个响应是对应哪个请求。
面试前 5 分钟 :看第一节(10 步机制图)+ 第四节(method 命名规则)+ 第七节(5 问速答),足够应付 80% 追问。 被追问细节:看第二节(6 个深度问题)+ 第四节(完整开门案例)。