jsbox的webview和网页之间是如何交互的,用我们的这例子进行说明
在这个例子中,JSBox 的 WebView(原生应用容器)与网页(HTML/JS)之间的交互,本质上是 单向的“指令注入”。
可以将 JSBox 想象成一个遥控器,而网页是电视机。
以下是具体的交互流程说明:
- 准备工作:在网页端“开门” (
ui.js
)
默认情况下,现代前端开发(使用 ES6 模块)是封闭的。就像电视机的内部电路是封装好的一样,外部无法直接触碰。
为了让 JSBox 能控制网页,我们需要在网页的全局作用域(
window 对象)上暴露接口。这就像是在电视机上安装了一个红外线接收口。
代码位置:
assets/js/ui.js (末尾)
javascript
// 我们做的事情:
Object.assign(window, {
showAIInterpretation, // 暴露 AI 解读函数
toggleScrollMode, // 暴露 卷轴模式函数
// ... 其他函数
});
- 原理:window 是浏览器的全局对象。只要挂载在 window 上的函数,任何在当前页面运行的脚本都可以直接调用(例如 window.showAIInterpretation())。
- 发送指令:JSBox 端注入代码 (诗词.js)
当您在 JSBox 的导航栏点击按钮时,JSBox 会通过
eval 方法,将一段字符串形式的 JavaScript 代码“注射”到 WebView 中去执行。
代码位置:
jsbox/诗词.js
javascript
// 1. 获取 WebView 组件
const webView = $("web");
// 2. 准备要执行的脚本字符串
// 这里的字符串就是在网页控制台里敲的代码
let script = "window.showAIInterpretation && window.showAIInterpretation();";
// 3. 注入并执行
webView.eval({
script: script + " null;"
});
- $("web"):获取原生 UI 中的 WebView 视图对象。
- webView.eval(...):这是核心方法。它告诉 WebView:“请在你当前的页面上下文中,运行这段 JavaScript 代码”。
- 特殊处理:解决返回值问题
在我们的调试过程中,您遇到了
执行JavaScript返回结果的类型不受支持 的错误。
- 原因: showAIInterpretation 是一个 async (异步) 函数,它执行后会返回一个 Promise 对象。JSBox 的 eval 尝试将这个复杂的 Promise 对象传回给原生层(JSBox),但它无法序列化这个对象,所以报错。
- 解决:我们在脚本末尾加了 null;。javascript script: "window.showAIInterpretation(); null;" 这样,整段脚本的返回值就变成了 null(一个简单类型),JSBox 就能愉快地处理了,而网页端的函数依然在后台默默执行。
总结交互图解
mermaid
graph TD
A[JSBox 原生层] -->|用户点击按钮| B(JSBox 脚本 handler)
B -->|构建脚本字符串| C{webView.eval}
C -->|注入 JS 代码| D[WebView 网页层]
D -->|查找 window 对象| E[window.showAIInterpretation]
E -->|执行网页逻辑| F[更新网页 DOM / 显示 AI 弹窗]
核心结论: 交互的关键在于 网页端通过
window 暴露函数,JSBox 端通过 eval 调用这些函数。这就是为什么我们之前必须修改
ui.js
才能让 JSBox 的按钮生效的原因。
script: "window.showAIInterpretation(); null;"这里不太明白,为何加null就可以
这是一个非常好的技术细节问题。
在 JavaScript 中,一段脚本的返回值,通常是这段脚本中“最后一条语句”的执行结果。
让我们看看不加
null 时发生了什么:
-
脚本内容:window.showAIInterpretation();
-
执行过程:浏览器执行这个函数。
-
函数特性: showAIInterpretation 是一个 async 函数。在 JavaScript 中,async 函数调用后,必定会立即返回一个 Promise 对象(代表“将来会完成的任务”)。
-
脚本返回值:所以,整段脚本的返回值就是一个 Promise 对象。
-
JSBox 的困境:JSBox 的 webView.eval 机制会尝试获取脚本执行后的返回值,并将其从 Web 环境(WebView)传递回原生环境(JSBox)。
- JSBox 支持传递简单类型:数字、字符串、布尔值、null、简单的 JSON 对象。
- 不支持传递复杂对象:Promise、DOM 节点、函数 等。
- 当它试图“打包”(序列化)这个 Promise 对象传回去时,发现做不到,于是报错:“类型不受支持”。
现在看看加了
null 之后:
-
脚本内容:window.showAIInterpretation(); null;
-
执行过程:
- 先执行第一句:window.showAIInterpretation()。函数启动了,开始在网页里跑(虽然它返回了一个 Promise,但我们没有用变量去接它,它就飘在空中了)。
- 接着执行第二句:null;。这是一个表达式,它的值就是 null。
-
脚本返回值:整段脚本的返回值变成了最后一句的结果,也就是 null。
-
JSBox 的反应:JSBox 看到返回值是 null,这是一个它认识的简单类型,于是它很高兴地将其传回,没有任何错误发生。
打个比方:
- 不加 null:您让工人(WebView)去干活(执行函数),工人干完活后,试图把整个“施工现场”(Promise 对象)搬回来给您看。您(JSBox)说:“这太大了,我搬不动,报错!”
- 加了 null:您让工人去干活,然后说:“干完活你给我一张白纸(null)就行”。工人干完活,递给您一张白纸。您接过来一看,没问题,任务完成。
所以,加
null 的作用就是强制覆盖脚本的最终返回值,用一个安全的、无意义的值来替代原本那个无法传递的复杂对象。