14-AI智能管家JSBridge深度问答
英文标题:14-AI智能管家JSBridge深度问答
AI智能管家通过RN+WebView加载H5聊天页,用户发起指令后,H5调用智能体云服务,由LLM解析意图并决定调用工具(Function Calling)。JSBridge实现H5与原生通信,
99.3 KB
237.7 KB
60.2 KB
中文
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 句话 :
1. RN 端把用户说的话发给智能体云服务
2. 等结果
3. 把结果显示出来
中间的「云服务怎么调 LLM、怎么过滤权限 」完全在云服务内部,前端零感知 。
| 层级 | RN 项目能看到 | 看不到 |
|---|---|---|
| 智能体云服务 | 能看到(URL <YOUR_AGENT_DOMAIN>) | — |
| LLM | 完全看不到 | 模型名称 调用次数 prompt |
| 工具注册表 | 完全看不到 | 哪些工具注册了 |
| 权限过滤 | 完全看不到 | 用户能用哪些工具 |
Q3:智能体云服务怎么处理工具权限?
A :大致流程是用户身份 → 权限过滤 → 工具列表 + 用户消息 → LLM → 二次校验 → 返回 。前端不可见。
推测的流程 :
用户发请求「帮我开一号楼大门」
智能体云服务 收到请求
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 发来的数据 |
对照代码 :
<WebView
onMessage={this.handleWebViewMessage}
injectedJavaScript={injectedJavaScript}
injectedJavaScriptBeforeContentLoaded={...}
/>
sendMessageToWebView = (method, data, requestId) => {
const jsCode = `
window.dispatchEvent(new CustomEvent('nativeMessage', {
detail: { method: '${method}', requestId: '${requestId}', data: ${JSON.stringify(data)} }
}));
true;
`;
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 拦截逻辑。
项目代码 :
handleShouldStartLoadWithRequest = (request) => {
const currentUri = this.props.route?.params?.uri;
const requestUrl = request?.url;
if (!requestUrl) return true;
if (request?.isTopFrame === false) return true;
if (!currentUri.includes('xingzhijia-agent')) return true;
if (requestUrl === 'about:blank' || requestUrl.startsWith('javascript:')) return true;
if (!requestUrl.startsWith('//') && !/^[a-z][a-z0-9+.-]*:/i.test(requestUrl)) return true;
if (currentUri.split('#')[0] === requestUrl.split('#')[0]) return true;
Linking.openURL(requestUrl).catch(console.warn);
return false;
};
用途 :安全防护 ,防止用户在 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 发起 :
{
method: '方法名',
params: {...},
requestId: '唯一ID'
}
RN 响应 :
ctx.sendMessage('方法名', result, requestId);
四、完整案例:「帮我开一号楼大门」(3 轮 H5↔RN 通信)
这是 AI 智能管家最经典的场景,包含 3 个 handler 调用 + 4 次 H5↔RN 通信 。
全流程时序图
关键代码对照
step1 H5 发请求(H5 端 SDK 调用)
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 项目代码)
export async function getBuildingInfoHandler(params, requestId, context) {
const {sendMessage} = context;
const buildingId = context.building?.curId;
sendMessage('getBuildingInfo', {buildingId}, requestId);
}
export async function getUserAllDoorHandler(params, requestId, context) {
const {sendMessage} = context;
const {buildingId, employeeNo} = params;
const result = await getUserAllDoorNew(buildingId, employeeNo);
sendMessage('getUserAllDoor', result, requestId);
}
export async function doorOpenHandler(params, requestId, context) {
const {sendMessage} = context;
const {deviceSerial, buildingId, employeeNo} = params;
const result = await doorOpen({deviceSerial, buildingId, employeeNo});
sendMessage('doorOpen', result, requestId);
}
step9 H5 接收响应(H5 端 SDK 内部)
window.addEventListener('nativeMessage', (e) => {
const {method, requestId, data} = e.detail;
if (method === 'getBuildingInfo') {
} 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 三名一致
handler 名 = H5 method = sendMessage 第一参数
口诀 3:业务数据回传路径
sendMessageToWebView → webViewRef.injectJavaScript → dispatchEvent nativeMessage
口诀 4:3 个相似但不同
injectedJavaScript 是 prop 初始化用
injectJavaScript() 是方法 动态调用
sendMessageToWebView 是自定义函数 封装了方法调用
口诀 5:method 命名规范
查数据用 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 个深度问题)+ 第四节(完整开门案例)。
English / 原文
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 句话 :
1. RN 端把用户说的话发给智能体云服务
2. 等结果
3. 把结果显示出来
中间的「云服务怎么调 LLM、怎么过滤权限 」完全在云服务内部,前端零感知 。
| 层级 | RN 项目能看到 | 看不到 |
|---|---|---|
| 智能体云服务 | 能看到(URL <YOUR_AGENT_DOMAIN>) | — |
| LLM | 完全看不到 | 模型名称 调用次数 prompt |
| 工具注册表 | 完全看不到 | 哪些工具注册了 |
| 权限过滤 | 完全看不到 | 用户能用哪些工具 |
Q3:智能体云服务怎么处理工具权限?
A :大致流程是用户身份 → 权限过滤 → 工具列表 + 用户消息 → LLM → 二次校验 → 返回 。前端不可见。
推测的流程 :
用户发请求「帮我开一号楼大门」
智能体云服务 收到请求
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 发来的数据 |
对照代码 :
<WebView
onMessage={this.handleWebViewMessage}
injectedJavaScript={injectedJavaScript}
injectedJavaScriptBeforeContentLoaded={...}
/>
sendMessageToWebView = (method, data, requestId) => {
const jsCode = `
window.dispatchEvent(new CustomEvent('nativeMessage', {
detail: { method: '${method}', requestId: '${requestId}', data: ${JSON.stringify(data)} }
}));
true;
`;
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 拦截逻辑。
项目代码 :
handleShouldStartLoadWithRequest = (request) => {
const currentUri = this.props.route?.params?.uri;
const requestUrl = request?.url;
if (!requestUrl) return true;
if (request?.isTopFrame === false) return true;
if (!currentUri.includes('xingzhijia-agent')) return true;
if (requestUrl === 'about:blank' || requestUrl.startsWith('javascript:')) return true;
if (!requestUrl.startsWith('//') && !/^[a-z][a-z0-9+.-]*:/i.test(requestUrl)) return true;
if (currentUri.split('#')[0] === requestUrl.split('#')[0]) return true;
Linking.openURL(requestUrl).catch(console.warn);
return false;
};
用途 :安全防护 ,防止用户在 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 发起 :
{
method: '方法名',
params: {...},
requestId: '唯一ID'
}
RN 响应 :
ctx.sendMessage('方法名', result, requestId);
四、完整案例:「帮我开一号楼大门」(3 轮 H5↔RN 通信)
这是 AI 智能管家最经典的场景,包含 3 个 handler 调用 + 4 次 H5↔RN 通信 。
全流程时序图
关键代码对照
step1 H5 发请求(H5 端 SDK 调用)
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 项目代码)
export async function getBuildingInfoHandler(params, requestId, context) {
const {sendMessage} = context;
const buildingId = context.building?.curId;
sendMessage('getBuildingInfo', {buildingId}, requestId);
}
export async function getUserAllDoorHandler(params, requestId, context) {
const {sendMessage} = context;
const {buildingId, employeeNo} = params;
const result = await getUserAllDoorNew(buildingId, employeeNo);
sendMessage('getUserAllDoor', result, requestId);
}
export async function doorOpenHandler(params, requestId, context) {
const {sendMessage} = context;
const {deviceSerial, buildingId, employeeNo} = params;
const result = await doorOpen({deviceSerial, buildingId, employeeNo});
sendMessage('doorOpen', result, requestId);
}
step9 H5 接收响应(H5 端 SDK 内部)
window.addEventListener('nativeMessage', (e) => {
const {method, requestId, data} = e.detail;
if (method === 'getBuildingInfo') {
} 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 三名一致
handler 名 = H5 method = sendMessage 第一参数
口诀 3:业务数据回传路径
sendMessageToWebView → webViewRef.injectJavaScript → dispatchEvent nativeMessage
口诀 4:3 个相似但不同
injectedJavaScript 是 prop 初始化用
injectJavaScript() 是方法 动态调用
sendMessageToWebView 是自定义函数 封装了方法调用
口诀 5:method 命名规范
查数据用 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 个深度问题)+ 第四节(完整开门案例)。