JavaScript互操作
CefBrowser控件在twinBASIC和页面中运行的JavaScript之间提供两座互补的桥:
- 消息 —— 在两个方向推送值(字符串、数字……)并在另一侧监听。
- 脚本调用 —— 从BASIC调用命名的JavaScript函数,并(可选)等待其返回值。
INFO
WebView2还暴露了第三座桥——宿主对象,其中BASIC类发布到 chrome.webview.hostObjects.<Name> 供页面调用。CEF包尚未暴露等效功能——参见参考的WebView2对等部分。
本教程涵盖两座桥,每个BASIC端旁边显示匹配的JavaScript端。工作代码来自示例1b——Chromium Embedded Framework示例(窗体示例2)。
桥1——消息
消息是在两个方向传递的值。当你不希望提前定义方法签名时,用于通知和临时数据负载。
BASIC → 页面
PostWebMessage向页面发送值;页面通过 window.chrome.webview 上的 message 事件接收它:
WebView.PostWebMessage "Hello from twinBASIC!"window.chrome.webview.addEventListener('message', (e) => {
alert("Host sent: " + e.data);
});字符串以JavaScript字符串到达;数值、Boolean、Null和Empty为页面进行JSON编码。对象和数组目前不支持。
如果在渲染器IPC连接之前调用PostWebMessage,调用会排队并在连接建立后分发——无需显式等待Ready。
页面 → BASIC
页面调用 window.chrome.webview.postMessage(value);BASIC通过JsMessage事件接收它:
function sendHostAMessage() {
window.chrome.webview.postMessage("This is a message from JavaScript.");
}Private Sub WebView_JsMessage(ByVal Message As Variant) _
Handles WebView.JsMessage
Debug.Print "Page sent: "; Message
End Sub两个半部形成请求/响应交换——页面发送查询字符串,BASIC处理它并返回结果:
Private Sub WebView_JsMessage(ByVal Message As Variant) _
Handles WebView.JsMessage
If Left$(Message, 6) = "QUERY:" Then
WebView.PostWebMessage "ANSWER:" & LookupAnswer(Mid$(Message, 7))
End If
End Sub桥2——脚本调用
当页面暴露命名的JS函数时,BASIC可以直接调用它们。有三种变体:
| 方法 | 返回值 | 使用场景 |
|---|---|---|
| JsRun | Variant,同步 | 你需要内联结果且JS是纯的(无回调)。 |
| JsRunAsync | 无;结果通过 JsAsyncResult | JS可能需要一段时间,你不想阻塞UI。 |
| ExecuteScript | 无(即发即弃) | 你只想触发某些操作——不需要返回值。 |
JsRun(同步)
给定一个页面端函数:
function multiplyTheseNumbers(a, b) {
return a * b;
}BASIC可以调用它并在同一行读取结果:
Dim product As Long = WebView.JsRun("multiplyTheseNumbers", 5, 6)
Debug.Print product ' 30调用会阻塞BASIC线程,直到渲染器进程回复。
WARNING
如果JavaScript函数在调用期间回调到BASIC——例如通过 window.chrome.webview.postMessage(...)——结果是死锁。仅对纯函数使用JsRun;不符合此条件时改用JsRunAsync。完整讨论参见重入性教程。
JsRunAsync(异步)
Private Sub btnRun_Click() Handles btnRun.Click
WebView.JsRunAsync "multiplyTheseNumbers", 5, 6
End Sub
Private Sub WebView_JsAsyncResult( _
ByVal Result As Variant, Token As LongLong, ErrString As String) _
Handles WebView.JsAsyncResult
If LenB(ErrString) = 0 Then
Debug.Print "Async result: "; Result
Else
Debug.Print "Async error: "; ErrString
End If
End SubJsAsyncResult事件包含Token参数,因此单个处理程序可以解复用多个进行中的调用。成功时ErrString为空。
在渲染器IPC连接之前进行的调用会排队并在连接建立后分发。
ExecuteScript(即发即弃)
WebView.ExecuteScript "startTimer()"无返回值,无事件。推动页面执行某些操作的最简单方式。
重入性
关于从BASIC调用同步JavaScript何时安全——以及不安全时该怎么做——的讨论在其自己的教程中。简短概述:
- 纯JS(输入→输出,无涉及宿主的副作用):JsRun可行。
- 可能回发消息、等待宿主对象或以其他方式重入BASIC的JS:使用JsRunAsync。
完整图景参见重入性教程。
下一步
- 托管本地Web资源 —— 打包并提供与宿主通信的JavaScript。
- 从twinBASIC驱动Monaco —— 使用两座桥的完整案例研究。
- 重入性 —— 同步与异步调用背后的深入故事。
- CefBrowser参考 —— 每个属性、方法和事件。