我正在尝试在vscode webview panel中使用React。我认为自己是React的重要组成部分,但我习惯于通过http与后端进行通信。
在此项目中,vscode扩展实质上是服务器,并且所有通信都必须通过在两者之间发送事件来完成。
//vscode extension (server)
//send message to react app
panel.webview.postMessage({ command: 'refactor' });
//receive messages from react app
panel.webview.onDidReceiveMessage(message => console.log(message))
//webview (react app)
//send message to extension
const vscode = acquireVsCodeApi();
vscode.postMessage({command: 'hello', text: 'world'});
//receive messages from extension
window.addEventListener('message', event => console.log(event))
我的大脑正在努力确定建立双向通讯的最佳方法。
例如,从Web视图发出请求以获取资源数据通常看起来像这样:
public refresh = () => {
this.setState({loading: true}, async () => {
try{
const item = await (await fetch('api/items/3')).json();
this.setState({item});
}catch(e){
this.setState({loading: false, err: e});
}
})
}
但是使用Event API时,这种方法显然行不通...
public refresh = () => {
// could register a listener before sending and deactivate it after, but that seems wrong!
vscode.postMessage({command: 'getItems', id: '3'});
}
我看到了一些有关在componentDidMount
中注册侦听器的讨论,但是由于该事件是通用的message
,因此与服务器通信的几乎每个组件都将被侦听,然后不得不过滤掉它在乎什么。
这时的一个想法是创建一个抽象层,该层允许发送消息并等待响应:
//track requests and wait for a response
const PENDING_REQUESTS = {};
function vscodeFetch(payload: any){
return new Promise((resolve, reject) => {
let reqId = crypto.getRandomValues(new Uint32Array(4)).join('-');
vscode.postMessage({reqId, payload);
PENDING_REQUESTS[reqId] = {resolve, reject};
});
}
//handle update resolve/reject promises on response
window.addEventListener('message', event => {
const message = event.data;
if(message.reqId){
let promise = PENDING_REQUESTS[message.reqId];
delete PENDING_REQUESTS[message.reqId];
if(message.success){
p.resolve(message.resp);
}else{
p.reject(message.err);
}
}
});
let items = await vscodeFetch({command: 'getItems', data: {id: 3}});
但这有点像我要发明自己的协议(听起来很简单,直到您开始考虑超时等异常行为为止!)